解析用C#中的引号括起来的CSV文件

我在解析CSV文件时看到了很多样本​​。 但这个是一种烦人的文件……

那么你如何解析这种CSV

“1”,1/2/2010,“样本(”adasdad“)asdada”,“我在门口”Stinky“,所以我会该死的,”“AK”

在大多数情况下,最好的答案可能是@Jim Mischel。 对于大多数传统案例, TextFieldParser似乎正是您想要的 – 虽然它奇怪地存在于Microsoft.VisualBasic命名空间中! 但这种情况并不常见。

我最后一次遇到这个问题的变化,我需要一些非常规的东西,我尴尬地放弃了regexp’ing并通过char检查剔除了一个char。 有时,这不足以做到。 如果你按字节推送,拆分字符串并不是一个问题。

所以我重写了这个案例作为字符串扩展。 我认为这很接近。

请注意, "I was pooping in the door "Stinky", so I'll be damn",是一个特别令人讨厌的案例。 如果没有下面的*** STINKY CONDITION ***代码,你会得到I was pooping in the door "Stinky as one value, so I'll be damn"作为另一个。

对于任何匿名奇怪的分离器/转义情况 ,唯一的方法是使用某种算法来确定每行中“通常”的列数,然后在这种情况下检查固定长度字段,如你的AK状态条目或其他一些可能的标志性作为不一致列的标准化逆止器。 但这可能是不需要的严重的疯狂逻辑,就像编码一样有趣。 正如@Vash所指出的那样,你最好更好地遵循一些标准和编码更多的进攻性。

但这里的问题可能比这更容易。 唯一具有词汇意义的案例是你的例子中的一个 – ", – 双引号,逗号,然后是一个空格。这就是*** STINKY CONDITION ***代码检查的内容。即便如此,这段代码变得更加肮脏比我想要的,这意味着你有一些奇怪的边缘情况,比如"This is also stinky," afab","Now what?" 哎呀,即使是"A,"B","C"也没有在这个代码中工作,iirc,因为我把开始和结束字符视为在前后固定时已经逃脱了。所以我们很大程度上回来了来@Vash的评论!

