Regex, макет csv-типа, допускает "" внутри строк в кавычках? - PullRequest
1 голос
/ 22 июня 2009

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

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

Кто-нибудь? (Не то чтобы это важно, но конкретный язык - Matlab.)

Ответы [ 7 ]

4 голосов
/ 22 июня 2009

В настоящее время я знаю, что у меня много шумихи вокруг регулярных выражений, но я бы действительно рекомендовал использовать библиотеку для задач, которые уже были реализованы другими - ее будет проще реализовать, легче читать и легче поддерживать (хочу читать csvs, разделенные кавычками в следующий раз? Библиотека, возможно, сделает это, но вашему регулярному выражению потребуется переписать). Быстрый поиск в Google должен дать вам хорошее начало.

1 голос
/ 22 июня 2009

Если вам действительно нужно сделать это с помощью регулярного выражения, я бы сделал это за два прохода; сначала разделите поля, разделив запятые чем-то вроде:

regexp(theString, '(?<!\\),', 'split');

Это должно делиться на запятые, только если нет предшествующей косой черты (я предполагаю, что вы имеете в виду экранированные запятые) (Я думаю, что в Matlab вы получите массив индексов для исходных строк)

Затем вы должны проверить каждое совпавшее поле на наличие экранированных кавычек и заменить их чем-то вроде:

regexprep(individualString, '""', '"');

Аналогично для запятых:

regexprep(individualString, '\\,', ',');

Я не уверен, что у дважды сбежавших в matlab не было большого опыта с этим.

Как уже говорили другие, вероятно, лучше использовать библиотеку csv для обработки исходного файла.

1 голос
/ 22 июня 2009

экранировать кавычки -? делает это необязательным.

\"?
0 голосов
/ 23 сентября 2010

Это можно сделать с помощью одного регулярного выражения с lookahead. Иллюстрировано здесь в Perl:

my @rows;

foreach my $line (@lines) {

    my @cells;
    while ($line =~ /( ("|').+?\2 | [^,]+? ) (?=(,|$))/gx) {
        push @cells, $1;
    }

    push @rows, \@cells;
}
0 голосов
/ 23 июня 2009

Страница 271 из Friedl's Мастеринг регулярных выражений имеет регулярное выражение для извлечения возможно заключенных в кавычки полей CSV, но требует немного постобработки:

>>> re.findall('(?:^|,)(?:"((?:[^"]|"")*)"|([^",]*))', '"a,b,c",d,e,f')
[('a,b,c', ''), ('', 'd'), ('', 'e'), ('', 'f')]
>>> re.findall('(?:^|,)(?:"((?:[^"]|"")*)"|([^",]*))', '"a,b,c",d,,f')
[('a,b,c', ''), ('', 'd'), ('', ''), ('', 'f')]

Тот же шаблон с подробным флагом:

csv = re.compile(r"""
    (?:^|,)
    (?: # now match either a double-quoted field
        # (inside, paired double quotes are allowed)...
        " # (double-quoted field's opening quote)
          (    (?: [^"] | "" )*    )
        " # (double-quoted field's closing quote)
    |
      # ...or some non-quote/non-comma text...
        ( [^",]* )
    )""", re.X)
0 голосов
/ 22 июня 2009

Спасибо за ответы. Классический случай, когда новичок думает, что проблема проста, а эксперты знают, что проблема трудна.

После прочтения ваших постов я просмотрел библиотеку консервированных csv-парсеров в Matlab; нашел пару, ни одна из которых не могла выполнить работу (сначала попытался сделать весь файл за один раз, произошел сбой в памяти; во-вторых, не удалось выполнить мой конкретный bugaboo, двойные кавычки в строке в кавычках).

Итак, мы свернули свои собственные, с помощью регулярного выражения, которое я нашел в сети и изменил. Остается перенести в Matlab, но код Python выглядит следующим образом:

import re

text = ["<omitted>"]

# Regex: empty before comma OR string w/ no quote or comma OR quote-surrounded string w/ optional doubles
p = re.compile('(?=,)|[^",]+|"(?:[^"]|"")*"')

for line in text:
    print 'Line: %s' % line
    m = p.search(line)                                  
    fld = 1
    while m:                                            
        val = m.group().strip('"').replace('""', '"')   
        print 'Field %d: %s' % (fld, val)
        line = re.sub(p, '', line, 1)        
        if line and line[0] == ',':          
            line = line[1:]
        fld += 1
        m = p.search(line)                   
    print
0 голосов
/ 22 июня 2009

Мне потребовалось некоторое время, чтобы разобраться с этим, поскольку многие регулярные выражения в сети не обрабатывают ту или иную часть. Вот код в F # /. NET. Извините, но я не говорю на матлабе:

let splitCsv (s:string) =
    let re = new Regex("\\s*((?:\"(?:(?:\"\")|[^\"])*\")|[^\"]*?)\\s*(?:,|$)")

    re.Matches( s + " ")
    |> Seq.cast<Match>
    |> Seq.map (fun m -> m.Groups.[1].Value)
    |> Seq.map (fun s -> s.Replace( "\"\"", "\"" ))
    |> Seq.map (fun s -> s.Trim( [| '"'; ' ' |] ))
    |> List.of_seq

Эта версия обрабатывает строки в кавычках, кавычки, экранированные как двойные кавычки, и обрезают дополнительные (экранированные) кавычки и пробелы вокруг всей строки (оригинал: "Test", двойные кавычки: "" "Test" ""). Он также правильно обрабатывает пустое поле в последней позиции (отсюда s + "") и правильно обрабатывает запятые внутри строк в кавычках.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...