헤더를 사용하여 C #에서 CSV 파일 구문 분석


266

C #에서 CSV 파일을 구문 분석하는 기본 / 공식 / 권장 방법이 있습니까? 내 파서를 굴리고 싶지 않다.

또한 ODBC / OLE DB를 사용하여 텍스트 드라이버를 통해 CSV를 읽는 사람들의 사례를 보았으며 많은 사람들이 "단점"으로 인해 이것을 권장하지 않습니다. 이러한 단점은 무엇입니까?

이상적으로는 첫 번째 레코드를 헤더 / 필드 이름으로 사용하여 열 이름으로 CSV를 읽을 수있는 방법을 찾고 있습니다. 주어진 답변 중 일부는 정확하지만 기본적으로 파일을 클래스로 직렬화 해제합니다.

답변:


138

도서관이 당신을 위해 모든 세부 사항을 처리하게하십시오! :-)

FileHelpers를 확인하고 DRY를 유지하십시오-스스로 반복하지 마십시오-바퀴를 다시 발명 할 필요가 없습니다 ....

기본적으로 공개 클래스 (따라서 기본값, NULL 값 대체 등과 같은 잘 알려진 속성), 포인트를 사용하여 데이터의 형태 (CSV의 개별 줄에있는 필드)를 정의하면됩니다. 파일의 FileHelpers 엔진 및 bingo-해당 파일에서 모든 항목을 가져옵니다. 하나의 간단한 조작-뛰어난 성능!


1
sth가 정말로 사용자 정의가 필요할 때까지 (그리고 대부분 확장으로 구현할 수 있습니다) FileHelpers는 지금까지 가장 편리하고 편리하며 테스트되고 성능이 우수한 솔루션입니다
mikus

3
2015 년 6 월 1 일부터 FileHelpers를 다운로드 할 수있는 유일한 방법은 sourceforge.net에서 검색하는 것입니다. 사용 된 링크는 다음과 같습니다. sourceforge.net/projects/filehelpers/?source=directory
Sudhanshu Mishra

2
@dotnetguy 우리는 3.1 (현재 3.1-rc2)을 출시하는 길에 있습니다. 또한 우리는 사이트를 재 설계했습니다 : www.filehelpers.net 거기서 최신 버전을 다운로드 할 수 있습니다
Marcos Meli

1
@MarcosMeli 많은 감사합니다! 나는 이미 내 프로젝트 중 하나에서 FileHelpers를 사용했으며 팀에 대한 kudos를 사용하는 것은 바람이 불었습니다. 나는 그것에 관한 블로그를 곧 계획하고있다-btw-새로운 사이트를 좋아해라 – 잘했다!
Sudhanshu Mishra

FileHelpers는 인용 된 쉼표를 CSV로 올바르게 처리하지 않거나 실제로 필드 헤더를 맵핑합니다. 대신 필드가 사용자 유형에서 선언 된 순서와 동일한 순서를 갖기를 기대합니다. 개인적으로는 사용하지 않겠습니다.
Alastair Maw

358

CSV 파서는 이제 .NET Framework의 일부입니다.

Microsoft.VisualBasic.dll에 대한 참조 추가 (C #에서는 잘 작동하지만 이름은 신경 쓰지 않음)

using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    while (!parser.EndOfData)
    {
        //Process row
        string[] fields = parser.ReadFields();
        foreach (string field in fields)
        {
            //TODO: Process field
        }
    }
}

문서는 여기에 있습니다 -TextFieldParser 클래스

