【问题标题】:GNU awk, FPAT and matching negative string regex with regex and special charsGNU awk、FPAT 和匹配负字符串正则表达式与正则表达式和特殊字符
【发布时间】:2023-03-11 17:40:02
【问题描述】:

TL(请参阅问题末尾附近的 TL;DR)

我使用管道作为字段分隔符 (|) 和反斜杠引号对作为数据中带有分隔符的字段的引号 (\") 来获取此数据,例如:

1|\"2\"|\"3.1|3.2\"|4  # basically 1, 2, 3.1|3.2, 4

即(在 awk 中):

$1==1
$2==\"2\"
$3==\"3.1|3.2\"
$4==4

我决定尝试使用 GNU awk 的 FPAT 来解决字段问题,因为将否定匹配正则表达式写入 \" 似乎并没有那么糟糕。

我来到this answer to Regular expression to match a line that doesn't contain a word 时有一个指向(非现场链接)an online generator of negative regular expressions given an input phrase 的链接。

由于生成器目前仅支持字母数字和空格字符\"(反斜杠引号)被替换为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 $1,$2,$3,$4
}' data

哪个输出:

1
bq2bq
bq3.1p3.2bq
4

这是对的。将生成的程序中的pqs 替换为|"s:

$ gawk 'BEGIN {
    FPAT="([^|]*)|(b\"([^b]|b+[^b\"])*b*b\")"
    OFS=ORS
}
{
    print $1,$2,$3,$4
}' data

输出:

1
b"2b"
b"3.1|3.2b"
4

这仍然是正确的。但是,当用\s 替换bs 并添加一些转义时,导致:

TL;DR如何在下面的脚本中修复转义)

$ gawk 'BEGIN {
    FPAT="([^|]*)|(\\\"([^\\]|\\+[^\\\"])*\\*\\\")"
    OFS=ORS
} 
{
    print $1,$2,$3,$4
}' data

并且输出失败或与之前不同:

1
\"2\"
\"3.1
3.2\"

所以我的\\s 可能有问题,但经过多次尝试和错误后,我的脑海中充满了反斜杠,所有的想法几乎都逃脱了(双关语)。由于社区都是关于分享的,所以我想与你们分享我的头痛。

编辑:显然它与引号中的反斜杠有关,因为如果不是定义FPAT="...",而是使用GNU awk's strongly typed 输入FPAT=@/.../,我会得到正确的输出:

$ gawk 'BEGIN {
    FPAT=@/([^|]*)|(\\\"([^\\]|\\+[^\\\"])*\\*\\\")/
    OFS=ORS
} 
{
    print $1,$2,$3,$4
}' data

现在输出:

1
\"2\"
\"3.1|3.2\"
4

【问题讨论】:

  • 关于Edit: Apparently it's got something to do with backslashes in quotes - idk,如果你逃避事情的方式有任何其他问题,但这不是你遇到的问题,这正是我在回答中所说的,@987654356 @ 不代表not \"。我尝试在你的最后一个代码段中使用 FPAT,但得到了awk: tst.awk:2: warning: regexp escape sequence `\"' is not a known regexp operator 所以我知道你想在那里发布什么。
  • 有趣。我最近在使用sub(/\"/...) 时也收到了同样的警告,以上所有部分都没有给我这样的警告。如果有意义的话,感觉就像是突然开始的。
  • 再次 - [^\\\"] 表示 neither the char \ nor the char " 当您需要一些表示 not the string \" 的东西时,BRE 或 ERE 中不存在这样的结构,这就是为什么您必须将每个 \" 转换为一个字符X 然后你可以写[^X],就像我的答案一样,我使用\n 表示X。当然,您可以使用其他一些正则表达式从发布的示例输入中获得预期的输出,但是如果给定其他输入,它将失败,例如包含单个 \ 或单个 " 的输入,例如 \"foo"bar\here\"
  • 我明白了。
  • 您应该从sub(/\"/...) 收​​到警告,因为该正则表达式要么试图转义文字字符,要么试图指定文字\ 但忘记转义它 - 在任何一种情况下,正则表达式都是错误,它应该是 sub(/"/,...)sub(/\\"/,''') 并且该工具不理解您要说的内容,因此它会猜测您想要前者并警告您这样做。

标签: regex awk


【解决方案1】:

您似乎试图使用[^\\\"] 来表示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
    $0 = $0                       # 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=1
$2=\"2\"
$3=\"3.1|3.2\"
$4=4

如果没有特定的字符不能出现在您的输入中,那么很容易操纵您的输入,以便在字段拆分期间不能出现您喜欢的任何字符(我在这里再次使用\n,但这次即使您的输入是包含\ns 的多行记录,它也可以工作,假设您适当地设置 RS 以允许读取多行记录):

$ cat tst.awk
BEGIN { FPAT="([^|]*)|(\n[^\n]+\n)" }
{
    gsub(/@/,"@A")
    gsub(/\n/,"@B")
    gsub(/\\"/,"\n")
    $0 = $0
    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=1
$2=\"2\"
$3=\"3.1|3.2\"
$4=4

【讨论】:

  • 首先感谢您这么及时的回答。 - - 试图使用[^\\\"] 来表示not the string \",但这并不意味着 嗯,这只是生成的正则表达式的一部分,它似乎在整体上工作(bpq s) 但我无法使用正确的字符。再说一次,我没有进一步测试它,但是,除了问题中的那条数据线,所以我真的不知道我面前的陷阱。
  • 它似乎只适用于bpq,它实际上不可能工作。在该正则表达式中看到[^bq] 是解决问题的重要线索。我怀疑你用来生成正则表达式的任何东西都认为bq 是一个包含一个字符的变量,而不是一个包含两个字符的字符串,而是 idk。
猜你喜欢
  • 1970-01-01
  • 2021-11-03
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
  • 1970-01-01
  • 2020-12-28
  • 1970-01-01
相关资源
最近更新 更多