gpt4 book ai didi

awk - 如何使用 awk 使用非贪婪的正则表达式在嵌套分隔符中提取数据

转载 作者:行者123 更新时间:2023-12-04 21:08:13 30 4
gpt4 key购买 nike

这个问题以多种形式重复出现,具有许多不同的多字符分隔符,因此恕我直言,值得一个规范的答案。

给定一个输入文件,如:

<foo> .. 1 <foo> .. a<2 .. </foo> .. </foo> <foo> .. @{<>}@ <foo> .. 4 .. </foo> .. </foo> <foo> .. 5 .. </foo>

如何使用与 awk 的非贪婪匹配来提取嵌套开始( <foo> )和结束( </foo> )分隔符之间的文本?

所需的输出(以任何顺序)是:
<foo> .. a<2 .. </foo>
<foo> .. 1 .. </foo>
<foo> .. 4 .. </foo>
<foo> .. @{<>}@ .. </foo>
<foo> .. 5 .. </foo>

请注意,开始或结束可以是任何多字符字符串,并且它们之间的文本可以是除这些字符串之外的任何内容,包括作为这些字符串一部分的字符,例如 <>本例中的字符。

最佳答案

主要的挑战是,由于 awk 仅支持贪婪匹配,因此您不能编写 <foo>.*</foo> 的任何变体。将在第一个 </foo> 处停止就行而不是上次</foo> .解决方法是将每个开始和结束字符串转换为不能出现在输入中的单个字符,这样就可以写x[^xy]*y其中 x 和 y 是那些开始/结束字符,但是如何选择无法出现在输入中的字符?你没有 - 你做一个:

$ cat nonGreedy.awk
{
$0 = encode($0)
while ( match($0,/({[^{}]*})/) ) {
print decode(substr($0,RSTART,RLENGTH))
$0 = substr($0,1,RSTART-1) substr($0,RSTART+RLENGTH)
}
}
function encode(str) {
gsub(/@/,"@A",str)
gsub(/{/,"@B",str); gsub(/}/,"@C",str)
gsub(/<foo>/,"{",str); gsub(/<\/foo>/,"}",str)
return str
}
function decode(str) {
gsub(/}/,"</foo>",str); gsub(/{/,"<foo>",str)
gsub(/@C/,"}",str); gsub(/@B/,"{",str)
gsub(/@A/,"@",str)
return str
}

$ awk -f nonGreedy.awk file
<foo> .. a<2 .. </foo>
<foo> .. 1 .. </foo>
<foo> .. 4 .. </foo>
<foo> .. @{<>}@ .. </foo>
<foo> .. 5 .. </foo>

上述工作通过您选择任何不能出现在开始/结束字符串中的字符(注意它不一定是根本不能出现在输入中的字符,只是不在那些字符串中),在这个案例我选择 @ ,并附加一个 A在输入中每次出现之后。此时每次出现 @A代表一个 @字符,并且保证不会出现 @B@后跟输入中任何其他地方。

现在我们可以选择另外 2 个我们想要用来表示开始/结束字符串的字符,在这种情况下我选择 {} ,并将它们转换为一些 @ - 前缀字符串,如 @B@C在这一点上,每次出现 @B代表一个 {字符和 @C代表一个 }字符并且没​​有 { s 或 } s 输入中的任何位置。

现在找到我们想要提取的字符串所要做的就是转换每个起始字符串 <foo>到我们选择的起始字符, { , 和每个结束字符串 </foo>到结束字符 }然后我们可以使用一个简单的正则表达式 {[^{}]*}表示 <foo>.*</foo> 的非贪婪版本.

当我们找到每个字符串时,我们只是以相反的顺序展开我们上面所做的转换(注意,您必须以完全相反的顺序展开对每个匹配字符串的替换),所以 {回到 <foo>@B回到 { , 和 @A回到 @等,我们有该字符串的原始文本。

以上将适用于任何awk。如果您的开始/结束字符串包含 RE 元字符,那么您必须转义这些或使用 while(index(substr()))循环而不是 gsub()来替换它们。

请注意,如果您确实使用了 gawk 并且标签不是嵌套的,那么您可以完全保留上面的 2 个函数并将脚本的其余部分更改为:
BEGIN { FPAT="{[^{}]*}" }
{
$0 = encode($0)
for (i=1; i<=NF; i++) {
print decode($i)
}
}

显然,您实际上并不需要将编码/解码功能放在单独的函数中,我只是在此处将其分开,以使该功能明确并与使用它的循环分开,以便清晰。

有关何时/如何应用上述方法的另一个示例,请参阅 https://stackoverflow.com/a/40540160/1745001 .

关于awk - 如何使用 awk 使用非贪婪的正则表达式在嵌套分隔符中提取数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40512518/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com