正则表达式,csv类型布局,允许内部引用字符串?

时间:2009-06-22 13:33:12

标签: regex matlab

我需要一个解析csv样式文件的正则表达式,类似于57个字段宽,大多数字段用引号括起来(但可能不是全部),用逗号分隔,带引号的字段有可能嵌入双精度{{1代表计算字符串中的单引号。

我是一个正则表达式的初学者/中级,我认为我可以很快得到基本表达式来进行字段解析,但它是嵌入式双引号(和逗号)我无法理解。

任何? (并不重要,但具体的语言是Matlab。)

7 个答案:

答案 0 :(得分:4)

我知道现在我对正则表达式的大肆宣传,但我真的建议将库用于已经由其他人实现的任务 - 它将更容易实现,更易于阅读和更易于维护(想要阅读csvs下次用引号分隔?库可能会这样做,但你的正则表达式需要重写)。快速google search应该会给你一个良好的开端。

答案 1 :(得分:1)

逃避报价 - ?使它成为可选的。

\"?

答案 2 :(得分:1)

如果你真的必须使用正则表达式,我会在两个通道中完成;首先通过用逗号分隔逗号来分隔字段:

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

这应该用逗号分隔,只有当没有前面的斜杠时(我假设这是你所说的转义逗号)。 (我认为在matlab中你最终会得到一个原始字符串的索引数组)

然后你应该检查每个匹配的字段以获取转义引号,并用以下内容替换它们:

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

同样的逗号:

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

我不确定matlab中没有多少经验的双重逃脱。

正如其他人所说,使用csv库处理初始文件可能更好。

答案 3 :(得分:0)

我花了一段时间来解决这个问题,因为网上的许多正则表达式都不能处理这部分或另一部分。这是F#/ .NET中的代码。对不起,但我不会说matlab:

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 +“”),并且还可以正确处理引用字符串中的逗号。

答案 4 :(得分:0)

感谢您的回复。经典案例的初学者认为问题很容易,专家知道问题很难。

阅读完帖子后,我在Matlab中浏览了一个罐装csv解析器库;发现了一对,其中任何一个都无法完成工作(首先尝试一次完成整个文件,内存失败;第二个未能通过我的特定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

答案 5 :(得分:0)

Friedl的掌握正则表达式

Page 271有一个正则表达式,用于提取可能引用的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)

答案 6 :(得分:0)

可以使用单个正则表达式进行前瞻。这里以perl为例进行说明:

my @rows;

foreach my $line (@lines) {

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

    push @rows, \@cells;
}