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