PS CSV 내보내기 가 필요한 경우 CsvExport를 사용해보십시오 (Discl : I 'm atributors).


2
내 경험상 TextFieldParser는 큰 (예 :> 250Mb) 파일에서 제대로 작동하지 않습니다. :(
MBoros

6
TextFieldParser는 IDisposable을 구현하므로 using 절에서 사용하는 것이 가장 좋습니다. 그렇지 않으면 좋은 대답입니다.
Chris Bush

3
생성자에서 당신과 같이, 기본적으로가 아닌 다른 인코딩을 사용할 수 있습니다 : 새 TextFieldParser를 ( "C : \ TEMP \ test.csv", System.Text.Encoding.UTF8)
neural5torm

1
(있는 경우) 참고 필드 당신의 CSV에 빈 줄이 포함되어, 그들이 건너 뛴 것이다 TextFieldParser.ReadLine(). 참조 TextFieldParser 워드 프로세서
mcNux

3
.NET Core에서 이것을 얻는 방법이 있습니까?
휴고 Zink

183

CsvHelper (내가 유지 관리하는 라이브러리)는 CSV 파일을 사용자 지정 개체로 읽습니다.

var csv = new CsvReader( File.OpenText( "file.csv" ) );
var myCustomObjects = csv.GetRecords<MyCustomObject>();

때로는 읽으려는 객체를 소유하지 않은 경우가 있습니다. 이 경우 클래스에 속성을 넣을 수 없으므로 매끄럽게 매핑 할 수 있습니다.

public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
    public MyCustomObjectMap()
    {
        Map( m => m.Property1 ).Name( "Column Name" );
        Map( m => m.Property2 ).Index( 4 );
        Map( m => m.Property3 ).Ignore();
        Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
    }
}

편집하다:

CsvReader는 이제 CultureInfo가 컨스트럭터 ( https://github.com/JoshClose/CsvHelper/issues/1441 ) 로 전달되어야 합니다.

예:

var csv = new CsvReader(File.OpenText("file.csv"), System.Globalization.CultureInfo.CurrentCulture);

18
@ kubal5003에 동의합니다. 나에게 팔린 것은 NuGet 패키지로 사용할 수 있다는 것입니다. 고마워 사람, 그것은 빠르며 필요한 모든 CSV 읽기를 수행합니다.
Gromer

7
정말 빠릅니다. 10 초 동안 130 만 개의 레코드를 읽고 역 직렬화했습니다.
marisks

2
구현하기 매우 쉬운 훌륭한 라이브러리. 이 답변이 작성된 이후 라이브러리가 약간 변경되어 더 이상 CsvHelper를 인스턴스화 할 수 없지만 (지금은 네임 스페이스 만) CsvReader 클래스를 사용해야하기 때문에 Josh에게 여기에 그의 대답을 업데이트하도록 제안하고 싶습니다.
Marko

1
CsvClassMap이 마지막 버전의 CsvHelper에 존재하지 않는 것 같습니다.
크 노크 테

1
knocte, 그것은 지금 ClassMap이라고합니다. 헤더 레코드를 요청하기 전에 읽기를 수행 해야하는 것과 같은 다른 변경 사항도 있습니다 (이는 처음 Read () 호출로 읽은 것으로 설정 됨). 다른 사람들이 이전에 언급했듯이 초고속이며 작업하기 쉽습니다.
norgie

31

비즈니스 응용 프로그램에서 codeproject.com, CSVReader 의 오픈 소스 프로젝트를 사용합니다 .

잘 작동하고 성능이 좋습니다. 내가 제공 한 링크에 대한 벤치마킹이 있습니다.

프로젝트 페이지에서 복사 한 간단한 예 :

using (CsvReader csv = new CsvReader(new StreamReader("data.csv"), true))
{
    int fieldCount = csv.FieldCount;
    string[] headers = csv.GetFieldHeaders();

    while (csv.ReadNextRecord())
    {
        for (int i = 0; i < fieldCount; i++)
            Console.Write(string.Format("{0} = {1};", headers[i], csv[i]));

        Console.WriteLine();
    }
}

보시다시피, 작업하기가 매우 쉽습니다.



12

csv 파일 만 읽어야하는 경우이 라이브러리를 권장합니다. 빠른 CSV 리더
csv 파일도 생성해야하는 경우이 파일을 사용하십시오. FileHelpers

둘 다 무료이며 오픈 소스입니다.


FileHelpers는 매력적인 요약을 가지고 있습니다. filehelpers.com FileHelpers는 무료이며 사용하기 쉬운 .NET 라이브러리로 고정 길이 또는 구분 된 레코드에서 파일, 문자열 또는 스트림으로 데이터를 가져 오거나 내보낼 수 있습니다.
AnneTheAgile

이 링크는 질문에 대답 할 수 있지만 링크 오버플로에서는 링크 만 답변하지 않는 것이 좋습니다. 링크의 중요한 부분을 가져 와서 답변에 넣으면이 답변을 개선 할 수 있습니다. 이렇게하면 링크가 변경 되어도 답변이 여전히 답변이됩니다 또는 제거 :)
WhatsThePoint

11

이 스레드로 돌아 오는 경우를 위해 내가 자주 사용하는 도우미 클래스가 있습니다 (공유하고 싶었습니다).

사용할 준비가 된 프로젝트로 간단하게 이식하기 위해 이것을 사용합니다.

public class CSVHelper : List<string[]>
{
  protected string csv = string.Empty;
  protected string separator = ",";

  public CSVHelper(string csv, string separator = "\",\"")
  {
    this.csv = csv;
    this.separator = separator;

    foreach (string line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !string.IsNullOrEmpty(s)))
    {
      string[] values = Regex.Split(line, separator);

      for (int i = 0; i < values.Length; i++)
      {
        //Trim values
        values[i] = values[i].Trim('\"');
      }

      this.Add(values);
    }
  }
}

