Разбить строку по разделителю C#
Есть файл csv файл, в котором содержатся 2 столбца чисел
3,"5,19615242270663"
"4,5",8
6,"11,1803398874989"
Как получить двумерный массив чисел из этого списка?
Ответы (2 шт):
Если вам нужно это реализовать для учёбы или тренировки: не буду давать готовый код, чтобы вы и сами научились, дам лишь подсказку.
Смотрите, вам нужно написать настоящий парсер. У вас могут быть запятые в кавычках, так что распарсить «наивно» через string.Split не получится. Делайте вот что.
Сканируйте строку по одному символу. Заведите флаг, который будет указывать, находитесь вы сейчас внутри кавычек или нет.
Если вы вне кавычек встречаете запятую, то предыдущая запись окончилась. Если в этом режиме встречаете перевод строки (CR + LF), то строка CSV окончена. Если вы встречаете кавычку, находясь в начале записи, переходите в режим «в кавычках». Кавычки не в начале записи являются нарушением формата.
Если вы внутри кавычек встречаете запятую, это не конец записи, продолжайте сканировать. Если вы встретили кавычку, выходите из режима «в кавычках», если только следующий символ не кавычка. (А если следующий символ кавычка, то эти две кавычки вместе дают одну кавычку в значении записи.) Если встретили перевод строки, он не заканчивает строку, а включается в запись. По выходу из режима кавычек должен быть конец записи, т. е. или запятая, или CR + LF, или конец текста, иначе CSV невалиден.
После того, как получите все записи, убедитесь, что каждая строка содержит одинаковое количество записей (это можно делать и в процессе разбора текста). Первая строка имеет право содержать (или не содержать) имена колонок, все последующие строки содержат значения.
Теперь можно попытаться все записи сконвертировать в double при помощи double.TryParse.
Если что, вот вам RFC с описанием формата: https://tools.ietf.org/html/rfc4180.
А если это не учебное задание, то берите нормальный CSV-парсер, а не стройте велосипеды.
"Наивная" реализация того, что вам нужно, умеющая переварить ваш пример из вопроса, может выглядеть примерно так.
static void Main(string[] args)
{
string text = @"3,""5,19615242270663""
""4,5"",8
6,""11,1803398874989""
";
try
{
double[,] numbers = CsvToDoubleArray(text);
for (int i = 0; i < numbers.GetLength(0); i++)
Console.WriteLine(string.Join(" ", Enumerable.Range(0, numbers.GetLength(1)).Select(j => numbers[i, j])));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static double[,] CsvToDoubleArray(string text)
{
string[] lines = text.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
double[,] result = null;
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
List<string> tokens = new List<string>();
StringBuilder token = new StringBuilder();
bool mode = false;
for (int j = 0; j < line.Length; j++)
{
switch (line[j])
{
case '\\':
j++;
if (j < line.Length)
token.Append(line[j]);
break;
case '"':
mode = !mode;
break;
case ',':
if (mode)
token.Append(line[j]);
else
{
tokens.Add(token.ToString());
token.Clear();
}
break;
default:
token.Append(line[j]);
break;
}
}
tokens.Add(token.ToString());
if (result == null)
result = new double[lines.Length, tokens.Count];
for (int j = 0; j < result.GetLength(1); j++)
{
result[i, j] = double.Parse(tokens[j]);
}
}
return result;
}
Этот код может потребовать значительной доработки в разных направлениях, от обработки разных ошибок в CSV, до поддержки разных разделителей и других моментов, указанных в соседнем ответе от @VladD. Смысл этого примера в том, что он работает с данными, которые вы привели. Как он будет работать с другими данными, мне неизвестно.
Вывод в консоль
3 5,19615242270663
4,5 8
6 11,1803398874989