CSV 파일을 .NET 데이터 테이블로 읽는 방법


170

CSV 파일을 System.Data.DataTable기반으로 데이터 테이블을 생성하여 CSV 파일을에로드하는 방법은 무엇입니까?

일반 ADO.net 기능이이를 허용합니까?


21
이것이 어떻게 '주제에서 벗어난 것'입니까? 구체적인 질문이며 100 명이 유용하다고 생각합니다.
Ryan

10
@Ryan : 진실로 나는 당신에게 말한다 ... StackOverflow 중재자는 독사의 무리입니다. 내 뒤에, StackOverflow 중재자!
Ronnie Overby

답변:


89

다음은 데이터 구조를 사용하여 CSV 데이터를 데이터 테이블에 복사하여 DataTable을 만드는 우수한 클래스입니다.

플랫 파일을위한 이식 가능하고 효율적인 일반 파서

구성하기 쉽고 사용하기 쉽습니다. 나는 당신이 살펴볼 것을 촉구합니다.


정말 훌륭합니다. 문서를 읽지 않아도 즉시 사용할 수 있습니다.
smirkingman

각 행의 구조가 다른 CSV 파일에서 작동합니까? 여러 유형의 로그 이벤트가있는 로그 파일이 있으며 여러 테이블로 분리해야합니다.
gonzobrains

2
@gonzobrains-아마 아닙니다; CSV 파일의 기본 가정은 첫 번째 행에 지정된 단일 열 헤더 세트를 기반으로하는 직사각형 데이터 구조입니다. 보다 일반적인 쉼표로 구분되고 구별되는 데이터로 보이며 파일에서 다양한 유형 (다른 DataTable의 DataRow를 포함 할 수 있음)의 오브젝트 인스턴스로 구문 분석하려면보다 정교한 "ETL"이 필요합니다.
KeithS

93

OleDb제공자를 사용하고 있습니다. 그러나 숫자 값이있는 행을 읽을 때 텍스트로 처리하려는 경우 문제가 있습니다. 그러나 schema.ini파일 을 만들어서이 문제를 해결할 수 있습니다 . 내가 사용한 방법은 다음과 같습니다.

// using System.Data;
// using System.Data.OleDb;
// using System.Globalization;
// using System.IO;

static DataTable GetDataTableFromCsv(string path, bool isFirstRowHeader)
{
    string header = isFirstRowHeader ? "Yes" : "No";

    string pathOnly = Path.GetDirectoryName(path);
    string fileName = Path.GetFileName(path);

    string sql = @"SELECT * FROM [" + fileName + "]";

    using(OleDbConnection connection = new OleDbConnection(
              @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathOnly + 
              ";Extended Properties=\"Text;HDR=" + header + "\""))
    using(OleDbCommand command = new OleDbCommand(sql, connection))
    using(OleDbDataAdapter adapter = new OleDbDataAdapter(command))
    {
        DataTable dataTable = new DataTable();
        dataTable.Locale = CultureInfo.CurrentCulture;
        adapter.Fill(dataTable);
        return dataTable;
    }
}

고마워 친구. 그것은 나를 도왔습니다. CSV 파일에는 쉼표가 구분 기호 일뿐 만 아니라 많은 열 값 내부에 있었으므로 줄을 나누는 정규식을 만드는 것이 다소 어려웠습니다. OleDbProvider가 스키마를 올바르게 추론했습니다.
Galilyou

구현은 의미가 있지만 혼합 데이터 유형을 포함하는 셀을 어떻게 처리합니까? 예를 들어 40C 등?
GKED

GKED, 읽고있는 데이터에 항상 예상되는 열 및 유형 세트가있는 경우 OleDb 제공자에게 열에 대한 정보를 알려주는 shema.ini 파일을 동일한 폴더에 배치 할 수 있습니다. 다음은 파일을 구성하는 방법에 대한 세부 정보를 제공하는 Microsoft 기사 링크입니다. msdn.microsoft.com/ko-kr/library/…
Jim Scott

4
이 답변은 효과가 있지만 나는 그것에 반대 할 것을 강력히 권합니다. 설치된 버전에 따라 동일한 컴퓨터에 다른 사무실 설치 (로컬 개발 환경에서 Excel 사용)와 충돌 할 수있는 외부 종속성이 있습니다. 보다 효율적이고 이식 가능한 방식으로이를 수행하는 NuGet 패키지 (ExcelDataReader, CsvHelper)가 있습니다.
A. 머레이

1
@ A.Murray-정확히 무엇을 의미합니까? System.Data.dll의 기본 제공 OleDb 공급자를 사용합니다. 추가 "드라이버"를 설치할 필요가 없습니다. 그리고 Windows 설치에 기본 Jet 드라이버가 설치되어 있지 않으면이 시대에 충격을 받았습니다. 이것은 1990 년대 CSV입니다 ....
Paul Easter

40

Sebastien Lorion의 Csv Reader 를 사용하기로 결정했습니다 .

