在.NET中parsing分隔的CSV

我有一个以逗号分隔的格式的文本文件,在大多数字段上分隔" ,我想把它变成我可以枚举的东西(例如,generics集合)。我无法控制文件是输出还是它用于分隔符的字符。

在这种情况下,字段之间用逗号隔开,文本字段用"标记"括起来,我遇到的问题是有些字段中有引号(即8 "托盘),并且偶然被拾取为下一个领域。 在数字字段的情况下,它们周围没有引号,但是它们的确以“+”或“ – ”号开始(描述正数/负数)。

我正在考虑RegEx,但我的技能不是那么好,所以希望有人能想出一些我可以尝试的想法。 这个文件里有大约19,000条logging,所以我试图尽可能有效地做到这一点。 以下是一些示例数据行:

 "00","000000112260 ","Pie Pumpkin ","RET","6.99 "," ","ea ",+0000000006.99000 "00","000000304078 ","Pie Apple caramel ","RET","9.99 "," ","ea ",+0000000009.99000 "00","StringValue here","8" Tray of Food ","RET","6.99 "," ","ea ",-00000000005.3200 

有更多的领域,但你可以得到的图片….

我正在使用VB.NET,我有一个通用的列表设置来接受数据。 我曾尝试使用CSVReader ,它似乎工作,直到你像第三个(在文本字段中的报价)击中logging。 如果我能以某种方式得到它来处理额外的报价,比CSVReader选项将工作得很好。

谢谢!

从这里 :

 Encoding fileEncoding = GetFileEncoding(csvFile); // get rid of all doublequotes except those used as field delimiters string fileContents = File.ReadAllText(csvFile, fileEncoding); string fixedContents = Regex.Replace(fileContents, @"([^\^,\r\n])""([^$,\r\n])", @"$1$2"); using (CsvReader csv = new CsvReader(new StringReader(fixedContents), true)) { // ... parse the CSV 

我build议看看.Net中的TextFieldParserClass 。 你需要包括

 Imports Microsoft.VisualBasic.FileIO.TextFieldParser 

这是一个快速示例:

  Dim afile As FileIO.TextFieldParser = New FileIO.TextFieldParser(FileName) Dim CurrentRecord As String() ' this array will hold each line of data afile.TextFieldType = FileIO.FieldType.Delimited afile.Delimiters = New String() {","} afile.HasFieldsEnclosedInQuotes = True ' parse the actual file Do While Not afile.EndOfData Try CurrentRecord = afile.ReadFields Catch ex As FileIO.MalformedLineException Stop End Try Loop 

试试这个网站。 http://kbcsv.codeplex.com/

我已经find了一个很好的实用程序,这是我find并正确工作的最好的手。 不要浪费你的时间来尝试其他的东西,这是免费的,它的工作原理。

正如这个链接所说… 不要推出自己的CSVparsing器!

build议使用TextFieldParser作为Avi。 微软已经为你做了这个。 如果你写了一个,而且你发现了一个bug,考虑replace它而不是修复这个bug。 我最近做了这个,为我节省了很多时间。

看看FileHelpers库 。

你可以试试CsvHelper ,它可以通过NuGet 。 它遵循CSV的RFC 4180标准。 它将能够处理字段内的任何内容,包括逗号,引号和新行。

CsvHelper使用简单,但也很容易configuration它使用许多不同types的分隔文件。

 CsvReader csv = new CsvReader( streamToFile ); IEnumerable<MyObject> myObjects = csv.GetRecords<MyObject>(); 

如果您想读取较低级别的CSV文件,可以直接使用parsing器,这将以string数组的forms返回每一行。

 var parser = new CsvParser( myTextReader ); while( true ) { string[] line = parser.ReadLine(); if( line == null ) { break; } } 

我发布这个答案,所以我可以解释我是如何做到这一点,为什么…米奇小麦的答案是给了我这种情况下最好的解决scheme,我只是不得不稍微修改它的格式这个数据是导出的。

这是VB代码:

 Dim fixedContents As String = Regex.Replace( File.ReadAllText(csvFile, fileEncoding), "(?<!,)("")(?!,)", AddressOf ReplaceQuotes) 

所使用的正则expression式是我需要更改的,因为某些字段中包含非转义引号,提供的正则expression式似乎不适用于所有示例。 这个使用“向前看”和“向后看”来查看引用是在逗号之后还是在之前。 在这种情况下,它们都是负数(意思是指双引号不在逗号之前或之后)。 这应该意味着报价是在一个string中间。

在这种情况下,而不是直接replace,我正在使用ReplaceQuotes函数来为我处理。 我使用这个的原因是因为我需要一些额外的逻辑来检测它是否在一行的开头。 如果我花了更多的时间,我肯定我可以调整RegEx来考虑行的开始(使用MultiLine等),但是当我快速尝试时,它似乎并没有工作所有。

有了这个,在一个32MB的CSV文件(大约19000行)上使用CSV阅读器,大约需要2秒钟的时间来读取文件,执行正则expression式,加载到CSV阅读器,将所有的数据添加到我的generics类,并完成。 真快!

至less有用于CSV文件的ODBC驱动程序。 但是有不同的CSV格式。

什么产生这些文件? 根据源应用程序的要求,不一定有匹配的驱动程序。

您的问题与CSVReader是在第三个logging中的报价不会用另一个报价(又名双引号)转义。 如果你不逃避他们,那么你将如何处理“,在文本字段的中间?

http://en.wikipedia.org/wiki/Comma-separated_values

(我最终不得不使用文件(使用不同的分隔符),但是文本值中的引号字符没有被转义,我最终编写了我自己的自定义分析器,我不知道这是否是绝对必要的。

这种自定义方法的逻辑是:一次读取文件1行,在逗号分隔每行,删除第一个和最后一个字符(删除外部引号但不影响任何内部引号),然后将数据添加到您的通用名单。 它很短,很容易阅读和使用。

  Dim fr As StreamReader = Nothing Dim FileString As String = "" Dim LineItemsArr() as String Dim FilePath As String = HttpContext.Current.Request.MapPath("YourFile.csv") fr = New System.IO.StreamReader(FilePath) While fr.Peek <> -1 FileString = fr.ReadLine.Trim If String.IsNullOrEmpty(FileString) Then Continue While 'Empty Line LineItemsArr = FileString.Split(",") For Each Item as String In LineItemsArr 'If every item will have a beginning and closing " (quote) then you can just 'cut the first and last characters of the string here. 'ie UpdatedItems = Item. remove first and last character 'Then stick the data into your Generic List (Of String()?) Next End While 
  public static Encoding GetFileEncoding(String fileName) { Encoding Result = null; FileInfo FI = new FileInfo(fileName); FileStream FS = null; try { FS = FI.OpenRead(); Encoding[] UnicodeEncodings = { Encoding.BigEndianUnicode, Encoding.Unicode, Encoding.UTF8 }; for (int i = 0; Result == null && i < UnicodeEncodings.Length; i++) { FS.Position = 0; byte[] Preamble = UnicodeEncodings[i].GetPreamble(); bool PreamblesAreEqual = true; for (int j = 0; PreamblesAreEqual && j < Preamble.Length; j++) { PreamblesAreEqual = Preamble[j] == FS.ReadByte(); } if (PreamblesAreEqual) { Result = UnicodeEncodings[i]; } } } catch (System.IO.IOException) { } finally { if (FS != null) { FS.Close(); } } if (Result == null) { Result = Encoding.Default; } return Result; } 

RegEx排除第一个和最后一个报价将是(?<!^)(?<!,)("")(?!,)(?!$) 。 当然,你需要使用RegexOptions.Multiline。

这样就不需要评估者的function。 我的代码用单引号replace不需要的双引号。

完整的C#代码如下。

 string fixedCSV = Regex.Replace( File.ReadAllText(fileName), @"(?<!^)(?<!;)("")(?!;)(?!$)", "'", RegexOptions.Multiline);