Разделить строку в С#
Я думал, что это будет тривиально, но я не могу заставить это работать.
Предположим, что строка в CSV файле:
"Barack Obama", 48, "President", "1600 Penn Ave, Washington DC"
string[] tokens = line.split(',')
Я ожидаю этого:
"Barack Obama"
48
"President"
"1600 Penn Ave, Washington DC"
но последний токен 'Washington DC'
не "1600 Penn Ave, Washington DC"
.
Есть ли простой способ заставить функцию split игнорировать запятую внутри кавычек?
У меня нет контроля над файлом CSV, и он не отправляется мне. Клиент A будет использовать приложение для чтения файлов, предоставленных внешним человеком.
Ответы
Ответ 1
Возможно, вам придется написать свою собственную функцию разделения.
- Итерации через каждый char в строке
- Когда вы нажмете символ
"
, переключите логическое
- Когда вы нажимаете запятую, если bool является true, игнорируйте его, иначе у вас есть ваш токен
Вот пример:
public static class StringExtensions
{
public static string[] SplitQuoted(this string input, char separator, char quotechar)
{
List<string> tokens = new List<string>();
StringBuilder sb = new StringBuilder();
bool escaped = false;
foreach (char c in input)
{
if (c.Equals(separator) && !escaped)
{
// we have a token
tokens.Add(sb.ToString().Trim());
sb.Clear();
}
else if (c.Equals(separator) && escaped)
{
// ignore but add to string
sb.Append(c);
}
else if (c.Equals(quotechar))
{
escaped = !escaped;
sb.Append(c);
}
else
{
sb.Append(c);
}
}
tokens.Add(sb.ToString().Trim());
return tokens.ToArray();
}
}
Затем просто позвоните:
string[] tokens = line.SplitQuoted(',','\"');
Бенчмарки
Ниже приведены результаты бенчмаркинга моего кода и кода Dan Tao. Я рад оценить любые другие решения, если люди хотят их?
Код:
string input = "\"Barak Obama\", 48, \"President\", \"1600 Penn Ave, Washington DC\""; // Console.ReadLine()
string[] tokens = null;
// run tests
DateTime start = DateTime.Now;
for (int i = 0; i < 1000000; i++)
tokens = input.SplitWithQualifier(',', '\"', false);
Console.WriteLine("1,000,000 x SplitWithQualifier = {0}ms", DateTime.Now.Subtract(start).TotalMilliseconds);
start = DateTime.Now;
for (int i = 0; i<1000000;i++)
tokens = input.SplitQuoted(',', '\"');
Console.WriteLine("1,000,000 x SplitQuoted = {0}ms", DateTime.Now.Subtract(start).TotalMilliseconds);
Вывод:
1,000,000 x SplitWithQualifier = 8156.25ms
1,000,000 x SplitQuoted = 2406.25ms
Ответ 2
У меня есть метод расширения SplitWithQualifier
, который я использую здесь и там, который использует Regex
.
Я не претендую на надежность этого кода, но он все время работал у меня.
// mangled code horribly to fit without scrolling
public static class CsvSplitter
{
public static string[] SplitWithQualifier(this string text,
char delimiter,
char qualifier,
bool stripQualifierFromResult)
{
string pattern = string.Format(
@"{0}(?=(?:[^{1}]*{1}[^{1}]*{1})*(?![^{1}]*{1}))",
Regex.Escape(delimiter.ToString()),
Regex.Escape(qualifier.ToString())
);
string[] split = Regex.Split(text, pattern);
if (stripQualifierFromResult)
return split.Select(s => s.Trim().Trim(qualifier)).ToArray();
else
return split;
}
}
Использование:
string csv = "\"Barak Obama\", 48, \"President\", \"1600 Penn Ave, Washington DC\"";
string[] values = csv.SplitWithQualifier(',', '\"', true);
foreach (string value in values)
Console.WriteLine(value);
Вывод:
Barak Obama
48
President
1600 Penn Ave, Washington DC
Ответ 3
Я вижу из большей картины, что вы на самом деле пытаетесь разобрать CSV-вход. Поэтому вместо того, чтобы советовать о том, как правильно разбить строку, я бы вместо этого рекомендовал вам использовать парсер CSV для выполнения такого рода действий.
Быстрый считыватель CSV
Я бы рекомендовал библиотеку (исходный код), которую вы можете получить на этой странице CodeProject: http://www.codeproject.com/KB/database/CsvReader.aspx p >
Я лично использую его сам и люблю. Это родной код .NET и намного быстрее, чем использование OLEDB (который также может выполнять синтаксический анализ CSV для вас, но поверьте, он медленный).
Ответ 4
Для этого вы должны использовать Microsoft.VisualBasic.FileIO.TextFieldParser
. Он будет обрабатывать все CSV файлы правильно для вас, см.: Аналогичный вопрос с примером, использующим TextFieldParser
PS: Не бойтесь использовать dll Microsoft.VisualBasic в проекте С#, все это .NET: -)
Ответ 5
Это будет ожидаемое поведение, поскольку кавычки - это всего лишь еще один строковый символ в С#. Похоже, что вы находитесь за процитированными токенами или числовыми токенами.
Я думаю, вам может понадобиться использовать Regex для разделения строк, если кто-то другой не знает лучшего способа.
Или вы могли бы просто прокрутить строку по одному символу за один раз, создав строку, когда вы идете, и построите маркеры таким образом. Это старая школа, но может быть самым надежным способом в вашем случае.
Ответ 6
Вы не можете разобрать CSV-строку с простым разделителем на запятых, потому что некоторое содержимое ячейки будет содержать запятые, которые не предназначены для разграничения данных, но фактически являются частью содержимого ячейки.
Вот ссылка на простой метод С#, основанный на регулярном выражении, который преобразует ваш CSV в ручную DataTable
:
http://www.hotblue.com/article0000.aspx?a=0006
Работа с DataTables очень проста - сообщите мне, если вам нужен образец кода для этого.
Ответ 7
Я бы рекомендовал вместо этого использовать регулярное выражение. Это позволит вам извлекать более сложные подстроки гораздо более универсальным образом (именно так, как вы хотите).
http://www.c-sharpcorner.com/uploadfile/prasad_1/regexppsd12062005021717am/regexppsd.aspx
http://oreilly.com/windows/archive/csharp-regular-expressions.html
Ответ 8
Не можете ли вы изменить способ создания CSV? Используя OpenOffice, вы можете установить разделитель char (use;) и как строка делится (используя "или" ).
Это было бы так: "Президент", "1600 Пенн-авеню, Вашингтон, округ Колумбия"
Ответ 9
string temp = line.Replace( "\" "," ");
string [] tokens = temp.Split(',')