Регулярное выражение для поиска неэкранированных двойных кавычек в файле CSV

Каким должно быть регулярное выражение для поиска наборов из двух двойных кавычек без экранирования, содержащихся в столбцах, выделенных двойными кавычками в файле CSV?

Не соответствует:

"asdf","asdf"
"", "asdf"
"asdf", ""
"adsf", "", "asdf"

Соответствие:

"asdf""asdf", "asdf"
"asdf", """asdf"""
"asdf", """"

person Even Mien    schedule 21.10.2009    source источник
comment
У меня в посте были отключены разрывы строк. Имеет ли это смысл сейчас?   -  person Even Mien    schedule 21.10.2009
comment
Да. Смотрите мое предложение.   -  person Bart Kiers    schedule 21.10.2009


Ответы (5)


Попробуй это:

(?m)""(?![ \t]*(,|$))

Объяснение:

(?m)       // enable multi-line matching (^ will act as the start of the line and $ will act as the end of the line (i))
""         // match two successive double quotes
(?!        // start negative look ahead
  [ \t]*   //   zero or more spaces or tabs
  (        //   open group 1
    ,      //     match a comma 
    |      //     OR
    $      //     the end of the line or string
  )        //   close group 1
)          // stop negative look ahead

Таким образом, на простом английском языке: "сопоставьте две последовательные двойные кавычки, только если перед ними НЕТ запятой или конца строки с необязательными пробелами и символами табуляции между ними".

(i) помимо обычных метасимволов начала строки и конца строки.

person Bart Kiers    schedule 21.10.2009
comment
Э-э, разве это не будет соответствовать `asdf,` (не должно совпадать), но не `asdfasdf, asdf` (должно совпадать)? - person Lucero; 21.10.2009
comment
@Lucero: нет, как раз наоборот. Он НЕ соответствует двойным кавычкам в "asdf", "" и ДЕЙСТВИТЕЛЬНО соответствует двойным кавычкам в "asdf""asdf", "asdf". - person Bart Kiers; 22.10.2009
comment
Как бы изменилось выражение, если бы мы нашли непоследовательные двойные кавычки, такие как asdf something asdf , asdf, ... - person stevenjmyu; 05.07.2010
comment
Это не работает в движке регулярных выражений NodeJS. Как объясняет NawaMan ниже, возможно, решение действительно во многом зависит от того, какой движок вы используете. - person giacecco; 06.02.2014

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

Мой ответ использует движок Ruby. Проверка — это всего лишь одно регулярное выражение, но я привожу здесь весь код, чтобы лучше его объяснить.

ЗАМЕТЬТЕ, что из-за механизма Ruby RegEx (или моих знаний) опциональный просмотр вперед/назад невозможен. Поэтому мне нужна небольшая проблема с пробелами до и после запятой.

Вот мой код:

orgTexts = [
    '"asdf","asdf"',
    '"", "asdf"',
    '"asdf", ""',
    '"adsf", "", "asdf"',
    '"asdf""asdf", "asdf"',
    '"asdf", """asdf"""',
    '"asdf", """"'
]

orgTexts.each{|orgText|
    # Preprocessing - Eliminate spaces before and after comma
    # Here is needed if you may have spaces before and after a valid comma
    orgText = orgText.gsub(Regexp.new('\" *, *\"'), '","')

    # Detect valid character (non-quote and valid quote)
    resText = orgText.gsub(Regexp.new('([^\"]|^\"|\"$|(?<=,)\"|\"(?=,)|(?<=\\\\)\")'), '-')
    # resText = orgText.gsub(Regexp.new('([^\"]|(^|(?<=,)|(?<=\\\\))\"|\"($|(?=,)))'), '-')
    # [^\"]       ===> A non qoute
    # |           ===> or
    # ^\"         ===> beginning quot
    # |           ===> or
    # \"$         ===> endding quot
    # |           ===> or
    # (?<=,)\"    ===> quot just after comma
    # \"(?=,)     ===> quot just before comma
    # (?<=\\\\)\" ===> escaped quot

    #  This part is to show the invalid non-escaped quots
    print orgText
    print resText.gsub(Regexp.new('"'), '^')

    # This part is to determine if there is non-escaped quotes
    # Here is the actual matching, use this one if you don't want to know which quote is un-escaped
    isMatch = ((orgText =~ /^([^\"]|^\"|\"$|(?<=,)\"|\"(?=,)|(?<=\\\\)\")*$/) != 0).to_s
    # Basicall, it match it from start to end (^...$) there is only a valid character

    print orgText + ": " + isMatch
    print 
    print ""
    print ""
} 

При выполнении код печатает:

"asdf","asdf"
-------------
"asdf","asdf": false


"","asdf"
---------
"","asdf": false


"asdf",""
---------
"asdf","": false


"adsf","","asdf"
----------------
"adsf","","asdf": false


"asdf""asdf","asdf"
-----^^------------
"asdf""asdf","asdf": true


"asdf","""asdf"""
--------^^----^^-
"asdf","""asdf""": true


"asdf",""""
--------^^-
"asdf","""": true

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

person NawaMan    schedule 21.10.2009

".*"(\n|(".*",)*)

должно работать, я думаю...

person aviraldg    schedule 21.10.2009
comment
Обратите внимание, что .* является жадным и может съесть любое количество символов, включая некоторое количество ", так что, например, "adsf", "", "asdf" также будет совпадением. (И производительность может быть довольно плохой, потому что она во многом совпадает даже в случае несоответствия.) - person Lucero; 21.10.2009

Для однострочных совпадений:

^("[^"]*"\s*,\s*)*"[^"]*""[^"]*"

или для многострочного:

(^|\r\n)("[^\r\n"]*"\s*,\s*)*"[^\r\n"]*""[^\r\n"]*"

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

person Lucero    schedule 21.10.2009
comment
Привет @Lucero, если вам было полезно знать, что это не работает в движке регулярных выражений NodeJS: › var x = \field 1\, \field 2 с \something\ в нем\,\field 3\; undefined › x.match(/^([^]*\s*,\s*)*[^]*[^]*/) null - person giacecco; 06.02.2014
comment
@giacecco, здесь задан вопрос о поиске двух последовательных двойных кавычек ("") в строках в кавычках, чего нет в вашей тестовой строке. - person Lucero; 06.02.2014
comment
вы правы, и я сожалею. Меня смутил тот факт, что, согласно CVS RFC, двойные двойные кавычки на самом деле являются действительными CSV: это способ экранирования двойных кавычек tools.ietf.org/html/rfc4180#section-2 . Похожая проблема привела меня сюда, но не оригинальную, которая была у Эвена Миена. - person giacecco; 06.02.2014

Попробуйте это регулярное выражение:

"(?:[^",\\]*|\\.)*(?:""(?:[^",\\]*|\\.)*)+"

Это будет соответствовать любой строке в кавычках, по крайней мере, с одной парой неэкранированных двойных кавычек.

person Gumbo    schedule 21.10.2009