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

时间:2011-05-03 06:12:52

标签: c# csv

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

那么你如何解析这种CSV

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

10 个答案:

答案 0 :(得分:5)

在大多数情况下,最好的答案可能是@Jim Mischel。 TextFieldParser似乎正是您对大多数传统案例的要求 - 虽然奇怪lives in the Microsoft.VisualBasic namespace! 但这种情况并非常规。

我最后一次遇到这个问题的变种,我需要一些非常规的东西,我尴尬地放弃了正则表达式并且通过char检查剔除了一个char。有时,这样做不够错误。如果你按字节推送,拆分字符串并不是一个很难的问题。

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

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

对于任何匿名怪异的分离器/逃逸案例,唯一能做得比更好的方法是使用某种算法来确定"通常"每行中的列数,然后在这种情况下检查固定长度字段,如",状态条目或某些其他可能的标记,作为非符合列的标准化逆止器。但这可能并不需要那种严肃的疯狂逻辑,就像编码一样有趣。正如@Vash所指出的那样,你最好不要遵循某些标准,并且更难以编码。

但这里的问题可能比这更容易。 唯一具有词汇意义的案例是您的示例中的案例 - *** STINKY CONDITION *** - 双引号,逗号,然后是空格。这就是"This is also stinky," a f a b","Now what?"的内容代码检查。即便如此,这段代码变得比我更糟糕了,这意味着你有一些奇怪的边缘情况,比如"A,"B","C"哎呀,即使if现在还没有在这段代码中工作,iirc,因为我将开始和结束字符视为已经过前后固定的转义。所以我们很大程度上回到了@Vash的评论!

为一行strictEscapeToSplitEvaluation语句的所有括号道歉,但我现在陷入了StyleCop世界。我并不一定建议你使用它 - namespace YourFavoriteNamespace { using System; using System.Collections.Generic; using System.Text; public static class Extensions { public static Queue<string> SplitSeeingQuotes(this string valToSplit, char splittingChar = ',', char escapeChar = '"', bool strictEscapeToSplitEvaluation = true, bool captureEndingNull = false) { Queue<string> qReturn = new Queue<string>(); 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 CONDITION使这有点复杂。但是值得注意的是,一个普通的csv解析器,对于引用而言更加直接,但却非常简单,但却是微不足道的。

"

可能值得一提的是"answer" you gave yourself没有&#34; Stinky&#34;其示例字符串中的问题。 ; ^)

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

如果您的csv行是abc,我希望这意味着我们有三个值:bc"aca"b"The sample ("adasdad") asdada"

您的strictEscapeToSplitEvaluation列中的相同优惠 - 不开始和结束单元格值的引号不会转义字符,也不一定需要加倍才能保持意义。所以我在这里添加了{{1}}标志。

享受。 ; ^)

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

基于

拆分
  

”,

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

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

答案 3 :(得分:1)

我找到了解析这个格式错误的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

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

答案 4 :(得分:1)

还有另一个开源库Cinchoo ETL,可以很好地处理带引号的字符串。这是示例代码。

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

using (var r = ChoCSVReader.LoadText(csv)
    .QuoteAllFields()
    )
{
    foreach (var rec in r)
        Console.WriteLine(rec.Dump());
}

输出:

[Count: 5]
Key: Column1 [Type: Int64]
Value: 1
Key: Column2 [Type: DateTime]
Value: 1/2/2010 12:00:00 AM
Key: Column3 [Type: String]
Value: The sample(adasdad) asdada
Key: Column4 [Type: String]
Value: I was pooping in the door Stinky, so I'll be damn
Key: Column5 [Type: String]
Value: AK

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

Here's my code to parse a CSV file但是我没有看到任何代码知道如何处理你的行,因为它的格式不正确。

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

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

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

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

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

  3. 示例文件:

    "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   
    

答案 9 :(得分:0)

首先,您需要对列名称进行操作:

            DataTable pbResults = new DataTable();
            OracleDataAdapter oda = new OracleDataAdapter(cmd);
            oda.Fill(pbResults);

            StringBuilder sb1 = new StringBuilder();
            StringBuilder sb2 = new StringBuilder();
            IEnumerable<string> columnNames = pbResults.Columns.Cast<DataColumn>().Select(column => column.ColumnName);

            sb1.Append(string.Join("\"" + "," + "\"", columnNames));                
            sb2.Append("\"");
            sb2.Append(sb1);
            sb2.AppendLine("\"");

第二步,您将为每一行做一次:

            foreach (DataRow row in pbResults.Rows)
            {
                IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
                sb2.Append("\"");
                sb2.Append(string.Join("\"" + "," + "\"", fields));
                sb2.AppendLine("\"");
            }