GNU awk、FPAT 和匹配负字符串正则表达式与正则表达式和特殊字符
GNU awk, FPAT and matching negative string regex with regex and special chars
TL(请参阅问题末尾的 TL;DR)
我使用管道作为字段分隔符 (|
) 和反斜杠引号对作为引号 (\"
) 来引用数据中带有分隔符的字段,例如:
1|\"2\"|\"3.1|3.2\"|4 # basically 1, 2, 3.1|3.2, 4
即(在 awk 中):
==1
==\"2\"
==\"3.1|3.2\"
==4
我决定尝试使用 GNU awk 的 FPAT 来解决现场问题,因为将否定匹配正则表达式写入 \"
似乎并不那么糟糕。
我是约this answer to Regular expression to match a line that doesn't contain a word with a link to (an offsite link) an online generator of negative regular expressions given an input phrase.
来的
由于生成器目前仅支持字母数字和space字符,\"
(反斜杠引号)被替换为bq
并且生成器提供的正则表达式:
^([^b]|b+[^bq])*b*$
|
替换为 p
并且上面的数据替换为:
1pbq2bqpbq3.1p3.2bqp4
1|\"2\"|\"3.1|3.2\"|4 # original for comparision
来自 GNU awk 文档 (FPAT="([^,]*)|(\"[^\"]+\")"
) 的示例 FPAT
用于生成 FPAT
:
FPAT="([^p]*)|(bq([^b]|b+[^bq])*b*bq)"
并完成了试验:
$ gawk 'BEGIN {
FPAT="([^p]*)|(bq([^b]|b+[^bq])*b*bq)"
OFS=ORS
}
{
print ,,,
}' data
哪个输出:
1
bq2bq
bq3.1p3.2bq
4
没错。在生成的程序中用 |"
s 替换 pq
s:
$ gawk 'BEGIN {
FPAT="([^|]*)|(b\"([^b]|b+[^b\"])*b*b\")"
OFS=ORS
}
{
print ,,,
}' data
输出:
1
b"2b"
b"3.1|3.2b"
4
还是对的。然而,当用 \
s 替换 b
s 并添加一些转义时,导致:
(TL;DR 如何修复下面脚本中的转义)
$ gawk 'BEGIN {
FPAT="([^|]*)|(\\"([^\]|\+[^\\"])*\*\\")"
OFS=ORS
}
{
print ,,,
}' data
并且输出失败或与之前的不同:
1
\"2\"
\"3.1
3.2\"
所以我的 \
可能有问题,但在尝试和犯错太多次之后,我的脑子里充满了反斜杠,所有的想法几乎都逃脱了(双关语意)。由于社区是关于分享的,所以我想和你们分享我的头痛。
编辑:显然它与引号中的反斜杠有关,因为 if 而不是定义 FPAT="..."
我使用 GNU awk's strongly typed 输入 FPAT=@/.../
我得到正确的输出:
$ gawk 'BEGIN {
FPAT=@/([^|]*)|(\\"([^\]|\+[^\\"])*\*\\")/
OFS=ORS
}
{
print ,,,
}' data
现在输出:
1
\"2\"
\"3.1|3.2\"
4
你好像想用[^\\"]
来表示not the string \"
,但其实不是那个意思,它的意思是neither the char \ nor the char "
。您 需要 在 FPAT
正则表达式的那部分中有一个单独的字符来求反,所以方法是将输入中的每个 \"
转换为一个单独的字符不能出现在输入中(我在下面使用 \n
因为通常是 RS
但你可以使用任何不能出现在记录中的字符),然后将记录拆分为字段,然后在使用每个单独的字段之前恢复 \"
s:
$ cat tst.awk
BEGIN { FPAT="([^|]*)|(\n[^\n]+\n)" }
{
gsub(/\"/,"\n") # Replace each\" with \n in the record
[=10=] = [=10=] # Re-split the record into fields
for (i=1; i<=NF; i++) {
gsub("\n","\\"",$i) # Replace each \n with \" in the field
print "$"i"=" $i
}
}
$ awk -f tst.awk file
=1
=\"2\"
=\"3.1|3.2\"
=4
如果您的输入中没有不能出现的特定字符,那么很容易操纵您的输入,这样您喜欢的任何字符在字段拆分期间都不会出现(我再次使用 \n
在这里,但这次即使您的输入是包含 \n
s 的多行记录,假设您适当地设置了 RS 以允许读取多行记录,它也会工作):
$ cat tst.awk
BEGIN { FPAT="([^|]*)|(\n[^\n]+\n)" }
{
gsub(/@/,"@A")
gsub(/\n/,"@B")
gsub(/\"/,"\n")
[=12=] = [=12=]
for (i=1; i<=NF; i++) {
gsub("\n","\\"",$i)
gsub("@B","\n",$i)
gsub("@A","@",$i)
print "$"i"=" $i
}
}
$ awk -f tst.awk file
=1
=\"2\"
=\"3.1|3.2\"
=4
TL(请参阅问题末尾的 TL;DR)
我使用管道作为字段分隔符 (|
) 和反斜杠引号对作为引号 (\"
) 来引用数据中带有分隔符的字段,例如:
1|\"2\"|\"3.1|3.2\"|4 # basically 1, 2, 3.1|3.2, 4
即(在 awk 中):
==1
==\"2\"
==\"3.1|3.2\"
==4
我决定尝试使用 GNU awk 的 FPAT 来解决现场问题,因为将否定匹配正则表达式写入 \"
似乎并不那么糟糕。
我是约this answer to Regular expression to match a line that doesn't contain a word with a link to (an offsite link) an online generator of negative regular expressions given an input phrase.
来的由于生成器目前仅支持字母数字和space字符,\"
(反斜杠引号)被替换为bq
并且生成器提供的正则表达式:
^([^b]|b+[^bq])*b*$
|
替换为 p
并且上面的数据替换为:
1pbq2bqpbq3.1p3.2bqp4
1|\"2\"|\"3.1|3.2\"|4 # original for comparision
来自 GNU awk 文档 (FPAT="([^,]*)|(\"[^\"]+\")"
) 的示例 FPAT
用于生成 FPAT
:
FPAT="([^p]*)|(bq([^b]|b+[^bq])*b*bq)"
并完成了试验:
$ gawk 'BEGIN {
FPAT="([^p]*)|(bq([^b]|b+[^bq])*b*bq)"
OFS=ORS
}
{
print ,,,
}' data
哪个输出:
1
bq2bq
bq3.1p3.2bq
4
没错。在生成的程序中用 |"
s 替换 pq
s:
$ gawk 'BEGIN {
FPAT="([^|]*)|(b\"([^b]|b+[^b\"])*b*b\")"
OFS=ORS
}
{
print ,,,
}' data
输出:
1
b"2b"
b"3.1|3.2b"
4
还是对的。然而,当用 \
s 替换 b
s 并添加一些转义时,导致:
(TL;DR 如何修复下面脚本中的转义)
$ gawk 'BEGIN {
FPAT="([^|]*)|(\\"([^\]|\+[^\\"])*\*\\")"
OFS=ORS
}
{
print ,,,
}' data
并且输出失败或与之前的不同:
1
\"2\"
\"3.1
3.2\"
所以我的 \
可能有问题,但在尝试和犯错太多次之后,我的脑子里充满了反斜杠,所有的想法几乎都逃脱了(双关语意)。由于社区是关于分享的,所以我想和你们分享我的头痛。
编辑:显然它与引号中的反斜杠有关,因为 if 而不是定义 FPAT="..."
我使用 GNU awk's strongly typed 输入 FPAT=@/.../
我得到正确的输出:
$ gawk 'BEGIN {
FPAT=@/([^|]*)|(\\"([^\]|\+[^\\"])*\*\\")/
OFS=ORS
}
{
print ,,,
}' data
现在输出:
1
\"2\"
\"3.1|3.2\"
4
你好像想用[^\\"]
来表示not the string \"
,但其实不是那个意思,它的意思是neither the char \ nor the char "
。您 需要 在 FPAT
正则表达式的那部分中有一个单独的字符来求反,所以方法是将输入中的每个 \"
转换为一个单独的字符不能出现在输入中(我在下面使用 \n
因为通常是 RS
但你可以使用任何不能出现在记录中的字符),然后将记录拆分为字段,然后在使用每个单独的字段之前恢复 \"
s:
$ cat tst.awk
BEGIN { FPAT="([^|]*)|(\n[^\n]+\n)" }
{
gsub(/\"/,"\n") # Replace each\" with \n in the record
[=10=] = [=10=] # Re-split the record into fields
for (i=1; i<=NF; i++) {
gsub("\n","\\"",$i) # Replace each \n with \" in the field
print "$"i"=" $i
}
}
$ awk -f tst.awk file
=1
=\"2\"
=\"3.1|3.2\"
=4
如果您的输入中没有不能出现的特定字符,那么很容易操纵您的输入,这样您喜欢的任何字符在字段拆分期间都不会出现(我再次使用 \n
在这里,但这次即使您的输入是包含 \n
s 的多行记录,假设您适当地设置了 RS 以允许读取多行记录,它也会工作):
$ cat tst.awk
BEGIN { FPAT="([^|]*)|(\n[^\n]+\n)" }
{
gsub(/@/,"@A")
gsub(/\n/,"@B")
gsub(/\"/,"\n")
[=12=] = [=12=]
for (i=1; i<=NF; i++) {
gsub("\n","\\"",$i)
gsub("@B","\n",$i)
gsub("@A","@",$i)
print "$"i"=" $i
}
}
$ awk -f tst.awk file
=1
=\"2\"
=\"3.1|3.2\"
=4