2011-07-01 75 views
59

鑑於如何分割CSV它的列可以包含,

2,1016,7 /二千○八分之三十一14:22,傑夫達爾加斯6 /二千零十一分之五22:21,http://stackoverflow.com,「科瓦利斯,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 

正如你可以看到列的一個載,< =(科瓦利斯,OR)

// //更新基於 上 C# Regex Split - commas outside quotes

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

雖然在Java中,類似的問題:http://stackoverflow.com/questions/1757065/java-splitting-a-comma-separated-string-but-ignoring-commas-in - 引用 –

+1

@saugok,對不起,先生,不是。 – q0987

+0

使用正則表達式來做到這一點是不好的建議。 .NET Framework已經內置了對解析CSV的支持。看到這個答案是你應該接受的答案。否則,我會關閉這是一個愚蠢的http://stackoverflow.com/questions/3147836/c-regex-split-commas-outside-quotes這是同樣錯誤。 – Kev

回答

131

使用Microsoft.VisualBasic.FileIO.TextFieldParser類。這將處理解析分隔文件TextReaderStream,其中一些字段用引號括起來,而另一些則不用。

例如:

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 
7/31/2008 14:22 
Geoff Dalgas 
6/5/2011 22:21 
http://stackoverflow.com 
Corvallis, OR 
7679 
351 
81 
b437f461b3fd27387c5d8ab47a293d35 
34 

更多信息參見Microsoft.VisualBasic.FileIO.TextFieldParser

您需要在添加引用.NET選項卡中添加對Microsoft.VisualBasic的引用。

+0

非常感謝你!這工作非常好! – vldmrrdjcc

+0

@tim我用這個,並注意到它跳過所有的偶數行號,只處理一個文件中有1050行的奇數行號。有任何想法嗎? – Smith

2

使用像LumenWorks庫做你的CSV閱讀。它會處理帶有引號的字段,並且由於已經存在了很長時間,它可能總體上比您的定製解決方案更強大。

4

你可以在所有的逗號之間進行拆分,所有逗號的引號都是偶數。

您還想查看關於處理逗號的CSV格式的specf

有用的鏈接:C# Regex Split - commas outside quotes

+3

@ q0987 - 這不是正確的答案。還有就是內置支持在此框架:http://stackoverflow.com/questions/6542996/how-to-split-csv-whose-columns-may-contain/6543418#6543418 – Kev

3

我看到,如果你粘貼在Excel CSV分隔符的文本,並做了「分列」,它問你一個「文本限定符」。它默認爲雙引號,因此它將雙引號內的文本視爲文字。我想象一下,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

當.csv文件可能是逗號分隔的字符串,逗號分隔的引號字符串或兩者的混淆組合時,解析.csv文件是一件棘手的事情。我提出的解決方案允許三種可能性中的任何一種。

我創建了一個方法ParseCsvRow(),它從csv字符串返回一個數組。我首先通過將雙引號中的字符串拆分爲一個名爲quotesArray的數組來處理字符串中的雙引號。引用的字符串。csv文件只有在偶數個雙引號時纔有效。列值中的雙引號應替換爲一對雙引號(這是Excel的方法)。只要.csv文件符合這些要求,您就可以期待分隔符逗號只出現在雙引號對之外。雙引號對內的逗號是列值的一部分,在將.csv分割爲數組時應該忽略。

我的方法將通過僅查看引用數組的引號來測試雙引號對之外的逗號。它還從列值的開始和結束中刪除雙引號。

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; 
    } 

我的方法的一個缺點是我暫時用一個模糊的unicode字符替代分隔符逗號的方式。這個角色需要非常模糊,它不會出現在你的.csv文件中。你可能想要更多的處理這個。

1

我有一個包含字段與他們引號字符,所以使用使用TextFieldParser一個CSV一個問題,我想出了以下內容:

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()); 
} 
相關問題