Jay Riggs 제안도 훌륭한 솔루션이지만 Andrew Rissing의 Generic Parser가 제공 하는 모든 기능이 필요하지는 않았습니다 .

2010 년 10 월 25 일 업데이트

내 프로젝트에서 거의 1 년 반 동안 Sebastien Lorion의 Csv Reader 를 사용한 후 , 잘 구성된 것으로 생각되는 일부 CSV 파일을 구문 분석 할 때 예외가 발생 함을 발견했습니다.

그래서 Andrew Rissing의 Generic Parser로 전환했는데 훨씬 나아진 것 같습니다.

2014 년 9 월 22 일 업데이트

요즘에는 주로이 확장 방법을 사용하여 구분 된 텍스트를 읽습니다.

https://github.com/Core-Techs/Common/blob/master/CoreTechs.Common/Text/DelimitedTextExtensions.cs#L22

https://www.nuget.org/packages/CoreTechs.Common/

2015 년 2 월 20 일 업데이트

예:

var csv = @"Name, Age
Ronnie, 30
Mark, 40
Ace, 50";

TextReader reader = new StringReader(csv);
var table = new DataTable();
using(var it = reader.ReadCsvWithHeader().GetEnumerator())
{

    if (!it.MoveNext()) return;

    foreach (var k in it.Current.Keys)
        table.Columns.Add(k);

    do
    {
        var row = table.NewRow();
        foreach (var k in it.Current.Keys)
            row[k] = it.Current[k];
    
        table.Rows.Add(row);
    
    } while (it.MoveNext());
}

Sebastien Lorien의 CSV 리더가 훌륭하다는 데 동의합니다. 나는 무거운 CSV 처리에 사용하지만 소규모 작업에 Andrew의 Rissing을 사용하여 잘 제공되었습니다. 즐기세요!
Jay Riggs 2016 년

이 클래스를 사용하여 CSV를 DATATABLE에로드하는 방법은 무엇입니까?
Muflix

나는 이것을 시도했지만 it.Current.Keys 컬렉션은 열 이름 대신 "System.Linq.Enumerable + WhereSelectListIterator`2 [System.Int32, System.Char]"와 함께 반환됩니다. 왜 그런지에 대한 생각?
user3658298

다중 문자 분리 문자를 사용할 수 있습니까?

아니요,하지만 가능하게 할 생각이었습니다.
Ronnie Overby

32

이봐 100 % 효과

  public static DataTable ConvertCSVtoDataTable(string strFilePath)
  {
    DataTable dt = new DataTable();
    using (StreamReader sr = new StreamReader(strFilePath))
    {
        string[] headers = sr.ReadLine().Split(',');
        foreach (string header in headers)
        {
            dt.Columns.Add(header);
        }
        while (!sr.EndOfStream)
        {
            string[] rows = sr.ReadLine().Split(',');
            DataRow dr = dt.NewRow();
            for (int i = 0; i < headers.Length; i++)
            {
                dr[i] = rows[i];
            }
            dt.Rows.Add(dr);
        }

    }


    return dt;
   }

CSV 이미지 여기에 이미지 설명을 입력하십시오

가져온 데이터 테이블 여기에 이미지 설명을 입력하십시오


7
입력의 100 %가 가장 간단한 CSV 파일 일 경우에만 해당됩니다 (귀하의 경우에 해당).
Ronnie Overby

당신이 올바른지. codeproject.com/Articles/9258/A-Fast-CSV-Reader (Lorion dll) 를 사용해야합니다 .
Shivam Srivastava

1
2009에서 내 답변을 참조하십시오.
Ronnie Overby

1
@ShivamSrivastava 마지막 행에 오류가 발생했습니다. 여기에 다른 연락처 정보가 있습니까?
Sunil Acharya

이 버전을 정확하게 사용하지는 않았지만 문제를 해결 한 버전을 기반으로했습니다. 감사합니다. 잘 작동합니다.
nrod

13

우리는 64 비트 응용 프로그램으로 갈 때까지 항상 Jet.OLEDB 드라이버를 사용했습니다. Microsoft는 64 비트 Jet 드라이버를 출시하지 않았습니다. 다음은 File.ReadAllLines 및 String.Split을 사용하여 CSV 파일을 읽고 구문 분석하고 DataTable을 수동으로로드하는 간단한 솔루션입니다. 위에서 언급했듯이 열 값 중 하나에 쉼표가 포함 된 상황은 처리하지 않습니다. 우리는 주로 사용자 정의 구성 파일을 읽는 데 사용합니다. CSV 파일 사용에 대한 좋은 부분은 Excel에서 파일을 편집 할 수 있다는 것입니다.

string CSVFilePathName = @"C:\test.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols; i++)
    dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 1; i < Lines.GetLength(0); i++)
{
    Fields = Lines[i].Split(new char[] { ',' });
    Row = dt.NewRow();
    for (int f = 0; f < Cols; f++)
        Row[f] = Fields[f];
    dt.Rows.Add(Row);
}

8

