Учитывая такую строку:
a,"string, with",various,"values, and some",quoted
Какой хороший алгоритм разбить это на основе запятых, игнорируя запятые внутри цитируемых разделов?
На выходе должен быть массив:
[ "a", "string, with", "various", "values, and some", "quoted" ]
Это означало бы неправильную кавычку входной строки, поэтому можно было бы выбросить исключение.
как бы вы поместили цитату в кавычки?





Конечно, лучше использовать парсер CSV, но ради удовольствия вы можете:
Loop on the string letter by letter.
If current_letter == quote :
toggle inside_quote variable.
Else if (current_letter ==comma and not inside_quote) :
push current_word into array and clear current_word.
Else
append the current_letter to current_word
When the loop is done push the current_word into array
Здесь автор добавил каплю кода C#, который обрабатывает сценарий, с которым у вас возникла проблема:
Перевод не должен быть слишком сложным.
Если бы выбранный мной язык не предлагал способа сделать это, не задумываясь, я бы сначала рассмотрел два варианта в качестве простого выхода:
Предварительно проанализируйте и замените запятые в строке другим управляющим символом, затем разделите их, а затем выполните последующий анализ массива для замены управляющего символа, использованного ранее, запятыми.
В качестве альтернативы разделите их на запятые, а затем проанализируйте полученный массив в другой массив, проверяя ведущие кавычки для каждой записи массива и объединяя записи, пока я не достигну завершающей цитаты.
Однако это хаки, и если это чисто «умственное» упражнение, то я подозреваю, что они окажутся бесполезными. Если это реальная проблема, то было бы полезно знать язык, чтобы мы могли дать какой-то конкретный совет.
Я ищу алгоритм для аналогичной проблемы, когда мне нужно обрабатывать огромные текстовые файлы (в ГБ). Эти текстовые файлы содержат квалифицированные данные, то есть разделитель полей / записей является частью данных, если они заключены в одинарные / двойные кавычки. Я ищу алгоритм, который поможет мне обрабатывать эти файлы параллельно (в нескольких потоках). Мы используем язык Java. Дайте мне знать, если у вас есть предложения
Я использую это для синтаксического анализа строк, не уверен, помогает ли это здесь; но возможно с небольшими изменениями?
function getstringbetween($string, $start, $end){
$string = " ".$string;
$ini = strpos($string,$start);
if ($ini == 0) return "";
$ini += strlen($start);
$len = strpos($string,$end,$ini) - $ini;
return substr($string,$ini,$len);
}
$fullstring = "this is my [tag]dog[/tag]";
$parsed = getstringbetween($fullstring, "[tag]", "[/tag]");
echo $parsed; // (result = dog)
/ мп
Вот простой алгоритм:
'"''"'.#COMMA#'"', отметьте те элементы в массиве, где индекс% 2 == 0','.#COMMA# символом ','.Вот реализация Python:
(исправлено для ручки '"a, b", c, "d, e, f, h", "i, j, k"')
def parse_input(input):
quote_mod = int(not input.startswith('"'))
input = input.split('"')
for item in input:
if item == '':
input.remove(item)
for i in range(len(input)):
if i % 2 == quoted_mod:
input[i] = input[i].replace(",", "#COMMA#")
input = "".join(input).split(",")
for item in input:
if item == '':
input.remove(item)
for i in range(len(input)):
input[i] = input[i].replace("#COMMA#", ",")
return input
# parse_input('a,"string, with",various,"values, and some",quoted')
# -> ['a,string', ' with,various,values', ' and some,quoted']
# parse_input('"a,b",c,"d,e,f,h","i,j,k"')
# -> ['a,b', 'c', 'd,e,f,h', 'i,j,k']
Это стандартный синтаксический анализ в стиле CSV. Многие люди пытаются сделать это с помощью регулярных выражений. Вы можете получить около 90% с помощью регулярных выражений, но вам действительно нужен настоящий парсер CSV, чтобы делать это правильно. Несколько месяцев назад я нашел быстрый, отличный парсер C# CSV на CodeProject, который очень рекомендую!
Конечно, есть еще один в .NET framework. Несмотря на то, что он находится в Microsoft.VisualBasic, вы все равно можете использовать его из C#. msdn.microsoft.com/en-us/library/…
Спасибо! Как специалист по C#, я всегда забываю, что есть множество полезных библиотек VB, которые я могу использовать. Честно говоря, я думаю, что они плохо названы, поскольку на самом деле это не VB. Это просто .NET.
def parsecsv(instr):
i = 0
j = 0
outstrs = []
# i is fixed until a match occurs, then it advances
# up to j. j inches forward each time through:
while i < len(instr):
if j < len(instr) and instr[j] == '"':
# skip the opening quote...
j += 1
# then iterate until we find a closing quote.
while instr[j] != '"':
j += 1
if j == len(instr):
raise Exception("Unmatched double quote at end of input.")
if j == len(instr) or instr[j] == ',':
s = instr[i:j] # get the substring we've found
s = s.strip() # remove extra whitespace
# remove surrounding quotes if they're there
if len(s) > 2 and s[0] == '"' and s[-1] == '"':
s = s[1:-1]
# add it to the result
outstrs.append(s)
# skip over the comma, move i up (to where
# j will be at the end of the iteration)
i = j+1
j = j+1
return outstrs
def testcase(instr, expected):
outstr = parsecsv(instr)
print outstr
assert expected == outstr
# Doesn't handle things like '1, 2, "a, b, c" d, 2' or
# escaped quotes, but those can be added pretty easily.
testcase('a, b, "1, 2, 3", c', ['a', 'b', '1, 2, 3', 'c'])
testcase('a,b,"1, 2, 3" , c', ['a', 'b', '1, 2, 3', 'c'])
# odd number of quotes gives a "unmatched quote" exception
#testcase('a,b,"1, 2, 3" , "c', ['a', 'b', '1, 2, 3', 'c'])
Похоже, у вас здесь есть хорошие ответы.
Для тех из вас, кто хочет самостоятельно разобрать файл CSV, прислушайтесь к советам экспертов и Не катите свой собственный парсер CSV.
Ваша первая мысль: «Мне нужно обрабатывать запятые внутри кавычек».
Ваша следующая мысль будет: «Вот дерьмо, мне нужно обрабатывать кавычки внутри кавычек. Экранированные кавычки. Двойные кавычки. Одинарные кавычки ...»
Это дорога к безумию. Не пишите свои собственные. Найдите библиотеку с обширным охватом модульного тестирования, которая затрагивает все сложные моменты и прошла через ад за вас. Для .NET используйте бесплатную библиотеку FileHelpers.
отличная ссылка на secretgeek - очень забавно. но он, к сожалению, отвечает только на вопрос тех, кто использует .NET.
Истинный; хотя совет применим ко всем разработчикам: не используйте собственный анализатор CSV. Ruby имеет один встроенный модуль, и существуют библиотеки для Python, C++, большинства широко используемых языков.
И хотя SecretGeek, похоже, этого не знает, он тоже встроен в VB.NET. msdn.microsoft.com/en-us/library/…
Я просто не мог сопротивляться, чтобы посмотреть, смогу ли я заставить его работать в однострочном Python:
arr = [i.replace("|", ",") for i in re.sub('"([^"]*)\,([^"]*)"',"\g<1>|\g<2>", str_to_test).split(",")]
Возвращает ['a', 'строка, с', 'различными', 'значениями и некоторыми', 'цитируемыми']
Он работает, сначала заменяя ',' внутри кавычек на другой разделитель (|), разбивая строку на ',' и заменяя | разделитель снова.
Откуда вы знаете, что их нет | в исходной строке? А как насчет экранирования кавычек внутри строк в кавычках?
Python:
import csv
reader = csv.reader(open("some.csv"))
for row in reader:
print row
Считаю это лучшим ответом. Это именно то, что мне нужно!
What if an odd number of quotes appear in the original string?
Это странно похоже на синтаксический анализ CSV, который имеет некоторые особенности обработки полей в кавычках. Поле экранируется только в том случае, если поле разделено двойными кавычками, поэтому:
field1, "field2, field3", field4, "field5, field6" field7
становится
field1
field2, field3
field4
"field5
field6" field7
Обратите внимание, если оно не начинается и не заканчивается кавычкой, значит, это поле не в кавычках, а двойные кавычки просто обрабатываются как двойные кавычки.
Очевидно, мой код, на который кто-то ссылался, на самом деле не обрабатывает это правильно, если я правильно помню.
Вот простая реализация Python, основанная на псевдокоде Пэта:
def splitIgnoringSingleQuote(string, split_char, remove_quotes=False):
string_split = []
current_word = ""
inside_quote = False
for letter in string:
if letter == "'":
if not remove_quotes:
current_word += letter
if inside_quote:
inside_quote = False
else:
inside_quote = True
elif letter == split_char and not inside_quote:
string_split.append(current_word)
current_word = ""
else:
current_word += letter
string_split.append(current_word)
return string_split
Поскольку вы сказали агностик языка, я написал свой алгоритм на языке, который максимально приближен к псевдокоду:
def find_character_indices(s, ch):
return [i for i, ltr in enumerate(s) if ltr == ch]
def split_text_preserving_quotes(content, include_quotes=False):
quote_indices = find_character_indices(content, '"')
output = content[:quote_indices[0]].split()
for i in range(1, len(quote_indices)):
if i % 2 == 1: # end of quoted sequence
start = quote_indices[i - 1]
end = quote_indices[i] + 1
output.extend([content[start:end]])
else:
start = quote_indices[i - 1] + 1
end = quote_indices[i]
split_section = content[start:end].split()
output.extend(split_section)
output += content[quote_indices[-1] + 1:].split()
return output
Что, если в исходной строке появится нечетное количество кавычек?