Подтвердить что ты не робот

Регулярное выражение Python для чтения CSV-подобных строк

Я хочу анализировать входящие CSV-подобные строки данных. Значения разделяются запятыми (и между запятыми могут быть ведущие и конечные пробелы) и могут быть указаны либо с помощью "или с". Например, это допустимая строка:

    data1, data2  ,"data3'''",  'data4""',,,data5,

но это неверно:

    data1, data2, da"ta3", 'data4',

- кавычки могут быть добавлены или перемещены пробелами.

Такие искаженные строки должны быть распознаны - лучше всего было бы каким-то образом отметить неверное значение в строке, но если регулярное выражение не соответствует всей строке, оно также приемлемо.

Я пытаюсь написать регулярное выражение, способное анализировать это, используя либо match() для findall(), но каждое отдельное регулярное выражение, с которым я иду, имеет некоторые проблемы с случаями ребер.

Итак, может быть, кто-то, у кого есть опыт в анализе чего-то подобного, может помочь мне в этом? (Или, может быть, это слишком сложно для регулярного выражения, и я должен просто написать функцию)

EDIT1:

csv модуль здесь не очень полезен:

    >>> list(csv.reader(StringIO('''2, "dat,a1", 'dat,a2',''')))
    [['2', ' "dat', 'a1"', " 'dat", "a2'", '']]

    >>> list(csv.reader(StringIO('''2,"dat,a1",'dat,a2',''')))
    [['2', 'dat,a1', "'dat", "a2'", '']]

- если это не может быть настроено?

EDIT2: несколько изменений языка - я надеюсь, что он станет более актуальным на английском языке

EDIT3: Спасибо за все ответы, теперь я уверен, что регулярное выражение здесь не так хорошо, как (1), охватывающее все случаи краев, может быть сложным (2) вывод записи не является регулярным. Написав это, я решил проверить упомянутый pyparsing и либо использовать его, либо написать собственный FS-подобный парсер.

4b9b3361

Ответ 1

Несмотря на то, что это возможно с некоторой комбинацией предварительной обработки, использования модуля csv, последующей обработки и использования регулярных выражений, ваши заявленные требования не соответствуют дизайну модуля csv, а также, возможно, с регулярными выражениями (в зависимости от сложности вложенных кавычек, которые вам, возможно, придется обрабатывать).

В сложных случаях синтаксического анализа pyparsing всегда является хорошим пакетом для возврата. Если это не одноразовая ситуация, она, скорее всего, принесет самый простой и удобный результат за счет, возможно, немного дополнительных усилий. Подумайте, что инвестиции должны быть возвращены быстро, однако, поскольку вы сбережете себе дополнительные усилия по отладке решений regex для обработки угловых случаев...

Вероятно, вы можете легко найти примеры синтаксического анализа CSV на основе pyparsing, этот вопрос, возможно, достаточно, чтобы вы начали.

Ответ 2

В то время как модуль csv является правильным ответом здесь, регулярное выражение, которое может сделать это, вполне выполнимо:

import re

r = re.compile(r'''
    \s*                # Any whitespace.
    (                  # Start capturing here.
      [^,"']+?         # Either a series of non-comma non-quote characters.
      |                # OR
      "(?:             # A double-quote followed by a string of characters...
          [^"\\]|\\.   # That are either non-quotes or escaped...
       )*              # ...repeated any number of times.
      "                # Followed by a closing double-quote.
      |                # OR
      '(?:[^'\\]|\\.)*'# Same as above, for single quotes.
    )                  # Done capturing.
    \s*                # Allow arbitrary space before the comma.
    (?:,|$)            # Followed by a comma or the end of a string.
    ''', re.VERBOSE)

line = r"""data1, data2  ,"data3'''",  'data4""',,,data5,"""

print r.findall(line)

# That prints: ['data1', 'data2', '"data3\'\'\'"', '\'data4""\'', 'data5']

EDIT: Чтобы проверить правильность строк, вы можете повторно использовать регулярное выражение выше с небольшими дополнениями:

import re

r_validation = re.compile(r'''
    ^(?:    # Capture from the start.
      # Below is the same regex as above, but condensed.
      # One tiny modification is that it allows empty values
      # The first plus is replaced by an asterisk.
      \s*([^,"']*?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')\s*(?:,|$)
    )*$    # And don't stop until the end.
    ''', re.VERBOSE)

line1 = r"""data1, data2  ,"data3'''",  'data4""',,,data5,"""
line2 = r"""data1, data2, da"ta3", 'data4',"""