이것은 내가 사용하는 코드이지만 앱은 net 버전 3.5로 실행해야합니다.

private void txtRead_Click(object sender, EventArgs e)
        {
           // var filename = @"d:\shiptest.txt";

            openFileDialog1.InitialDirectory = "d:\\";
            openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            DialogResult result = openFileDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                if (openFileDialog1.FileName != "")
                {
                    var reader = ReadAsLines(openFileDialog1.FileName);

                    var data = new DataTable();

                    //this assume the first record is filled with the column names
                    var headers = reader.First().Split(',');
                    foreach (var header in headers)
                    {
                        data.Columns.Add(header);
                    }

                    var records = reader.Skip(1);
                    foreach (var record in records)
                    {
                        data.Rows.Add(record.Split(','));
                    }

                    dgList.DataSource = data;
                }
            }
        }

        static IEnumerable<string> ReadAsLines(string filename)
        {
            using (StreamReader reader = new StreamReader(filename))
                while (!reader.EndOfStream)
                    yield return reader.ReadLine();
        }

이것은 내가 제시하고 싶었던 것입니다.
선장 켄 파치

8

C #에서 Microsoft.VisualBasic.FileIO.TextFieldParser dll을 사용하여 얻을 수 있습니다.

static void Main()
        {
            string csv_file_path=@"C:\Users\Administrator\Desktop\test.csv";

            DataTable csvData = GetDataTabletFromCSVFile(csv_file_path);

            Console.WriteLine("Rows count:" + csvData.Rows.Count);

            Console.ReadLine();
        }


