Как разделить строку с разделителями-запятыми в Python, за исключением запятых, находящихся в кавычках

Я пытаюсь разделить строку с разделителями-запятыми в python. Трудная часть для меня здесь заключается в том, что некоторые из полей в самих данных содержат в себе запятую, и они заключены в кавычки ( " или ' ). Полученная в результате разделительная строка также должна содержать кавычки вокруг удаленных полей. поля могут быть пустыми.

Пример:

 hey,hello,,"hello,world",'hey,world' 

необходимо разделить на 5 частей, как показано ниже

 ['hey', 'hello', '', 'hello,world', 'hey,world'] 

Любые идеи / мысли / предложения / помощь в том, как решить эту проблему в Python, будут высоко оценены.

Благодарю вас, Виш

  • Регулярное выражение Python для целого?
  • Python Regex работает не так, как ожидалось
  • pandas применяют regex для замены значений
  • Обратное слово в Vim
  • Используйте Python для извлечения длин ветвей из Newick Format
  • Разделение разделенных запятыми строк в python
  • Проблема с regexp python и sqlite
  • Как обрабатывать кодировку ответа из urllib.request.urlopen ()
  • 4 Solutions collect form web for “Как разделить строку с разделителями-запятыми в Python, за исключением запятых, находящихся в кавычках”

    (Изменить: исходный ответ имел проблемы с пустыми полями на краях из-за того, как работает re.findall , поэтому я немного re.findall его и добавил тесты.)

     import re def parse_fields(text): r""" >>> list(parse_fields('hey,hello,,"hello,world",\'hey,world\'')) ['hey', 'hello', '', 'hello,world', 'hey,world'] >>> list(parse_fields('hey,hello,,"hello,world",\'hey,world\',')) ['hey', 'hello', '', 'hello,world', 'hey,world', ''] >>> list(parse_fields(',hey,hello,,"hello,world",\'hey,world\',')) ['', 'hey', 'hello', '', 'hello,world', 'hey,world', ''] >>> list(parse_fields('')) [''] >>> list(parse_fields(',')) ['', ''] >>> list(parse_fields('testing,quotes not at "the" beginning \'of\' the,string')) ['testing', 'quotes not at "the" beginning \'of\' the', 'string'] >>> list(parse_fields('testing,"unterminated quotes')) ['testing', '"unterminated quotes'] """ pos = 0 exp = re.compile(r"""(['"]?)(.*?)\1(,|$)""") while True: m = exp.search(text, pos) result = m.group(2) separator = m.group(3) yield result if not separator: break pos = m.end(0) if __name__ == "__main__": import doctest doctest.testmod() 

    (['"]?) соответствует необязательной одно- или двухкатегории.

    (.*?) соответствует самой строке. Это не жадный матч, чтобы соответствовать столько, сколько необходимо, не съедая целую цепочку. Это присваивается result , и именно это мы и получаем в результате.

    \1 – это обратная ссылка, чтобы соответствовать одной и той же одно- или двойной кавычке, которую мы сопоставляли ранее (если она есть).

    (,|$) соответствует запятой, разделяющей каждую запись или конец строки. Это назначается separator .

    Если разделитель является ложным (например, пустым), это означает, что нет разделителя, поэтому мы находимся в конце строки – мы сделали. В противном случае мы обновляем новую начальную позицию на основе того, где закончилось регулярное выражение ( m.end(0) ), и продолжаем цикл.

    Похоже, вам нужен CSV- модуль.

    Модуль csv не будет обрабатывать сценарий «и», являясь котировками одновременно. Отсутствие модуля, который обеспечивает такой диалект, нужно получить в бизнесе разбора. Чтобы избежать зависимости от стороннего модуля, мы можем используйте модуль re для выполнения лексического анализа, используя трюк re.MatchObject.lastindex, чтобы связать тип токена с совпадающим шаблоном.

    Следующий код при запуске в качестве скрипта передает все показанные тесты с Python 2.7 и 2.2.

     import re # lexical token symbols DQUOTED, SQUOTED, UNQUOTED, COMMA, NEWLINE = xrange(5) _pattern_tuples = ( (r'"[^"]*"', DQUOTED), (r"'[^']*'", SQUOTED), (r",", COMMA), (r"$", NEWLINE), # matches end of string OR \n just before end of string (r"[^,\n]+", UNQUOTED), # order in the above list is important ) _matcher = re.compile( '(' + ')|('.join([i[0] for i in _pattern_tuples]) + ')', ).match _toktype = [None] + [i[1] for i in _pattern_tuples] # need dummy at start because re.MatchObject.lastindex counts from 1 def csv_split(text): """Split a csv string into a list of fields. Fields may be quoted with " or ' or be unquoted. An unquoted string can contain both a " and a ', provided neither is at the start of the string. A trailing \n will be ignored if present. """ fields = [] pos = 0 want_field = True while 1: m = _matcher(text, pos) if not m: raise ValueError("Problem at offset %d in %r" % (pos, text)) ttype = _toktype[m.lastindex] if want_field: if ttype in (DQUOTED, SQUOTED): fields.append(m.group(0)[1:-1]) want_field = False elif ttype == UNQUOTED: fields.append(m.group(0)) want_field = False elif ttype == COMMA: fields.append("") else: assert ttype == NEWLINE fields.append("") break else: if ttype == COMMA: want_field = True elif ttype == NEWLINE: break else: print "*** Error dump ***", ttype, repr(m.group(0)), fields raise ValueError("Missing comma at offset %d in %r" % (pos, text)) pos = m.end(0) return fields if __name__ == "__main__": tests = ( ("""hey,hello,,"hello,world",'hey,world'\n""", ['hey', 'hello', '', 'hello,world', 'hey,world']), ("""\n""", ['']), ("""""", ['']), ("""a,b\n""", ['a', 'b']), ("""a,b""", ['a', 'b']), (""",,,\n""", ['', '', '', '']), ("""a,contains both " and ',c""", ['a', 'contains both " and \'', 'c']), ("""a,'"starts with "...',c""", ['a', '"starts with "...', 'c']), ) for text, expected in tests: result = csv_split(text) print print repr(text) print repr(result) print repr(expected) print result == expected 

    Я сфабриковал что-то вроде этого. Я полагаю, слишком избыточно, но это делает для меня работу. Вы должны немного адаптировать его к своим характеристикам:

     def csv_splitter(line): splitthese = [0] splitted = [] splitpos = True for nr, i in enumerate(line): if i == "\"" and splitpos == True: splitpos = False elif i == "\"" and splitpos == False: splitpos = True if i == "," and splitpos == True: splitthese.append(nr) splitthese.append(len(line)+1) for i in range(len(splitthese)-1): splitted.append(re.sub("^,|\"","",line[splitthese[i]:splitthese[i+1]])) return splitted 
    Python - лучший язык программирования в мире.