그리고 그것을 다음과 같이 사용하십시오 :

public List<Person> GetPeople(string csvContent)
{
  List<Person> people = new List<Person>();
  CSVHelper csv = new CSVHelper(csvContent);
  foreach(string[] line in csv)
  {
    Person person = new Person();
    person.Name = line[0];
    person.TelephoneNo = line[1];
    people.Add(person);
  }
  return people;
}

[CSV 도우미 업데이트 : 마지막 줄 바꾸기 문자가 줄 바꾸기를 만들 때 발생하는 버그 수정]


17
csv 항목 중 하나에 쉼표 (,)가 포함되어 있으면이 코드가 작동하지 않습니다.
hakan

가벼운 것을 유지하기 위해 파이프 문자를 구분 기호로 사용했습니다. '|'
Base33

탁월한 솔루션. 두 번째 스 니펫에 대한 질문입니다. Person
Cocoa Dev

@CocoaDev Name과 TelephoneNo라는 두 가지 문자열 속성을 포함하는 클래스입니다. 그래도 예를 들어. 속성 중 하나가 정수인 경우 간단하게 변환해야합니다 (확인?).
Base33

10

이 솔루션은 공식 Microsoft.VisualBasic을 사용하고 있습니다. 어셈블리를 CSV를 구문 분석합니다.

장점 :

  • 분리 문자 이스케이프
  • 헤더를 무시
  • 트림 공간
  • 댓글 무시

암호:

    using Microsoft.VisualBasic.FileIO;

    public static List<List<string>> ParseCSV (string csv)
    {
        List<List<string>> result = new List<List<string>>();


        // To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project. 
        using (TextFieldParser parser = new TextFieldParser(new StringReader(csv))) 
        {
            parser.CommentTokens = new string[] { "#" };
            parser.SetDelimiters(new string[] { ";" });
            parser.HasFieldsEnclosedInQuotes = true;

            // Skip over header line.
            //parser.ReadLine();

            while (!parser.EndOfData)
            {
                var values = new List<string>();

                var readFields = parser.ReadFields();
                if (readFields != null)
                    values.AddRange(readFields);
                result.Add(values);
            }
        }

        return result;
    }

7

TinyCsvParser를 작성 했습니다 for .NET . 이는 가장 빠른 .NET 파서 중 하나이며 거의 모든 CSV 형식을 구문 분석하도록 구성 할 수 있습니다.

