首页 > 解决方案 > AWK FPAT 无法按预期进行字符串解析

问题描述

我必须解析一个非常长的字符串(来自标准输入)。它基本上是一个 .sql 文件。我必须从中获取数据。我正在努力解析数据,以便可以将其转换为 csv。为此,我正在使用 awk。就我而言,一个示例片段(两条记录)如下:

b="(abc@xyz.com,www.example.com,'field2,(2)'),(dfr@xyz.com,www.example.com,'field0'),"
echo $b|awk 'BEGIN {FPAT = "([^\\)]+)|('\''[^'\'']+'\'')"}{print $1}'

在我的正则表达式中,我说的是“)”括号上的拆分,或者如果找到单引号,则忽略所有文本,直到找到最后一个引号。但我的输出如下:

(abc@xyz.com,www.example.com,'field2,(2

我期待这个输出

(abc@xyz.com,www.example.com,'field2,(2)'

我的代码中的问题在哪里。我搜索了很多并检查了 awk 手册,但没有成功。

标签: sqlregexbashawkstring-parsing

解决方案


我在下面的第一个答案是错误的,您正在尝试做的事情有一个 ERE:

$ echo "$b" | awk -v FPAT="[(]([^)]|'[^']*')*)" '{for (i=1; i<=NF; i++) print $i}'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')

原始答案,另一种方法:

您需要一种 2-pass 方法,首先将)引用字段中的所有 s 替换为输入中尚不存在的内容(例如 RS),然后识别(...)字段并将 RS 放回)s 之前打印它们:

$ echo "$b" |
awk -F"'" -v OFS= '
    {
        for (i=2; i<=NF; i+=2) {
            gsub(/)/,RS,$i)
            $i = FS $i FS
        }
        FPAT = "[(][^)]*)"
        $0 = $0
        for (i=1; i<=NF; i++) {
            gsub(RS,")",$i)
            print $i
        }
        FS = FS
    }
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')

由于 FPAT(或者我们可以使用 gawk patsplit()),上述内容仅适用于 gawk,而其他 awk 则使用了 while-match()-substr() 循环:

$ echo "$b" |
awk -F"'" -v OFS= '
    {
        for (i=2; i<=NF; i+=2) {
            gsub(/)/,RS,$i)
            $i = FS $i FS
        }
        while ( match($0,/[(][^)]*)/) ) {
            field = substr($0,RSTART,RLENGTH)
            gsub(RS,")",field)
            print field
            $0 = substr($0,RSTART+RLENGTH)
        }
    }
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')

推荐阅读