Сложно разбирать файлы .csv, когда файл .csv может быть либо строками, разделенными запятыми, либо строками в кавычках, разделенных запятыми, либо их хаотической комбинацией. Решение, которое я придумал, допускает любую из трех возможностей.
Я создал метод ParseCsvRow (), который возвращает массив из строки csv. Сначала я имею дело с двойными кавычками в строке, разбивая строку на двойные кавычки в массив с именем quotesArray. Строковые .csv-файлы в кавычках действительны только в том случае, если есть четное количество двойных кавычек. Двойные кавычки в значении столбца следует заменить парой двойных кавычек (это подход Excel). Если файл .csv соответствует этим требованиям, можно ожидать, что разделительные запятые будут появляться только вне пар двойных кавычек. Запятые внутри пар двойных кавычек являются частью значения столбца и должны игнорироваться при разделении .csv на массив.
Мой метод будет проверять наличие запятых вне пар с двойными кавычками, глядя только на четные индексы quotesArray. Он также удаляет двойные кавычки из начала и конца значений столбца.
public static string[] ParseCsvRow(string csvrow)
{
const string obscureCharacter = "ᖳ";
if (csvrow.Contains(obscureCharacter)) throw new Exception("Error: csv row may not contain the " + obscureCharacter + " character");
var unicodeSeparatedString = "";
var quotesArray = csvrow.Split('"'); // Split string on double quote character
if (quotesArray.Length > 1)
{
for (var i = 0; i < quotesArray.Length; i++)
{
// CSV must use double quotes to represent a quote inside a quoted cell
// Quotes must be paired up
// Test if a comma lays outside a pair of quotes. If so, replace the comma with an obscure unicode character
if (Math.Round(Math.Round((decimal) i/2)*2) == i)
{
var s = quotesArray[i].Trim();
switch (s)
{
case ",":
quotesArray[i] = obscureCharacter; // Change quoted comma seperated string to quoted "obscure character" seperated string
break;
}
}
// Build string and Replace quotes where quotes were expected.
unicodeSeparatedString += (i > 0 ? "\"" : "") + quotesArray[i].Trim();
}
}
else
{
// String does not have any pairs of double quotes. It should be safe to just replace the commas with the obscure character
unicodeSeparatedString = csvrow.Replace(",", obscureCharacter);
}
var csvRowArray = unicodeSeparatedString.Split(obscureCharacter[0]);
for (var i = 0; i < csvRowArray.Length; i++)
{
var s = csvRowArray[i].Trim();
if (s.StartsWith("\"") && s.EndsWith("\""))
{
csvRowArray[i] = s.Length > 2 ? s.Substring(1, s.Length - 2) : ""; // Remove start and end quotes.
}
}
return csvRowArray;
}
Одним из недостатков моего подхода является то, что я временно заменяю запятые-разделители непонятным символом Юникода. Этот символ должен быть настолько неясным, чтобы он никогда не отображался в вашем файле .csv. Возможно, вы захотите приложить больше усилий для этого.