awk使用正则表达式前后顾环匹配作为字段分隔符

6
我想使用转义序列来分割行,但失败了。例如:
$ echo "1,2\,2,333"|awk -F "(?<\!\\,)," '{print $2}'   ## expecting "2\,2"
awk: warning: escape sequence `\!' treated as plain `!'
awk: warning: escape sequence `\,' treated as plain `,'

awk/gawk是否支持具有正则表达式前瞻或后顾的字段分隔符?

6
在 awk/gawk 的正则表达式中没有“向后查找”的功能。如果确实需要这些功能,请使用 Perl,或者重新考虑你的方法。 - nhahtdh
1
通过切换到制表符或管道分隔的数据,您可以消除*nix处理中的整个问题类别。仍然明智的做法是检查每个记录,以确保NF!=expectdColCount {print "#ERR : NF mismatch " $0}'。祝你好运。 - shellter
2个回答

6
正如我在评论中所说,awk不支持前瞻或后顾,因为它使用POSIX扩展正则表达式(ERE)。如果你真的需要前瞻或后顾,你可能想使用Perl。然而,在这种情况下,你可以略微改变你的方法来解决问题。
如果你的数据包含分隔符,那么最好直接匹配字段,而不是通过查找未转义的分隔符来拆分数据(当有很多\时可能会失败)。
匹配字段的正则表达式是 /([^\\,]|\\.)+/。请注意,此正则表达式不知道带引号的字段。如果你想支持它们,那就取决于你如何处理引号没有正确关闭的情况,或者一个字段中有多个引号的情况。如果你可以假设你的数据格式良好,那么你可以想出适用于你的数据的正则表达式。
以下是一些让你入门的内容。下面的代码打印一行中的所有字段。
echo "1,2\,2,333" | awk '{while (match($0, /([^\\,]|\\.)+/)) {print substr($0, RSTART, RLENGTH);$0=substr($0, RSTART+RLENGTH)}}'

参考资料


在包含print的括号前面加上/([^\,] | \。)+/不会产生相同的效果吗? - Mordechai

3

处理这个问题的一种方法是在gnu-awk中使用FPAT(按内容分割)

awk 'BEGIN{ FPAT=",([^\\\\]*\\\\,)*[^,]*,|[^,]+" } {
  for (i=1; i<=NF; i++) {gsub(/^,|,$/, "", $i); printf "$%d: <%s>\n", i, $i}
}' <<< "1,2\,2,333"
$1: <1>
$2: <2\,2>
$3: <333>

非常感谢大家!但是 awk 的内容分割有点头疼,我更喜欢 Perl 的 split。$ echo -n "1,2\,2,333"|perl -e 'while (<>) {@a=split(/(?<!\\),/);print "$a[1]\n"}' - peihan
2
@peihan:请注意,当一行中可能有多个“\”时,回顾法并不适用:例如1,2\\\\3,4,55 - nhahtdh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接