MIT 라이센스에 따라 배포됩니다 :

NuGet 을 사용 하여 설치할 수 있습니다. 패키지 관리자 콘솔 에서 다음 명령을 실행하십시오 .

PM> Install-Package TinyCsvParser

용법

persons.csv이름, 성 및 생년월일이 있는 CSV 파일에 사람 목록이 있다고 가정 합니다.

FirstName;LastName;BirthDate
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02

시스템의 해당 도메인 모델은 다음과 같습니다.

private class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

TinyCsvParser를 사용하는 경우 CSV 데이터의 열과 도메인 모델의 속성 간의 매핑을 정의해야합니다.

private class CsvPersonMapping : CsvMapping<Person>
{

    public CsvPersonMapping()
        : base()
    {
        MapProperty(0, x => x.FirstName);
        MapProperty(1, x => x.LastName);
        MapProperty(2, x => x.BirthDate);
    }
}

그런 다음 매핑을 사용하여 CSV 데이터를로 구문 분석 할 수 있습니다 CsvParser.

namespace TinyCsvParser.Test
{
    [TestFixture]
    public class TinyCsvParserTest
    {
        [Test]
        public void TinyCsvTest()
        {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
            CsvPersonMapping csvMapper = new CsvPersonMapping();
            CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);

            var result = csvParser
                .ReadFromFile(@"persons.csv", Encoding.ASCII)
                .ToList();

            Assert.AreEqual(2, result.Count);

            Assert.IsTrue(result.All(x => x.IsValid));

            Assert.AreEqual("Philipp", result[0].Result.FirstName);
            Assert.AreEqual("Wagner", result[0].Result.LastName);

            Assert.AreEqual(1986, result[0].Result.BirthDate.Year);
            Assert.AreEqual(5, result[0].Result.BirthDate.Month);
            Assert.AreEqual(12, result[0].Result.BirthDate.Day);

            Assert.AreEqual("Max", result[1].Result.FirstName);
            Assert.AreEqual("Mustermann", result[1].Result.LastName);

            Assert.AreEqual(2014, result[1].Result.BirthDate.Year);
            Assert.AreEqual(1, result[1].Result.BirthDate.Month);
            Assert.AreEqual(1, result[1].Result.BirthDate.Day);
        }
    }
}

사용자 설명서

전체 사용 설명서는 다음에서 구할 수 있습니다.


1

내 KISS 구현은 다음과 같습니다.

using System;
using System.Collections.Generic;
using System.Text;

class CsvParser
{
    public static List<string> Parse(string line)
    {
        const char escapeChar = '"';
        const char splitChar = ',';
        bool inEscape = false;
        bool priorEscape = false;

        List<string> result = new List<string>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < line.Length; i++)
        {
            char c = line[i];
            switch (c)
            {
                case escapeChar:
                    if (!inEscape)
                        inEscape = true;
                    else
                    {
                        if (!priorEscape)
                        {
                            if (i + 1 < line.Length && line[i + 1] == escapeChar)
                                priorEscape = true;
                            else
                                inEscape = false;
                        }
                        else
                        {
                            sb.Append(c);
                            priorEscape = false;
                        }
                    }
                    break;
                case splitChar:
                    if (inEscape) //if in escape
                        sb.Append(c);
                    else
                    {
                        result.Add(sb.ToString());
                        sb.Length = 0;
                    }
                    break;
                default:
                    sb.Append(c);
                    break;
            }
        }

        if (sb.Length > 0)
            result.Add(sb.ToString());

        return result;
    }

}

1
CSV 파일에서 유효한 인용 문자열 내의 줄 바꿈은 처리하지 않습니다.
John Leidegren