private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
        {
            DataTable csvData = new DataTable();

            try
            {

            using(TextFieldParser csvReader = new TextFieldParser(csv_file_path))
                {
                    csvReader.SetDelimiters(new string[] { "," });
                    csvReader.HasFieldsEnclosedInQuotes = true;
                    string[] colFields = csvReader.ReadFields();
                    foreach (string column in colFields)
                    {
                        DataColumn datecolumn = new DataColumn(column);
                        datecolumn.AllowDBNull = true;
                        csvData.Columns.Add(datecolumn);
                    }

                    while (!csvReader.EndOfData)
                    {
                        string[] fieldData = csvReader.ReadFields();
                        //Making empty value as null
                        for (int i = 0; i < fieldData.Length; i++)
                        {
                            if (fieldData[i] == "")
                            {
                                fieldData[i] = null;
                            }
                        }
                        csvData.Rows.Add(fieldData);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            return csvData;
        }

CSV 처리로 휠을 다시 발명하지 마십시오. 매우 강력한 오픈 소스 대안이 너무 많습니다.
Mike Cole

1
감사합니다. Brad는 TextFieldParser와 관련하여 포함 된 따옴표를 처리하는 데 유용한 팁입니다.
mattpm

3
public class Csv
{
    public static DataTable DataSetGet(string filename, string separatorChar, out List<string> errors)
    {
        errors = new List<string>();
        var table = new DataTable("StringLocalization");
        using (var sr = new StreamReader(filename, Encoding.Default))
        {
            string line;
            var i = 0;
            while (sr.Peek() >= 0)
            {
                try
                {
                    line = sr.ReadLine();
                    if (string.IsNullOrEmpty(line)) continue;
                    var values = line.Split(new[] {separatorChar}, StringSplitOptions.None);
                    var row = table.NewRow();
                    for (var colNum = 0; colNum < values.Length; colNum++)
                    {
                        var value = values[colNum];
                        if (i == 0)
                        {
                            table.Columns.Add(value, typeof (String));
                        }
                        else
                        {
                            row[table.Columns[colNum]] = value;
                        }
                    }
                    if (i != 0) table.Rows.Add(row);
                }
                catch(Exception ex)
                {
                    errors.Add(ex.Message);
                }
                i++;
            }
        }
        return table;
    }
}

3

Linq와 regex를 사용하여 CSV 파일을 구문 분석하는이 코드 조각을 발견했습니다. 참조 기사는 1 년 반이 넘었지만 Linq (및 정규식)를 사용하여 CSV를 구문 분석하는 깔끔한 방법을 찾지 못했습니다. 여기에 적용되는 정규식은 쉼표로 구분 된 파일 (따옴표 안에 쉼표를 감지합니다!)이며 헤더에는 잘 걸리지 않지만 극복 할 수있는 방법이 있습니다. 절정을 맞이하십시오 :

Dim lines As String() = System.IO.File.ReadAllLines(strCustomerFile)
Dim pattern As String = ",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"
Dim r As System.Text.RegularExpressions.Regex = New System.Text.RegularExpressions.Regex(pattern)
Dim custs = From line In lines _
            Let data = r.Split(line) _
                Select New With {.custnmbr = data(0), _
                                 .custname = data(1)}
For Each cust In custs
    strCUSTNMBR = Replace(cust.custnmbr, Chr(34), "")
    strCUSTNAME = Replace(cust.custname, Chr(34), "")
Next

3

내가 찾은 가장 좋은 옵션은 다른 버전의 Office가 설치되어있을 수있는 문제를 해결하며 Chuck Bevitt 와 같은 32/64 비트 문제 는 FileHelpers 입니다.

NuGet을 사용하여 프로젝트 참조에 추가 할 수 있으며 단일 라이너 솔루션을 제공합니다.

CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);

CommonEngine이 무엇인지 알 수 있습니까? NuGet은 NuGet.Core와 동일합니다. 참조에서 NuGet.Core 만 찾았습니다
sindhu jampani

필요한 FileHelper입니다. NuGet이 있으면 NuGet으로 추가하십시오. 그렇지 않으면 프로젝트에서 어셈블리로 추가하십시오. CommonEngine은 FileHelper의 일부입니다.
Neo

3

외부 라이브러리를 사용하지 않고 OleDB를 사용하지 않으려는 경우 아래 예를 참조하십시오. 내가 찾은 모든 것은 OleDB, 외부 라이브러리 또는 단순히 쉼표를 기준으로 분할되었습니다! 제 경우에는 OleDB가 작동하지 않아서 다른 것을 원했습니다.

MarkJ의 기사에서 Microsoft.VisualBasic.FileIO.TextFieldParser 메서드를 참조하는 기사를 찾았 습니다 . 이 기사는 VB로 작성되었으며 데이터 테이블을 반환하지 않으므로 아래 예제를 참조하십시오.

public static DataTable LoadCSV(string path, bool hasHeader)
    {
        DataTable dt = new DataTable();

        using (var MyReader = new Microsoft.VisualBasic.FileIO.TextFieldParser(path))
        {
            MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
            MyReader.Delimiters = new String[] { "," };

            string[] currentRow;

            //'Loop through all of the fields in the file.  
            //'If any lines are corrupt, report an error and continue parsing.  
            bool firstRow = true;
            while (!MyReader.EndOfData)
            {
                try
                {
                    currentRow = MyReader.ReadFields();

                    //Add the header columns
                    if (hasHeader && firstRow)
                    {
                        foreach (string c in currentRow)
                        {
                            dt.Columns.Add(c, typeof(string));
                        }

                        firstRow = false;
                        continue;
                    }

                    //Create a new row
                    DataRow dr = dt.NewRow();
                    dt.Rows.Add(dr);

                    //Loop thru the current line and fill the data out
                    for(int c = 0; c < currentRow.Count(); c++)
                    {
                        dr[c] = currentRow[c];
                    }
                }
                catch (Microsoft.VisualBasic.FileIO.MalformedLineException ex)
                {
                    //Handle the exception here
                }
            }
        }

        return dt;
    }

3

매우 기본적인 대답 : 간단한 split 함수를 사용할 수있는 복잡한 csv가 없으면 가져 오기에 잘 작동합니다 (이 가져 오기는 문자열로 표시됩니다. 필요한 경우 나중에 데이터 유형 변환을 수행합니다)

 private DataTable csvToDataTable(string fileName, char splitCharacter)
    {                
        StreamReader sr = new StreamReader(fileName);
        string myStringRow = sr.ReadLine();
        var rows = myStringRow.Split(splitCharacter);
        DataTable CsvData = new DataTable();
        foreach (string column in rows)
        {
            //creates the columns of new datatable based on first row of csv
            CsvData.Columns.Add(column);
        }
        myStringRow = sr.ReadLine();
        while (myStringRow != null)
        {
            //runs until string reader returns null and adds rows to dt 
            rows = myStringRow.Split(splitCharacter);
            CsvData.Rows.Add(rows);
            myStringRow = sr.ReadLine();
        }
        sr.Close();
        sr.Dispose();
        return CsvData;
    }

string [] 구분 기호가있는 테이블을 가져오고 현재 읽고있는 줄이 csv 또는 텍스트 파일 <-IN의 다음 줄로 이동했을 수있는 문제를 처리하는 경우 내 방법 첫 번째 행의 총 줄 수 (열)

public static DataTable ImportCSV(string fullPath, string[] sepString)
    {
        DataTable dt = new DataTable();
        using (StreamReader sr = new StreamReader(fullPath))
        {
           //stream uses using statement because it implements iDisposable
            string firstLine = sr.ReadLine();
            var headers = firstLine.Split(sepString, StringSplitOptions.None);
            foreach (var header in headers)
            {
               //create column headers
                dt.Columns.Add(header);
            }
            int columnInterval = headers.Count();
            string newLine = sr.ReadLine();
            while (newLine != null)
            {
                //loop adds each row to the datatable
                var fields = newLine.Split(sepString, StringSplitOptions.None); // csv delimiter    
                var currentLength = fields.Count();
                if (currentLength < columnInterval)
                {
                    while (currentLength < columnInterval)
                    {
                       //if the count of items in the row is less than the column row go to next line until count matches column number total
                        newLine += sr.ReadLine();
                        currentLength = newLine.Split(sepString, StringSplitOptions.None).Count();
                    }
                    fields = newLine.Split(sepString, StringSplitOptions.None);
                }
                if (currentLength > columnInterval)
                {  
                    //ideally never executes - but if csv row has too many separators, line is skipped
                    newLine = sr.ReadLine();
                    continue;
                }
                dt.Rows.Add(fields);
                newLine = sr.ReadLine();
            }
            sr.Close();
        }

        return dt;
    }

좋은 행을 아직 string []로 선언하지 않았습니다.
동물 스타일

@AnimalStyle 당신이 맞아요-더 강력한 메소드와 선언 된 행으로 업데이트
Matt Farguson

3

Mr ChuckBevitt 에서 수정

작업 솔루션 :

string CSVFilePathName = APP_PATH + "Facilities.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols-1; i++)
        dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 0; i < Lines.GetLength(0)-1; i++)
{
        Fields = Lines[i].Split(new char[] { ',' });
        Row = dt.NewRow();
        for (int f = 0; f < Cols-1; f++)
                Row[f] = Fields[f];
        dt.Rows.Add(Row);
}

메모리 문제가 해결 되었습니까? 이것은 라인 단위 처리이며 메모리에 유지되지 않으므로 예외가 없어야합니까? 나는 이것이 처리되는 방식을 좋아하지만 File.ReadAllLines ()가 모두 메모리에 저장하지 않습니까? 거대한 메모리 버퍼를 피하기 위해 File.ReadLines ()를 사용해야한다고 생각합니까? 이것은 메모리 문제에 대해 알고 싶은 질문에 대한 좋은 대답입니다.
DtechNet

2

ADO.Net의 ODBC 텍스트 드라이버를 사용하는 솔루션은 다음과 같습니다.

Dim csvFileFolder As String = "C:\YourFileFolder"
Dim csvFileName As String = "YourFile.csv"

'Note that the folder is specified in the connection string,
'not the file. That's specified in the SELECT query, later.
Dim connString As String = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" _
    & csvFileFolder & ";Extended Properties=""Text;HDR=No;FMT=Delimited"""
Dim conn As New Odbc.OdbcConnection(connString)

'Open a data adapter, specifying the file name to load
Dim da As New Odbc.OdbcDataAdapter("SELECT * FROM [" & csvFileName & "]", conn)
'Then fill a data table, which can be bound to a grid
Dim dt As New DataTableda.Fill(dt)

grdCSVData.DataSource = dt

채워지면 ColumnName과 같은 데이터 테이블의 속성을 평가하여 ADO.Net 데이터 개체의 모든 기능을 활용할 수 있습니다.

VS2008에서는 Linq를 사용하여 동일한 효과를 얻을 수 있습니다.

참고 : 이것은 복제본 일 수 있습니다. SO 질문 .


2

이것에 내 스핀을 추가하는 것을 저항 할 수 없습니다. 이것은 내가 과거에 사용했던 것보다 훨씬 낫고 컴팩트합니다.

이 솔루션 :

  • 데이터베이스 드라이버 또는 타사 라이브러리에 의존하지 않습니다.
  • 중복 열 이름에서 실패하지 않습니다
  • 데이터에서 쉼표를 처리합니다
  • 쉼표뿐만 아니라 모든 구분 기호를 처리합니다 (기본값이지만).

내가 생각해 낸 것은 다음과 같습니다.

  Public Function ToDataTable(FileName As String, Optional Delimiter As String = ",") As DataTable
    ToDataTable = New DataTable
    Using TextFieldParser As New Microsoft.VisualBasic.FileIO.TextFieldParser(FileName) With
      {.HasFieldsEnclosedInQuotes = True, .TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited, .TrimWhiteSpace = True}
      With TextFieldParser
        .SetDelimiters({Delimiter})
        .ReadFields.ToList.Unique.ForEach(Sub(x) ToDataTable.Columns.Add(x))
        ToDataTable.Columns.Cast(Of DataColumn).ToList.ForEach(Sub(x) x.AllowDBNull = True)
        Do Until .EndOfData
          ToDataTable.Rows.Add(.ReadFields.Select(Function(x) Text.BlankToNothing(x)).ToArray)
        Loop
      End With
    End Using
  End Function

문자열 목록에 고유 숫자를 추가하는 방법Unique 에서 중복 열 이름을 처리 하는 확장 방법 ( ) 에 따라 다릅니다.

그리고 여기 BlankToNothing도우미 기능이 있습니다 :

  Public Function BlankToNothing(ByVal Value As String) As Object 
    If String.IsNullOrEmpty(Value) Then Return Nothing
    Return Value
  End Function

2

오픈 소스 라이브러리 인 Cinchoo ETL을 사용하면 몇 줄의 코드만으로 CSV 파일을 DataTable로 쉽게 변환 할 수 있습니다.

using (var p = new ChoCSVReader(** YOUR CSV FILE **)
     .WithFirstLineHeader()
    )
{
    var dt = p.AsDataTable();
}

자세한 내용은 codeproject 기사를 참조하십시오.

도움이 되길 바랍니다.


2
    private static DataTable LoadCsvData(string refPath)
    {
        var cfg = new Configuration() { Delimiter = ",", HasHeaderRecord = true };
        var result = new DataTable();
        using (var sr = new StreamReader(refPath, Encoding.UTF8, false, 16384 * 2))
        {
            using (var rdr = new CsvReader(sr, cfg))
            using (var dataRdr = new CsvDataReader(rdr))
            {
                result.Load(dataRdr);
            }
        }
        return result;
    }

사용 : https://joshclose.github.io/CsvHelper/


릴리스 13 에서는 네임 스페이스 충돌을 피하기 위해 이름Configuration 이 바뀌 었습니다 CsvConfiguration . 이 답변의 데모 작동 : dotnetfiddle.net/sdwc6i
dbc

2

ExcelDataReader라는 라이브러리를 사용합니다. NuGet에서 찾을 수 있습니다. ExcelDataReader와 ExcelDataReader.DataSet 확장을 모두 설치하십시오 (후자는 아래 참조 된 필수 AsDataSet 메소드를 제공함).

하나의 함수로 모든 것을 캡슐화했으며 코드에서 직접 복사 할 수 있습니다. CSV 파일의 경로를 지정하면 하나의 테이블로 데이터 세트를 얻을 수 있습니다.

public static DataSet GetDataSet(string filepath)
{
   var stream = File.OpenRead(filepath);

   try
   {
       var reader = ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration()
       {
           LeaveOpen = false
       });

       var result = reader.AsDataSet(new ExcelDataSetConfiguration()
       {
           // Gets or sets a value indicating whether to set the DataColumn.DataType 
           // property in a second pass.
           UseColumnDataType = true,

           // Gets or sets a callback to determine whether to include the current sheet
           // in the DataSet. Called once per sheet before ConfigureDataTable.
           FilterSheet = (tableReader, sheetIndex) => true,

           // Gets or sets a callback to obtain configuration options for a DataTable. 
           ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
           {
               // Gets or sets a value indicating the prefix of generated column names.
               EmptyColumnNamePrefix = "Column",

               // Gets or sets a value indicating whether to use a row from the 
               // data as column names.
               UseHeaderRow = true,

               // Gets or sets a callback to determine which row is the header row. 
               // Only called when UseHeaderRow = true.
               ReadHeaderRow = (rowReader) =>
               {
                   // F.ex skip the first row and use the 2nd row as column headers:
                   //rowReader.Read();
               },

               // Gets or sets a callback to determine whether to include the 
               // current row in the DataTable.
               FilterRow = (rowReader) =>
               {
                   return true;
               },

               // Gets or sets a callback to determine whether to include the specific
               // column in the DataTable. Called once per column after reading the 
               // headers.
               FilterColumn = (rowReader, columnIndex) =>
               {
                   return true;
               }
           }
       });

       return result;
   }
   catch (Exception ex)
   {
       return null;
   }
   finally
   {
       stream.Close();
       stream.Dispose();
   }
}

