用Perl的正则表达式分析csv文件

问题来自这个帖子。我没理解错的话,楼主的要求是替换分隔符。

这里假设 csv 的规范是逗号分隔字段,允许字段中有双引号引用的字符串,如果串内要表示双引号,那么用两个双引号代替。

例如:

sdf,sdaf"asd""""""asd","asd"

对应的输出是:

sdf<$>sdafasd"""asd<$>asd

这个问题,似乎一行 Perl 就能解决:

perl -pe 'BEGIN{$sep=q{<$>}}$s="";/(?:([^",]+)(?{$s.=$1})|"(?:([^"])(?{$s.=$2})|""(?{$s.=q{"}}))*"|,(?{$s.=$sep}))+/;$_=$s;'

输入分成三类:

  • 非双引号非逗号即 [^”,]+,这类直接输出,这里用了 Perl 正则内嵌代码的功能,(?{$s.=$1}) 即把 $1 追加到 $s。
    这一部分对应到正则表达式中的

    (?:([^",]+)(?{$s.=$1})
    

    由一对 non-capturing parentheses(?:) 包起来,里面先是模式,然后跟着的是 Perl 正则表达式内嵌代码即 (?{}),用来把匹配的串追加到 $2 末尾。

  • 逗号,这类输出分隔符
    对应

    ,(?{$s.=$sep})
    
  • 双引号内的文本,碰到开双引号后,碰到非双引号字符则直接输出,否则用两个双引号转义,碰到单个双引号则结束。这类用 "(([^"])|"")*" 表示。
    对应

    "(?:([^"])(?{$s.=$2})|""(?{$s.=q{"}}))*"`