Работа с полями, содержащими неизолированные двойные кавычки с 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?

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

Рабочее решение:

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

Ответ 4

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

Ответ 5

Перед началом чтения установите HasFieldsEnclosedInQuotes = true на объект TextFieldParser.