Alex가 John이 말하려고하는 것은 RFC 4180 ( ietf.org/rfc/rfc4180.txt- 섹션 2 및 항목 6 참조)을 통해 열이 열 중간에 CR LF를 효과적으로 분산시킬 수 있다는 것입니다. 파일에 2 줄. 귀하의 솔루션은 대부분의 경우 (특히 Excel에서 저장하여 CSV 파일을 만든 경우) 잘 작동하지만이 경우를 다루지 않습니다. 위에서 언급 한 CsvHelper는이 경우를 고려해야합니다.
David Yates

예, 이것은 사실이지만 CSV에 CR LF가있는 경우 CSV를 사용하지 않을 가능성이 있지만 json 또는 xml 또는 고정 길이 형식과 같은 것이 더 적합 할 수 있습니다.
Alex 시작

1

얼마 전에 Microsoft.VisualBasic라이브러리를 기반으로 CSV 읽기 / 쓰기를위한 간단한 클래스를 작성했습니다 . 이 간단한 클래스를 사용하면 2 차원 배열과 같은 CSV로 작업 할 수 있습니다. 다음 링크를 통해 내 수업을 찾을 수 있습니다 : https://github.com/ukushu/DataExporter

간단한 사용법 예 :

Csv csv = new Csv("\t");//delimiter symbol

csv.FileOpen("c:\\file1.csv");

var row1Cell6Value = csv.Rows[0][5];

csv.AddRow("asdf","asdffffff","5")

csv.FileSave("c:\\file2.csv");

헤더를 읽으려면 csv.Rows[0]셀 을 읽는 것만 필요 합니다. :)


1

간단한 구문 분석 요구를위한 단일 소스 파일 솔루션. 불쾌한 모든 경우를 처리합니다. 줄 바꿈 정규화 및 따옴표 붙은 문자열 리터럴에서 줄 바꾸기 처리와 같은. 천만에요!

CSV 파일에 헤더가 있으면 첫 번째 행에서 열 이름을 읽고 열 인덱스를 계산하십시오. 그렇게 간단합니다.

참고 DumpLINQPad 방법입니다, 당신은 LINQPad을 사용하지 않는 경우 있음을 제거 할 수 있습니다.

void Main()
{
    var file1 = "a,b,c\r\nx,y,z";
    CSV.ParseText(file1).Dump();

    var file2 = "a,\"b\",c\r\nx,\"y,z\"";
    CSV.ParseText(file2).Dump();

    var file3 = "a,\"b\",c\r\nx,\"y\r\nz\"";
    CSV.ParseText(file3).Dump();

    var file4 = "\"\"\"\"";
    CSV.ParseText(file4).Dump();
}

static class CSV
{
    public struct Record
    {
        public readonly string[] Row;

        public string this[int index] => Row[index];

        public Record(string[] row)
        {
            Row = row;
        }
    }

    public static List<Record> ParseText(string text)
    {
        return Parse(new StringReader(text));
    }

    public static List<Record> ParseFile(string fn)
    {
        using (var reader = File.OpenText(fn))
        {
            return Parse(reader);
        }
    }

    public static List<Record> Parse(TextReader reader)
    {
        var data = new List<Record>();

        var col = new StringBuilder();
        var row = new List<string>();
        for (; ; )
        {
            var ln = reader.ReadLine();
            if (ln == null) break;
            if (Tokenize(ln, col, row))
            {
                data.Add(new Record(row.ToArray()));
                row.Clear();
            }
        }

        return data;
    }

    public static bool Tokenize(string s, StringBuilder col, List<string> row)
    {
        int i = 0;

        if (col.Length > 0)
        {
            col.AppendLine(); // continuation

            if (!TokenizeQuote(s, ref i, col, row))
            {
                return false;
            }
        }

        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == ',')
            {
                row.Add(col.ToString().Trim());
                col.Length = 0;
                i++;
            }
            else if (ch == '"')
            {
                i++;
                if (!TokenizeQuote(s, ref i, col, row))
                {
                    return false;
                }
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }

        if (col.Length > 0)
        {
            row.Add(col.ToString().Trim());
            col.Length = 0;
        }

