用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{"}}))*"`