Разделить строку, игнорирующую цитируемые разделы
Для такой строки:
a, "string, with", different, "values and some", quoted
Что такое хороший алгоритм для разделения этого на основе запятых, игнорируя запятые внутри цитируемых разделов?
Выходом должен быть массив:
[ "a", "string, with", "различные", "values" и некоторые "," quoted "]
Ответы
Ответ 1
Если бы мой язык выбора не предлагал способ сделать это, не задумываясь, я бы сначала рассмотрел два варианта в качестве простого выхода:
-
Предварительно проанализируйте и замените запятые внутри строки другим символом управления, а затем разделите их, а затем постобразуйте в массиве, чтобы заменить управляющий символ, используемый ранее запятыми.
-
Альтернативно разделять их на запятые, а затем анализировать полученный массив на другой массив, проверяя ведущие кавычки на каждой записи массива и конкатенацию записей до тех пор, пока я не достигнет завершающей цитаты.
Однако это хаки, и если это чистое "умственное" упражнение, то я подозреваю, что они окажутся бесполезными. Если это проблема реального мира, то это поможет узнать язык, чтобы мы могли предложить некоторые конкретные советы.
Ответ 2
Похоже, у вас есть хорошие ответы здесь.
Для тех из вас, кто хочет обработать ваш собственный синтаксический анализ файлов CSV, прислушайтесь к советам экспертов и Не сворачивайте собственный анализатор CSV.
Ваша первая мысль: "Мне нужно обрабатывать запятые внутри кавычек".
Ваша следующая мысль будет: "О, дерьмо, мне нужно обрабатывать кавычки внутри цитат. Сбежавшие цитаты. Двойные кавычки. Одиночные кавычки..."
Это дорога к безумию. Не пишите свои собственные. Найдите библиотеку с обширным охватом unit test, который поражает все тяжелые части и прошел через ад для вас. Для .NET используйте бесплатную библиотеку FileHelpers.
Ответ 3
Python:
import csv
reader = csv.reader(open("some.csv"))
for row in reader:
print row
Ответ 4
Конечно, использование парсера 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
Ответ 5
Автор здесь упал в блоке кода С#, который обрабатывает сценарий, с которым у вас возникла проблема:
Импорт файлов CSV в .Net
Не следует переводить слишком сложно.
Ответ 6
Что делать, если появляется нечетное число кавычек в исходной строке?
Это выглядит странно, как разбор CSV, который имеет некоторые особенности для обработки цитируемых полей. Поле только экранируется, если поле делится на двойные кавычки, поэтому:
field1, "field2, field3", field4, "field5, field6" field7
становится
поле1
поле2, поле3
Field4
"Field5
поле6" поле7
Обратите внимание, если он не начинается и не заканчивается цитатой, то это не кавычки, а двойные кавычки просто рассматриваются как двойные кавычки.
В самом деле, мой код, с которым связан кто-то, на самом деле не справляется с этим правильно, если я правильно помню.
Ответ 7
Здесь простая реализация python на основе псевдокода Pat:
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
Ответ 8
Я использую это для синтаксического анализа строк, не уверен, помогает ли это здесь; но с некоторыми незначительными изменениями, возможно?
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)
/тр
Ответ 9
Здесь простой алгоритм:
- Определите, начинается ли строка с символа
'"'
- Разделите строку на массив, ограниченный символом
'"'
.
- Отметьте цитируемые запятые с помощью заполнителя
#COMMA#
- Если ввод начинается с
'"'
, отметьте те элементы в массиве, где индекс% 2 == 0
- В противном случае отметьте те элементы в массиве, где индекс% 2 == 1
- Объединить элементы в массиве, чтобы сформировать измененную строку ввода.
- Разделите строку на массив, ограниченный символом
','
.
- Замените все экземпляры в массиве
#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']
Ответ 10
Это стандартный синтаксический анализ в формате CSV. Многие люди пытаются сделать это с помощью регулярных выражений. Вы можете получить около 90% с регулярными выражениями, но вам действительно нужен настоящий синтаксический анализатор CSV, чтобы сделать это правильно. Несколько месяцев назад я нашел быстрый, отличный С# CSV парсер на CodeProject, который я очень рекомендую!
Ответ 11
Здесь один в псевдокоде (a.k.a. Python) за один проход: -P
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'])
Ответ 12
Я просто не мог удержаться, чтобы увидеть, могу ли я заставить его работать в однострочном Python:
arr = [i.replace("|", ",") for i in re.sub('"([^"]*)\,([^"]*)"',"\g<1>|\g<2>", str_to_test).split(",")]
Возвращает ['a', 'string, with', 'different', 'values, and some', 'quoted']
Он работает, сначала заменяя ',' внутри кавычек на другой разделитель (|),
разбивая строку на ',' и заменяя | разделитель снова.