if r_validation.match(line1):
    print 'Line 1 is valid.'
else:
    print 'Line 1 is INvalid.'

if r_validation.match(line2):
    print 'Line 2 is valid.'
else:
    print 'Line 2 is INvalid.'

# Prints:
#    Line 1 is valid.
#    Line 2 is INvalid.

Ответ 3

Python имеет стандартный библиотечный модуль для чтения файлов csv:

import csv

reader = csv.reader(open('file.csv'))

for line in reader:
    print line

В качестве примера введите эти отпечатки

['data1', ' data2 ', "data3'''", ' \'data4""\'', '', '', 'data5', '']

EDIT:

вам нужно добавить skipinitalspace = True, чтобы разрешить пробелы перед двойными кавычками для дополнительных примеров, которые вы предоставили. Пока еще не уверены в одиночных кавычках.

>>> list(csv.reader(StringIO('''2, "dat,a1", 'dat,a2','''), skipinitialspace=True))
[['2', 'dat,a1', "'dat", "a2'", '']]

>>> list(csv.reader(StringIO('''2,"dat,a1",'dat,a2','''), skipinitialspace=True))
[['2', 'dat,a1', "'dat", "a2'", '']]

Ответ 4

Невозможно дать вам ответ, потому что вы не полностью указали протокол, который используется автором.

Он, очевидно, содержит такие правила, как:

Если поле содержит какие-либо запятые или одинарные кавычки, процитируйте их двойными кавычками.
Иначе, если поле содержит любые двойные кавычки, укажите его одинарными кавычками.
Примечание: результат остается в силе, если вы поменяете swap double и single в приведенных выше предложениях.
Просто не цитируйте это. Полученное поле может содержать или добавлять пробелы (или другие пробелы?).
Дополненные поля собираются в строку, разделяются запятыми и заканчиваются платформой newline (LF или CRLF).

Что не упоминается, что писатель делает в этих случаях:
 (0) содержит BOTH одинарные кавычки и двойные кавычки
 (1) содержит ведущие пробелы без новой строки
 (2) поле содержит завершающие пробелы без символа новой строки
 (3) содержит любую новую строку. Если автор игнорирует любой из этих случаев, укажите, какие результаты вы хотите.

Вы также отмечаете, что "кавычки могут быть добавлены или перемещены пробелами" - конечно, вы имеете в виду, что также разрешены запятые, в противном случае ваш пример 'data4""',,,data5, завершается с ошибкой в ​​первой запятой.

Как кодируются ваши данные?

Ответ 5

Это, вероятно, звучит слишком просто, но на самом деле из взглядов вещей вы ищете строку, содержащую либо [a-zA-Z0-9] ["'] + [a-zA-Z0-9], я означает, что без углубленного тестирования против данных действительно то, что вы ищете, это цитата или двойная кавычка (или любая комбинация) между буквами (вы также можете добавить туда номера).

Основываясь на том, что вы спрашивали, действительно не имеет значения, что это CSV, важно, чтобы у вас были данные, которые не соответствуют. Который, я считаю, просто выполняет поиск буквы, затем любую комбинацию одной или нескольких "или" и другой буквы.

Теперь вы хотите получить "количество" или просто распечатку строки, которая содержит его, чтобы вы знали, какие из них нужно возвращать и исправлять?

Извините, я не знаю python regex, но в perl это выглядело бы примерно так:

# Look for one or more letter/number at least one ' or " or more and at least one    
#  or more letter/number
if ($line =~ m/[a-zA-Z0-9]+['"]+[a-zA-Z0-9]+/ig)
{
    # Prints the line if the above regex is found
    print $line;

}

Просто преобразуйте это, когда вы посмотрите на строку.

Извините, если я неправильно понял вопрос

Надеюсь, это поможет!

Ответ 6

Если ваша цель - преобразовать данные в XML (или JSON или YAML), посмотрите этот пример для Gelatin, который производит следующий вывод:

<xml>
  <line>
    <column>data1</column>
    <column>data2  </column>
    <column>data3'''</column>
    <column>data4""</column>
    <column/>
    <column/>
    <column>data5</column>
    <column/>
  </line>
</xml>

Обратите внимание, что Gelatin также имеет API Python:

from Gelatin.util import compile, generate_to_file
syntax = compile('syntax.gel')
generate_to_file(syntax, 'input.csv', 'output.xml', 'xml')