        return true;
    }

    public static bool TokenizeQuote(string s, ref int i, StringBuilder col, List<string> row)
    {
        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == '"')
            {
                // escape sequence
                if (i + 1 < s.Length && s[i + 1] == '"')
                {
                    col.Append('"');
                    i++;
                    i++;
                    continue;
                }
                i++;
                return true;
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }
        return false;
    }
}

1

이 목록의 또 다른 하나 인 Cinchoo ETL 여러 파일 형식 (CSV, 플랫 파일, Xml, JSON 등)을 읽고 쓰는 오픈 소스 라이브러리

아래 샘플은 CSV 파일을 빠르게 읽는 방법을 보여줍니다 (POCO 개체 필요 없음).

string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

using (var p = ChoCSVReader.LoadText(csv)
    .WithFirstLineHeader()
    )
{
    foreach (var rec in p)
    {
        Console.WriteLine($"Id: {rec.Id}");
        Console.WriteLine($"Name: {rec.Name}");
    }
}

아래 샘플은 POCO 객체를 사용하여 CSV 파일을 읽는 방법을 보여줍니다.

public partial class EmployeeRec
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void CSVTest()
{
    string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

    using (var p = ChoCSVReader<EmployeeRec>.LoadText(csv)
        .WithFirstLineHeader()
        )
    {
        foreach (var rec in p)
        {
            Console.WriteLine($"Id: {rec.Id}");
            Console.WriteLine($"Name: {rec.Name}");
        }
    }
}

CodeProject 에서 사용 방법에 대한 기사를 확인 하십시오.


0

C # split () 함수를 사용하여 CSV를 올바르게 분할하는 방법 에 대한 무제한의 게시물을 기반으로 합니까? :

string[] tokens = System.Text.RegularExpressions.Regex.Split(paramString, ",");

참고 : 이스케이프 / 중첩 쉼표 등을 처리하지 않으므로 특정 간단한 CSV 목록에만 적합합니다.


2
이것은 매우 나쁘고 느려질 것입니다 :)
EKS

1
아마도 작은 매개 변수 집합에 대해 완벽하고 간단하게 작동하므로 유효하고 유용한 솔루션입니다. 왜 공감해야합니까? "매우 나쁘다"는 약간 극단적 인 것 같아요?
radsdau

1
확실히 모든 CSV 파일에없는 일 / 중첩 쉼표, 어떤 경우 등 윌 작업을 탈출하지만 처리하지 않습니다
NStuke

당신이 옳습니다. 이를 반영하여 답장을 편집하겠습니다. 감사. 그러나 여전히 그 자리가 있습니다.
radsdau

이것은 SQL Server clr dll을 작성하고 다른 외부 패키지를 사용할 수없는 사용 사례에 완벽하게 작동했습니다. 파일 이름과 행 수로 간단한 csv 파일을 구문 분석해야했습니다.
dubvfan87

0

이 코드는 csv를 DataTable로 읽습니다.

public static DataTable ReadCsv(string path)
{
    DataTable result = new DataTable("SomeData");
    using (TextFieldParser parser = new TextFieldParser(path))
    {
        parser.TextFieldType = FieldType.Delimited;
        parser.SetDelimiters(",");
        bool isFirstRow = true;
        //IList<string> headers = new List<string>();

        while (!parser.EndOfData)
        {
            string[] fields = parser.ReadFields();
            if (isFirstRow)
            {
                foreach (string field in fields)
                {
                    result.Columns.Add(new DataColumn(field, typeof(string)));
                }
                isFirstRow = false;
            }
            else
            {
                int i = 0;
                DataRow row = result.NewRow();
                foreach (string field in fields)
                {
                    row[i++] = field;
                }
                result.Rows.Add(row);
            }
        }
    }
    return result;
}

1
TextFieldParser는 Microsoft.VisualBasic.dll에 있습니다.
user3285954

