Работа с полями, содержащими неоткрытые двойные кавычки с TextFieldParser

Я пытаюсь импортировать CSV-файл с помощью TextFieldParser. Конкретный файл 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]);
    }
}

есть ли в любом случае, чтобы правильно проанализировать CSV с этим типом форматирования с помощью TextFieldParser?

6 ответов


Я согласен с Советом Ханса Пассанта, что вы не несете ответственности за анализ искаженных данных. Однако, в соответствии с Принцип Робастности, кто-то столкнулся с этой ситуацией может попытаться обработать определенные типы искаженных данных. Код, который я написал ниже, работает над набором данных, указанным в вопросе. В основном он обнаруживает ошибку парсера на искаженной строке, определяет, является ли она двойной кавычкой, обернутой на основе первого символа, а затем разбивает/разбивает все перенос двойных кавычек вручную.

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

Я уверен, что можно состряпать патологический пример, когда это не удается (например, запятые, соседние с двойными кавычками в значении поля), но любые такие примеры, вероятно, будут непростимыми в самом строгом смысле, тогда как проблемная строка, данная в вопросе, расшифровывается, несмотря на то, что она искажена.


может быть проще просто сделать это вручную, и это, безусловно, даст вам больше контроля:

изменить: Для вашего уточненного примера я по-прежнему предлагаю вручную обрабатывать синтаксический анализ:

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

Рабочего Раствора :

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

Если вы не установили HasFieldsEnclosedInQuotes = true результирующий список столбцов будет больше, если данные содержат (,) запятая. например "На Col1","Столбец Col2","Кол3" "Test1", 100, " Test1, Test2" "Test2", 200, " Test22" Этот файл должен иметь 3 столбца, но при разборе вы получите 4 поля, что неправильно.


решение Джордана довольно хорошее, но оно делает неправильное предположение, что строка ошибки всегда будет начинаться с двойной кавычки. Моя строка ошибки была такой:

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

обратите внимание, что в последнем поле были дополнительные/неоткрытые двойные кавычки, но первое поле было в порядке. Так что решение Джордана не сработало. Вот мое модифицированное решение, основанное на Иорданском:

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 по-разному, но общая концепция отлично работает для меня.


пожалуйста HasFieldsEnclosedInQuotes = true на объекте TextFieldParser перед началом чтения файла.