对于单行if语句的所有括号都道歉,但我现在陷入了StyleCop世界。 我不一定建议你使用它 – strictEscapeToSplitEvaluation加上STINKY CONDITION使这有点复杂。 但是值得注意的是,对于引用很聪明的普通csv解析器更加直接到达单调乏味,但在其他方面是微不足道的。

 namespace YourFavoriteNamespace { using System; using System.Collections.Generic; using System.Text; public static class Extensions { public static Queue SplitSeeingQuotes(this string valToSplit, char splittingChar = ',', char escapeChar = '"', bool strictEscapeToSplitEvaluation = true, bool captureEndingNull = false) { Queue qReturn = new Queue(); StringBuilder stringBuilder = new StringBuilder(); bool bInEscapeVal = false; for (int i = 0; i < valToSplit.Length; i++) { if (!bInEscapeVal) { // Escape values must come immediately after a split. // abc,"b,ca",cab has an escaped comma. // abc,b"ca,c"ab does not. if (escapeChar == valToSplit[i] && (!strictEscapeToSplitEvaluation || (i == 0 || (i != 0 && splittingChar == valToSplit[i - 1])))) { bInEscapeVal = true; // not capturing escapeChar as part of value; easy enough to change if need be. } else if (splittingChar == valToSplit[i]) { qReturn.Enqueue(stringBuilder.ToString()); stringBuilder = new StringBuilder(); } else { stringBuilder.Append(valToSplit[i]); } } else { // Can't use switch b/c we're comparing to a variable, I believe. if (escapeChar == valToSplit[i]) { // Repeated escape always reduces to one escape char in this logic. // So if you wanted "I'm ""double quote"" crazy!" to come out with // the double double quotes, you're toast. if (i + 1 < valToSplit.Length && escapeChar == valToSplit[i + 1]) { i++; stringBuilder.Append(escapeChar); } else if (!strictEscapeToSplitEvaluation) { bInEscapeVal = false; } // *** STINKY CONDITION *** // Kinda defense, since only `", ` really makes sense. else if ('"' == escapeChar && i + 2 < valToSplit.Length && valToSplit[i + 1] == ',' && valToSplit[i + 2] == ' ') { i = i+2; stringBuilder.Append("\", "); } // *** EO STINKY CONDITION *** else if (i+1 == valToSplit.Length || (i + 1 < valToSplit.Length && valToSplit[i + 1] == splittingChar)) { bInEscapeVal = false; } else { stringBuilder.Append(escapeChar); } } else { stringBuilder.Append(valToSplit[i]); } } } // NOTE: The `captureEndingNull` flag is not tested. // Catch null final entry? "abc,cab,bca," could be four entries, with the last an empty string. if ((captureEndingNull && splittingChar == valToSplit[valToSplit.Length-1]) || (stringBuilder.Length > 0)) { qReturn.Enqueue(stringBuilder.ToString()); } return qReturn; } } } 

可能值得一提的是, 你给自己的“答案”在其示例字符串中没有“Stinky”问题。 ; ^)

[了解我们问你们三年后],我会说你们的例子并不像这里的人们那样疯狂。 我可以看到想要将转义字符(在本例中为" )”视为转义字符, 只有它们是拆分字符后的第一个值,或者在找到一个开放转义后,只有在拆分器之前找到转义字符时才停止 ;在这种情况下,分离器显然是,

如果你的csv行是abc,bc"a,ca"b ,我希望这意味着我们有三个值: abcbc"aca"b

您的"The sample ("adasdad") asdada"列中的相同交易 – 不开始和结束单元格值的引号不是转义字符,并且不一定需要加倍来维持含义。 所以我在这里添加了一个strictEscapeToSplitEvaluation标志。

请享用。 ; ^)

我强烈建议使用TextFieldParser 。 使用String.Split或正则表达式的手动编码解析器几乎总是error handling带有嵌入式引号或嵌入式分隔符的带引号的字段。

但是,如果它处理了你的特定例子,我会感到惊讶。 正如其他人所说,这条线充其量是模棱两可的。

拆分基于

我会使用MyString.IndexOf(“\”,“

然后对部分进行子串。 除此之外,我确定有人写了一个csv解析器,可以处理这个:)

我找到了解析这个格式错误的CSV的方法。 我寻找一个模式并找到它….我首先用一个字符替换(“,”)…像“¤”然后分开它…

由此:

 "Annoying","CSV File","poop@mypants.com",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby","yeah!" 

对此:

 "Annoying¤CSV File¤poop@mypants.com",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby¤yeah!" 

然后分开它:

 ArrayA[0]: "Annoying //this value will be trimmed by replace("\"","") same as the array[4] ArrayA[1]: CSV File ArrayA[2]: poop@mypants.com",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby ArrayA[3]: yeah!" 

拆分后,我将用Array替换ArrayA [2]“和”中的字符串,然后再将其拆分

由此

 ArrayA[2]: poop@mypants.com",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby 

对此

 ArrayA[2]: poop@mypants.com¤1999,01-20-2001¤oh,boy¤01-20-2001¤yeah baby 

然后再拆分它,然后转向这个

 ArrayB[0]: poop@mypants.com ArrayB[1]: 1999,01-20-2001 ArrayB[2]: oh,boy ArrayB[3]: 01-20-2001 ArrayB[4]: yeah baby 

最后……我将仅将年份和ArrayB [1]中的日期拆分为ArrayC

这很乏味但没有其他方法可以做到……

你可以用","分割字符串。 建议csv文件可以将每个单元格值括在引号中,如“1”,“2”,“3”……

如果每条线都不同,我不知道你怎么可能。 此行格式不正确。 值中包含的引号必须加倍,如下所示。 我甚至无法确定应该终止值的位置。

 "1",1/2/2010,"The sample (""adasdad"") asdada","I was pooping in the door ""Stinky"", so I'll be damn","AK" 

这是我解析CSV文件的代码,但我没有看到任何代码如何知道如何处理你的行,因为它的格式不正确。

您可能想尝试一下CsvReader 。 它将处理带引号的字符串,因此您只需删除前导和尾随引号。

如果您的字符串包含昏迷,它将失败。 为了避免这种情况,报价需要加倍,如其他答案所述。

由于没有(正确的).csv解析器可以正确地解析非csv数据,因此任务不是解析数据,而是解决文件(然后解析正确的数据)。

要修复数据,您需要一个坏行列表(发送给负责人工编辑的垃圾负责人)。 要获得这样的列表,你可以

  1. 使用具有正确导入规范的Access来导入文件。 您将获得导入失败的列表。

  2. 编写一个脚本/程序,通过OLEDB文本驱动程序打开文件。

示例文件:

 "Id","Remark","DateDue" 1,"This is good",20110413 2,"This is ""good""",20110414 3,"This is ""good"","bad",and "ugly",,20110415 4,"This is ""good""" again,20110415 

示例SQL /结果:

  SELECT * FROM [badcsv01.csv] Id Remark DateDue 1 This is good 4/13/2011 2 This is "good" 4/14/2011 3 This is "good", NULL 4 This is "good" again 4/15/2011 SELECT * FROM [badcsv01.csv] WHERE DateDue Is Null Id Remark DateDue 3 This is "good", NULL