0

스 니펫을 원하는 사람은 라이브러리를 바인딩하거나 패키지를 다운로드하지 않고도 코드를 사용할 수 있습니다. 내가 쓴 버전은 다음과 같습니다.

    public static string FormatCSV(List<string> parts)
    {
        string result = "";

        foreach (string s in parts)
        {
            if (result.Length > 0)
            {
                result += ",";

                if (s.Length == 0)
                    continue;
            }

            if (s.Length > 0)
            {
                result += "\"" + s.Replace("\"", "\"\"") + "\"";
            }
            else
            {
                // cannot output double quotes since its considered an escape for a quote
                result += ",";
            }
        }

        return result;
    }

    enum CSVMode
    {
        CLOSED = 0,
        OPENED_RAW = 1,
        OPENED_QUOTE = 2
    }

    public static List<string> ParseCSV(string input)
    {
        List<string> results;

        CSVMode mode;

        char[] letters;

        string content;


        mode = CSVMode.CLOSED;

        content = "";
        results = new List<string>();
        letters = input.ToCharArray();

        for (int i = 0; i < letters.Length; i++)
        {
            char letter = letters[i];
            char nextLetter = '\0';

            if (i < letters.Length - 1)
                nextLetter = letters[i + 1];

            // If its a quote character
            if (letter == '"')
            {
                // If that next letter is a quote
                if (nextLetter == '"' && mode == CSVMode.OPENED_QUOTE)
                {
                    // Then this quote is escaped and should be added to the content

                    content += letter;

                    // Skip the escape character
                    i++;
                    continue;
                }
                else
                {
                    // otherwise its not an escaped quote and is an opening or closing one
                    // Character is skipped

                    // If it was open, then close it
                    if (mode == CSVMode.OPENED_QUOTE)
                    {
                        results.Add(content);

                        // reset the content
                        content = "";

                        mode = CSVMode.CLOSED;

                        // If there is a next letter available
                        if (nextLetter != '\0')
                        {
                            // If it is a comma
                            if (nextLetter == ',')
                            {
                                i++;
                                continue;
                            }
                            else
                            {
                                throw new Exception("Expected comma. Found: " + nextLetter);
                            }
                        }
                    }
                    else if (mode == CSVMode.OPENED_RAW)
                    {
                        // If it was opened raw, then just add the quote 
                        content += letter;
                    }
                    else if (mode == CSVMode.CLOSED)
                    {
                        // Otherwise open it as a quote 

                        mode = CSVMode.OPENED_QUOTE;
                    }
                }
            }
            // If its a comma seperator
            else if (letter == ',')
            {
                // If in quote mode
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    // Just read it
                    content += letter;
                }
                // If raw, then close the content
                else if (mode == CSVMode.OPENED_RAW)
                {
                    results.Add(content);

                    content = "";

                    mode = CSVMode.CLOSED;
                }
                // If it was closed, then open it raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    results.Add(content);

                    content = "";
                }
            }
            else
            {
                // If opened quote, just read it
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    content += letter;
                }
                // If opened raw, then read it
                else if (mode == CSVMode.OPENED_RAW)
                {
                    content += letter;
                }
                // It closed, then open raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    content += letter;
                }
            }
        }

        // If it was still reading when the buffer finished
        if (mode != CSVMode.CLOSED)
        {
            results.Add(content);
        }

        return results;
    }

0

짧고 간단한 해결책이 있습니다.

                using (TextFieldParser parser = new TextFieldParser(outputLocation))
                 {
                        parser.TextFieldType = FieldType.Delimited;
                        parser.SetDelimiters(",");
                        string[] headers = parser.ReadLine().Split(',');
                        foreach (string header in headers)
                        {
                            dataTable.Columns.Add(header);
                        }
                        while (!parser.EndOfData)
                        {
                            string[] fields = parser.ReadFields();
                            dataTable.Rows.Add(fields);
                        }
                    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.