2020 년이며 여기에있는 오래된 답변과 비교하여 훌륭한 솔루션입니다. 멋지게 패키지되어 있으며 NuGet의 인기 있고 가벼운 라이브러리를 사용합니다. 유연성이 뛰어납니다. CSV가 메모리에 있으면 MemoryStream파일 경로 대신 CSV로 전달하면됩니다 . OP가 요청한 DataTable은 다음과 같이 DataSet에서 쉽게 추출됩니다.result.Tables[0]
Tawab Wakil

1

이 확장 방법을 공유하면 누군가에게 도움이되기를 바랍니다.

public static List<string> ToCSV(this DataSet ds, char separator = '|')
{
    List<string> lResult = new List<string>();

    foreach (DataTable dt in ds.Tables)
    {
        StringBuilder sb = new StringBuilder();
        IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
                                          Select(column => column.ColumnName);
        sb.AppendLine(string.Join(separator.ToString(), columnNames));

        foreach (DataRow row in dt.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field =>
              string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
            sb.AppendLine(string.Join(separator.ToString(), fields));
        }

        lResult.Add(sb.ToString());
    }
    return lResult;
}

public static DataSet CSVtoDataSet(this List<string> collectionCSV, char separator = '|')
{
    var ds = new DataSet();

    foreach (var csv in collectionCSV)
    {
        var dt = new DataTable();

        var readHeader = false;
        foreach (var line in csv.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
        {
            if (!readHeader)
            {
                foreach (var c in line.Split(separator))
                    dt.Columns.Add(c);
            }
            else
            {
                dt.Rows.Add(line.Split(separator));
            }
        }

        ds.Tables.Add(dt);
    }

    return ds;
}

0

이것을 사용하면 하나의 함수가 쉼표와 따옴표의 모든 문제를 해결합니다.

public static DataTable CsvToDataTable(string strFilePath)
    {

        if (File.Exists(strFilePath))
        {

            string[] Lines;
            string CSVFilePathName = strFilePath;

            Lines = File.ReadAllLines(CSVFilePathName);
            while (Lines[0].EndsWith(","))
            {
                Lines[0] = Lines[0].Remove(Lines[0].Length - 1);
            }
            string[] Fields;
            Fields = Lines[0].Split(new char[] { ',' });
            int Cols = Fields.GetLength(0);
            DataTable dt = new DataTable();
            //1st row must be column names; force lower case to ensure matching later on.
            for (int i = 0; i < Cols; i++)
                dt.Columns.Add(Fields[i], typeof(string));
            DataRow Row;
            int rowcount = 0;
            try
            {
                string[] ToBeContinued = new string[]{};
                bool lineToBeContinued = false;
                for (int i = 1; i < Lines.GetLength(0); i++)
                {
                    if (!Lines[i].Equals(""))
                    {
                        Fields = Lines[i].Split(new char[] { ',' });
                        string temp0 = string.Join("", Fields).Replace("\"\"", "");
                        int quaotCount0 = temp0.Count(c => c == '"');
                        if (Fields.GetLength(0) < Cols || lineToBeContinued || quaotCount0 % 2 != 0)
                        {
                            if (ToBeContinued.GetLength(0) > 0)
                            {
                                ToBeContinued[ToBeContinued.Length - 1] += "\n" + Fields[0];
                                Fields = Fields.Skip(1).ToArray();
                            }
                            string[] newArray = new string[ToBeContinued.Length + Fields.Length];
                            Array.Copy(ToBeContinued, newArray, ToBeContinued.Length);
                            Array.Copy(Fields, 0, newArray, ToBeContinued.Length, Fields.Length);
                            ToBeContinued = newArray;
                            string temp = string.Join("", ToBeContinued).Replace("\"\"", "");
                            int quaotCount = temp.Count(c => c == '"');
                            if (ToBeContinued.GetLength(0) >= Cols && quaotCount % 2 == 0 )
                            {
                                Fields = ToBeContinued;
                                ToBeContinued = new string[] { };
                                lineToBeContinued = false;
                            }
                            else
                            {
                                lineToBeContinued = true;
                                continue;
                            }
                        }

                        //modified by Teemo @2016 09 13
                        //handle ',' and '"'
                        //Deserialize CSV following Excel's rule:
                        // 1: If there is commas in a field, quote the field.
                        // 2: Two consecutive quotes indicate a user's quote.

                        List<int> singleLeftquota = new List<int>();
                        List<int> singleRightquota = new List<int>();

                        //combine fileds if number of commas match
                        if (Fields.GetLength(0) > Cols) 
                        {
                            bool lastSingleQuoteIsLeft = true;
                            for (int j = 0; j < Fields.GetLength(0); j++)
                            {
                                bool leftOddquota = false;
                                bool rightOddquota = false;
                                if (Fields[j].StartsWith("\"")) 
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    foreach (char c in Fields[j]) //start with how many "
                                    {
                                        if (c == '"')
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }
                                    if (numberOfConsecutiveQuotes % 2 == 1)//start with odd number of quotes indicate system quote
                                    {
                                        leftOddquota = true;
                                    }
                                }

                                if (Fields[j].EndsWith("\""))
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    for (int jj = Fields[j].Length - 1; jj >= 0; jj--)
                                    {
                                        if (Fields[j].Substring(jj,1) == "\"") // end with how many "
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }

                                    if (numberOfConsecutiveQuotes % 2 == 1)//end with odd number of quotes indicate system quote
                                    {
                                        rightOddquota = true;
                                    }
                                }
                                if (leftOddquota && !rightOddquota)
                                {
                                    singleLeftquota.Add(j);
                                    lastSingleQuoteIsLeft = true;
                                }
                                else if (!leftOddquota && rightOddquota)
                                {
                                    singleRightquota.Add(j);
                                    lastSingleQuoteIsLeft = false;
                                }
                                else if (Fields[j] == "\"") //only one quota in a field
                                {
                                    if (lastSingleQuoteIsLeft)
                                    {
                                        singleRightquota.Add(j);
                                    }
                                    else
                                    {
                                        singleLeftquota.Add(j);
                                    }
                                }
                            }
                            if (singleLeftquota.Count == singleRightquota.Count)
                            {
                                int insideCommas = 0;
                                for (int indexN = 0; indexN < singleLeftquota.Count; indexN++)
                                {
                                    insideCommas += singleRightquota[indexN] - singleLeftquota[indexN];
                                }
                                if (Fields.GetLength(0) - Cols >= insideCommas) //probabaly matched
                                {
                                    int validFildsCount = insideCommas + Cols; //(Fields.GetLength(0) - insideCommas) may be exceed the Cols
                                    String[] temp = new String[validFildsCount];
                                    int totalOffSet = 0;
                                    for (int iii = 0; iii < validFildsCount - totalOffSet; iii++)
                                    {
                                        bool combine = false;
                                        int storedIndex = 0;
                                        for (int iInLeft = 0; iInLeft < singleLeftquota.Count; iInLeft++)
                                        {
                                            if (iii + totalOffSet == singleLeftquota[iInLeft])
                                            {
                                                combine = true;
                                                storedIndex = iInLeft;
                                                break;
                                            }
                                        }
                                        if (combine)
                                        {
                                            int offset = singleRightquota[storedIndex] - singleLeftquota[storedIndex];
                                            for (int combineI = 0; combineI <= offset; combineI++)
                                            {
                                                temp[iii] += Fields[iii + totalOffSet + combineI] + ",";
                                            }
                                            temp[iii] = temp[iii].Remove(temp[iii].Length - 1, 1);
                                            totalOffSet += offset;
                                        }
                                        else
                                        {
                                            temp[iii] = Fields[iii + totalOffSet];
                                        }
                                    }
                                    Fields = temp;
                                }
                            }
                        }
                        Row = dt.NewRow();
                        for (int f = 0; f < Cols; f++)
                        {
                            Fields[f] = Fields[f].Replace("\"\"", "\""); //Two consecutive quotes indicate a user's quote
                            if (Fields[f].StartsWith("\""))
                            {
                                if (Fields[f].EndsWith("\""))
                                {
                                    Fields[f] = Fields[f].Remove(0, 1);
                                    if (Fields[f].Length > 0)
                                    {
                                        Fields[f] = Fields[f].Remove(Fields[f].Length - 1, 1);
                                    }
                                }
                            }
                            Row[f] = Fields[f];
                        }
                        dt.Rows.Add(Row);
                        rowcount++;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception( "row: " + (rowcount+2) + ", " + ex.Message);
            }
            //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", FilePath + FileName));
            //OleDbCommand command = new OleDbCommand("SELECT * FROM " + FileName, connection);
            //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
            //DataTable dt = new DataTable();
            //adapter.Fill(dt);
            //adapter.Dispose();
            return dt;
        }
        else
            return null;

        //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", strFilePath));
        //OleDbCommand command = new OleDbCommand("SELECT * FROM " + strFileName, connection);
        //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
        //DataTable dt = new DataTable();
        //adapter.Fill(dt);
        //return dt;
    }

0
 Public Function ReadCsvFileToDataTable(strFilePath As String) As DataTable
    Dim dtCsv As DataTable = New DataTable()
    Dim Fulltext As String
    Using sr As StreamReader = New StreamReader(strFilePath)
        While Not sr.EndOfStream
            Fulltext = sr.ReadToEnd().ToString()
            Dim rows As String() = Fulltext.Split(vbLf)
            For i As Integer = 0 To rows.Count() - 1 - 1
                Dim rowValues As String() = rows(i).Split(","c)
                If True Then
                    If i = 0 Then
                        For j As Integer = 0 To rowValues.Count() - 1
                            dtCsv.Columns.Add(rowValues(j))
                        Next
                    Else
                        Dim dr As DataRow = dtCsv.NewRow()
                        For k As Integer = 0 To rowValues.Count() - 1
                            dr(k) = rowValues(k).ToString()
                        Next
                        dtCsv.Rows.Add(dr)
                    End If
                End If
            Next
        End While
    End Using
    Return dtCsv
End Function

0

나는 최근 에 .NET 용 CSV 파서 를 작성하여 현재 Nuget 패키지로 가장 빨리 사용 가능 하다고 주장 합니다 : Sylvan.Data.Csv .

이 라이브러리를 사용하여로드하는 DataTable것은 매우 쉽습니다.

using var tr = File.OpenText("data.csv");
using var dr = CsvDataReader.Create(tr);
var dt = new DataTable();
dt.Load(dr);

파일이 헤더가있는 표준 쉼표로 구분 된 파일이라고 가정하면 충분합니다. 헤더없이 파일을 읽고 대체 구분 기호 등을 사용할 수있는 옵션도 있습니다.

열을 string값 이외의 것으로 취급 할 수 있도록 CSV 파일에 대한 사용자 정의 스키마를 제공 할 수도 있습니다 . 이렇게하면 DataTable열에 액세스 할 때 강제로 할 필요가 없으므로 작업하기가 더 쉬운 값으로 열을로드 할 수 있습니다.

var schema = new TypedCsvSchema();
schema.Add(0, typeof(int));
schema.Add(1, typeof(string));
schema.Add(2, typeof(double?));
schema.Add(3, typeof(DateTime));
schema.Add(4, typeof(DateTime?));

var options = new CsvDataReaderOptions { 
    Schema = schema 
};

using var tr = GetData();
using var dr = CsvDataReader.Create(tr, options);

TypedCsvSchemaICsvSchemaProvider열 유형을 정의하는 간단한 방법을 제공 하는 구현입니다 . 그러나 ICsvSchemaProvider고유성 또는 제한된 열 크기 등과 같은 더 많은 메타 데이터를 제공하려는 경우 사용자 정의를 제공 할 수도 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.