14

我正在尝试使用TextFieldParser导入 CSV 文件。一个特定的 CSV 文件由于其非标准格式而给我带来了问题。有问题的 CSV 的字段用双引号括起来。当特定字段中有一组额外的未转义双引号时,就会出现问题。

这是一个强调问题的过度简化的测试用例。我正在处理的实际 CSV 文件的格式并不完全相同,并且有几十个字段,其中任何一个都可能包含这些可能很棘手的格式问题。

TextReader reader = new StringReader("\"Row\",\"Test String\"\n" +
    "\"1\",\"This is a test string.  It is parsed correctly.\"\n" +
    "\"2\",\"This is a test string with a comma,  which is parsed correctly\"\n" +
    "\"3\",\"This is a test string with double \"\"double quotes\"\". It is parsed correctly\"\n" +
    "\"4\",\"This is a test string with 'single quotes'. It is parsed correctly\"\n" +
    "5,This is a test string with fields that aren't enclosed in double quotes.  It is parsed correctly.\n" +
    "\"6\",\"This is a test string with single \"double quotes\".  It can't be parsed.\"");

using (TextFieldParser parser = new TextFieldParser(reader))
{
    parser.Delimiters = new[] { "," };
    while (!parser.EndOfData)
    {
        string[] fields= parser.ReadFields();
        Console.WriteLine("This line was parsed as:\n{0},{1}",
            fields[0], fields[1]);
    }
}

无论如何,是否可以使用 TextFieldParser 正确解析具有这种格式的 CSV?

4

6 回答 6

8

我同意 Hans Passant 的建议,即解析格式错误的数据不是您的责任。然而,根据鲁棒性原则,面对这种情况的人可能会尝试处理特定类型的畸形数据。我在下面编写的代码适用于问题中指定的数据集。基本上,它检测格式错误的行上的解析器错误,根据第一个字符确定它是否被双引号包裹,然后手动拆分/剥离所有包裹双引号。

using (TextFieldParser parser = new TextFieldParser(reader))
{
    parser.Delimiters = new[] { "," };

    while (!parser.EndOfData)
    {
        string[] fields = null;
        try
        {
            fields = parser.ReadFields();
        }
        catch (MalformedLineException ex)
        {
            if (parser.ErrorLine.StartsWith("\""))
            {
                var line = parser.ErrorLine.Substring(1, parser.ErrorLine.Length - 2);
                fields = line.Split(new string[] { "\",\"" }, StringSplitOptions.None);
            }
            else
            {
                throw;
            }
        }
        Console.WriteLine("This line was parsed as:\n{0},{1}", fields[0], fields[1]);
    }
}

我确信可以编造一个失败的病态示例(例如,在字段值中与双引号相邻的逗号),但任何此类示例在最严格的意义上可能是不可解析的,而问题中给出的问题是尽管格式不正确,但仍可破译。

于 2015-03-24T19:04:37.697 回答
1

Jordan 的解决方案非常好,但它错误地假设错误行总是以双引号开头。我的错误行是这样的:

170,"CMS ALT",853,,,NON_MOVEX,COM,NULL,"2014-04-25",""  204 Route de Trays"

注意最后一个字段有额外/未转义的双引号,但第一个字段很好。所以乔丹的解决方案没有奏效。这是我基于 Jordan 的修改后的解决方案:

using(TextFieldParser parser = new TextFieldParser(new StringReader(csv))) {
 parser.Delimiters = new [] {","};

 while (!parser.EndOfData) {
  string[] fields = null;
  try {
   fields = parser.ReadFields();
  } catch (MalformedLineException ex) {
   string errorLine = SafeTrim(parser.ErrorLine);
   fields = errorLine.Split(',');
  }
 }
}

您可能希望以不同的方式处理 catch 块,但一般概念对我来说非常有用。

于 2018-10-01T22:58:49.053 回答
0

手动执行此操作可能更容易,并且肯定会给您更多控制权:

编辑:对于你澄清的例子,我仍然建议手动处理解析:

using System.IO;

string[] csvFile = File.ReadAllLines(pathToCsv);
foreach (string line in csvFile)
{
    // get the first comma in the line
    // everything before this index is the row number
    // everything after is the row value
    int firstCommaIndex = line.IndexOf(',');

    //Note: SubString used here is (startIndex, length) 
    string row = line.Substring(0, firstCommaIndex+1);
    string rowValue = line.Substring(firstCommaIndex+1).Trim();

    Console.WriteLine("This line was parsed as:\n{0},{1}",
            row, rowValue);
}

对于不允许在字段中使用逗号的通用 CSV:

using System.IO;

string[] csvFile = File.ReadAllLines(pathToCsv);
foreach (string line in csvFile)
{
    string[] fields = line.Split(',');
    Console.WriteLine("This line was parsed as:\n{0},{1}",
            fields[0], fields[1]);
}
于 2013-04-25T23:20:41.337 回答
0

工作解决方案:

using (TextFieldParser csvReader = new TextFieldParser(csv_file_path))
            {
                csvReader.SetDelimiters(new string[] { "," });
                csvReader.HasFieldsEnclosedInQuotes = false;
                string[] colFields = csvReader.ReadFields();

                while (!csvReader.EndOfData)
                {
                    string[] fieldData = csvReader.ReadFields();
                    for (i = 0; i < fieldData.Length; i++)
                    {
                        if (fieldData[i] == "")
                        {
                            fieldData[i] = null;
                        }
                        else
                        {
                            if (fieldData[i][0] == '"' && fieldData[i][fieldData[i].Length - 1] == '"')
                            {
                                fieldData[i] = fieldData[i].Substring(1, fieldData[i].Length - 2);
                            }
                        }
                    }
                    csvData.Rows.Add(fieldData);
                   }
            }
于 2014-07-03T09:09:12.327 回答
0

如果您未设置HasFieldsEnclosedInQuotes = true,则如果数据包含 (,) 逗号,则生成的列列表将更多。例如 "Col1","Col2","Col3" "Test1", 100, "Test1,Test2" "Test2", 200, "Test22" 这个文件应该有 3 列,但是在解析时你会得到 4 个错误的字段。

于 2015-11-23T11:45:58.400 回答
-1

在开始读取文件之前,请在 TextFieldParser 对象上设置HasFieldsEnclosedInQuotes = true 。

于 2013-05-16T13:30:15.823 回答