열에,가 포함될 수있는 CSV를 분할하는 방법


105

주어진

2,1016,7 / 31 / 2008 14 : 22, Geoff Dalgas, 6 / 5 / 2011 22:21, http://stackoverflow.com , "Corvallis, OR", 7679,351,81, b437f461b3fd27387c5d8ab47a293d35,34

C #을 사용하여 위의 정보를 다음과 같이 문자열로 분할하는 방법 :

2
1016
7/31/2008 14:22
Geoff Dalgas
6/5/2011 22:21
http://stackoverflow.com
Corvallis, OR
7679
351
81
b437f461b3fd27387c5d8ab47a293d35
34

보시다시피 열 중 하나에, <= (Corvallis, OR)

// 업데이트 // C # Regex Split 기반 -따옴표 외부의 쉼표

string[] result = Regex.Split(samplestring, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");

1
Java에서는 비슷한 질문 : stackoverflow.com/questions/1757065/…
sgokhales

1
이를 위해 정규식을 사용하는 것은 나쁜 조언입니다. .NET Framework에는 이미 CSV 구문 분석을위한 기본 제공 지원이 있습니다. 수락해야하는이 답변을 참조하십시오. 그렇지 않으면 나는 이것을 stackoverflow.com/questions/3147836/… 의 속임수로 닫을 것입니다. 이것은 똑같이 잘못되었습니다.
Kev

쉼표가 포함 된 CSV 파일 구문 분석에 대한 .NET의 내장 지원이 무엇인지 자세히 설명해 주시겠습니까? Microsoft.VisualBasic.FileIO.TextFieldParser 클래스를 참조하고 있습니까?
AllSolutions

답변:


182

Microsoft.VisualBasic.FileIO.TextFieldParser수업을 사용하십시오 . 이것은 구분 된 파일 TextReader또는 Stream일부 필드가 따옴표로 묶여 있고 일부는 그렇지 않은 경우 구문 분석을 처리 합니다.

예를 들면 :

using Microsoft.VisualBasic.FileIO;

string csv = "2,1016,7/31/2008 14:22,Geoff Dalgas,6/5/2011 22:21,http://stackoverflow.com,\"Corvallis, OR\",7679,351,81,b437f461b3fd27387c5d8ab47a293d35,34";

TextFieldParser parser = new TextFieldParser(new StringReader(csv));

// You can also read from a file
// TextFieldParser parser = new TextFieldParser("mycsvfile.csv");

parser.HasFieldsEnclosedInQuotes = true;
parser.SetDelimiters(",");

string[] fields;

while (!parser.EndOfData)
{
    fields = parser.ReadFields();
    foreach (string field in fields)
    {
        Console.WriteLine(field);
    }
} 

parser.Close();

결과는 다음과 같습니다.

2
1016 년
2008/7/31 14:22
제프 달 가스
2011/6/5 22:21
http://stackoverflow.com
코발리스, OR
7679
351
81
b437f461b3fd27387c5d8ab47a293d35
34

자세한 내용은 Microsoft.VisualBasic.FileIO.TextFieldParser 를 참조하십시오.

참조 Microsoft.VisualBasic추가 .NET 탭에서에 참조를 추가해야합니다 .


9
야,이 솔루션에 대해 대단히 감사합니다. 테이블에로드해야하는 약 50 만 개 이상의 CSV 데이터 행이 있으며 따옴표 안에 쉼표가 포함되어 있습니다. 우리의 길을 건너면 나는 당신이 선택한 성인 음료를 빚지고 있습니다.
Mark Kram

@tim 나는 이것을 사용했고 모든 짝수 줄 번호를 건너 뛰고 1050 줄이있는 파일에서 홀수 줄 번호 만 처리한다는 것을 알았습니다. 어떤 아이디어?
스미스

@Smith-귀하의 코드 또는 샘플 입력을 보지 않고는 전혀 모릅니다. 새 질문을 게시하는 것이 좋습니다. 파일의 짝수 줄에 캐리지 리턴이나 다른 줄 끝 표시가 없을 수도 있습니다.

이 라이브러리를보기 전까지는이 라이브러리에 대해 몰랐습니다. 감사합니다! 다른 사람이 전체 CSV 파일을 구문 분석하는 예제를 원하는 경우 다음 SO 답변을 참조하십시오. stackoverflow.com/a/3508572/3105807
Amy Barrett

2
문자열을받는 생성자를 제공하지 않아서 먼저 스트림으로 변환하는 과정을 건너 뛰어야한다는 이유로 Microsoft를 린치 할 수 있습니까 ?? 그렇지 않으면 좋은 대답입니다.
Loren Pechtel

43

너무 늦었지만 누군가에게 도움이 될 수 있습니다. RegEx를 다음과 같이 사용할 수 있습니다.

Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
String[] Fields = CSVParser.Split(Test);

4
이것은 완벽 해요. 다른 라이브러리 전체를 가져 오는 것보다 이것을 사용하십시오. 브라보.
TheGeekYouNeed

1
매치 로 "," ", asdf을 ,\"DF "

이 솔루션은 올바르게 작동하지 않습니다. 음성 표시를 고려하지 않으므로 읽는 동안 잘못된 위치에 많은 음성 표시가 나타납니다.
AidanH

어떤 줄에 끝 따옴표가없는 경우 : asd, "", "as, \"df ","asd asd ","as
MarmiK

1
이것은 나를 위해 일했으며 인용 된 음성 표시를 설명했습니다. 3 천만 행. 아주 좋고 최소한의 코드.
GBGOLC


4

Excel에서 csv로 구분 된 텍스트를 붙여넣고 "Text to Columns"를 수행하면 "텍스트 한정자"를 묻는 메시지가 표시됩니다. 기본적으로 큰 따옴표로 설정되어 있으므로 큰 따옴표 안의 텍스트를 리터럴로 처리합니다. Excel이 한 번에 한 문자 씩 이동하여이를 구현한다고 생각합니다. "텍스트 한정자"를 만나면 다음 "한정자"로 계속 이동합니다. for 루프와 리터럴 텍스트 안에 있는지를 나타내는 부울을 사용하여 직접 구현할 수 있습니다.

public string[] CsvParser(string csvText)
{
    List<string> tokens = new List<string>();

    int last = -1;
    int current = 0;
    bool inText = false;

    while(current < csvText.Length)
    {
        switch(csvText[current])
        {
            case '"':
                inText = !inText; break;
            case ',':
                if (!inText) 
                {
                    tokens.Add(csvText.Substring(last + 1, (current - last)).Trim(' ', ',')); 
                    last = current;
                }
                break;
            default:
                break;
        }
        current++;
    }

    if (last != csvText.Length - 1) 
    {
        tokens.Add(csvText.Substring(last+1).Trim());
    }

    return tokens.ToArray();
}

3

LumenWorks 와 같은 라이브러리를 사용 하여 CSV 읽기를 수행하십시오. 따옴표가있는 필드를 처리하고 오랫동안 사용했기 때문에 사용자 지정 솔루션보다 전반적으로 더 강력 할 것입니다.


2

.csv 파일이 쉼표로 구분 된 문자열, 쉼표로 구분 된 따옴표로 묶인 문자열 또는이 둘의 혼돈 된 조합 일 수있는 경우 .csv 파일을 구문 분석하는 것은 까다로운 문제입니다. 내가 생각 해낸 솔루션은 세 가지 가능성 중 하나를 허용합니다.

csv 문자열에서 배열을 반환하는 ParseCsvRow () 메서드를 만들었습니다. 먼저 큰 따옴표의 문자열을 quotesArray라는 배열로 분할하여 문자열의 큰 따옴표를 처리합니다. 인용 문자열 .csv 파일은 짝수의 큰 따옴표가있는 경우에만 유효합니다. 열 값의 큰 따옴표는 큰 따옴표 쌍으로 바꿔야합니다 (이는 Excel의 접근 방식입니다). .csv 파일이 이러한 요구 사항을 충족하는 한 구분 기호 쉼표는 큰 따옴표 쌍 외부에만 나타날 것으로 예상 할 수 있습니다. 큰 따옴표 쌍 안에있는 쉼표는 열 값의 일부이며 .csv를 배열로 분할 할 때 무시해야합니다.

내 방법은 quotesArray의 짝수 인덱스 만 확인하여 큰 따옴표 쌍 외부의 쉼표를 테스트합니다. 또한 열 값의 시작과 끝에서 큰 따옴표를 제거합니다.

    public static string[] ParseCsvRow(string csvrow)
    {
        const string obscureCharacter = "ᖳ";
        if (csvrow.Contains(obscureCharacter)) throw new Exception("Error: csv row may not contain the " + obscureCharacter + " character");

        var unicodeSeparatedString = "";

        var quotesArray = csvrow.Split('"');  // Split string on double quote character
        if (quotesArray.Length > 1)
        {
            for (var i = 0; i < quotesArray.Length; i++)
            {
                // CSV must use double quotes to represent a quote inside a quoted cell
                // Quotes must be paired up
                // Test if a comma lays outside a pair of quotes.  If so, replace the comma with an obscure unicode character
                if (Math.Round(Math.Round((decimal) i/2)*2) == i)
                {
                    var s = quotesArray[i].Trim();
                    switch (s)
                    {
                        case ",":
                            quotesArray[i] = obscureCharacter;  // Change quoted comma seperated string to quoted "obscure character" seperated string
                            break;
                    }
                }
                // Build string and Replace quotes where quotes were expected.
                unicodeSeparatedString += (i > 0 ? "\"" : "") + quotesArray[i].Trim();
            }
        }
        else
        {
            // String does not have any pairs of double quotes.  It should be safe to just replace the commas with the obscure character
            unicodeSeparatedString = csvrow.Replace(",", obscureCharacter);
        }

        var csvRowArray = unicodeSeparatedString.Split(obscureCharacter[0]); 

        for (var i = 0; i < csvRowArray.Length; i++)
        {
            var s = csvRowArray[i].Trim();
            if (s.StartsWith("\"") && s.EndsWith("\""))
            {
                csvRowArray[i] = s.Length > 2 ? s.Substring(1, s.Length - 2) : "";  // Remove start and end quotes.
            }
        }

        return csvRowArray;
    }

내 접근 방식의 한 가지 단점은 구분 기호 쉼표를 모호한 유니 코드 문자로 일시적으로 대체하는 방법입니다. 이 문자는 너무 모호해야하므로 .csv 파일에 표시되지 않습니다. 이 문제를 더 많이 처리 할 수 ​​있습니다.


1

따옴표 문자가있는 필드를 포함하는 CSV에 문제가있어서 TextFieldParser를 사용하여 다음을 생각해 냈습니다.

private static string[] parseCSVLine(string csvLine)
{
  using (TextFieldParser TFP = new TextFieldParser(new MemoryStream(Encoding.UTF8.GetBytes(csvLine))))
  {
    TFP.HasFieldsEnclosedInQuotes = true;
    TFP.SetDelimiters(",");

    try 
    {           
      return TFP.ReadFields();
    }
    catch (MalformedLineException)
    {
      StringBuilder m_sbLine = new StringBuilder();

      for (int i = 0; i < TFP.ErrorLine.Length; i++)
      {
        if (i > 0 && TFP.ErrorLine[i]== '"' &&(TFP.ErrorLine[i + 1] != ',' && TFP.ErrorLine[i - 1] != ','))
          m_sbLine.Append("\"\"");
        else
          m_sbLine.Append(TFP.ErrorLine[i]);
      }

      return parseCSVLine(m_sbLine.ToString());
    }
  }
}

StreamReader는 다음과 같이 CSV를 한 줄씩 읽는 데 계속 사용됩니다.

using(StreamReader SR = new StreamReader(FileName))
{
  while (SR.Peek() >-1)
    myStringArray = parseCSVLine(SR.ReadLine());
}

1

오픈 소스 라이브러리 인 Cinchoo ETL을 사용하면 구분 기호가 포함 된 열 값을 자동으로 처리 할 수 ​​있습니다.

string csv = @"2,1016,7/31/2008 14:22,Geoff Dalgas,6/5/2011 22:21,http://stackoverflow.com,""Corvallis, OR"",7679,351,81,b437f461b3fd27387c5d8ab47a293d35,34";

using (var p = ChoCSVReader.LoadText(csv)
    )
{
    Console.WriteLine(p.Dump());
}

산출:

Key: Column1 [Type: String]
Value: 2
Key: Column2 [Type: String]
Value: 1016
Key: Column3 [Type: String]
Value: 7/31/2008 14:22
Key: Column4 [Type: String]
Value: Geoff Dalgas
Key: Column5 [Type: String]
Value: 6/5/2011 22:21
Key: Column6 [Type: String]
Value: http://stackoverflow.com
Key: Column7 [Type: String]
Value: Corvallis, OR
Key: Column8 [Type: String]
Value: 7679
Key: Column9 [Type: String]
Value: 351
Key: Column10 [Type: String]
Value: 81
Key: Column11 [Type: String]
Value: b437f461b3fd27387c5d8ab47a293d35
Key: Column12 [Type: String]
Value: 34

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

도움이되기를 바랍니다.

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