【问题标题】:How to represent many parts of awk sub/gsub's matched string如何表示 awk sub/gsub 的匹配字符串的许多部分
【发布时间】:2019-09-24 01:46:11
【问题描述】:

如何表示 awk sub 或 gsub 的匹配字符串的多个部分。

对于像“##code”这样的正则表达式,如果我想在“##”和“code”之间插入一个单词,我想要一种类似VSCode语法的方式,其中$1代表第一部分,$2代表第二部分部分

sub(/(##)(code)/, "$1before$2", str)

从awk的用户手册中,我发现awk使用&来表示整个匹配的字符串。我怎样才能像VSCode一样表示匹配字符串中的一个,两个或多个部分。

sub(正则表达式,替换 [,目标]) 搜索目标,将其视为字符串,查找与正则表达式 regexp 匹配的最左侧、最长的子字符串。通过用替换替换匹配的文本来修改整个字符串。修改后的字符串成为目标的新值。返回替换的次数(零或一)。

正则表达式参数可以是正则表达式常量 (/.../) 或字符串常量 ("...")。在后一种情况下,字符串被视为要匹配的正则表达式。请参阅 Computed Regexps 讨论这两种形式之间的差异,以及正确编写程序的含义。

这个函数很特别,因为 target 不只是用来计算一个值,也不是任何表达式都可以——它必须是一个变量、字段或数组元素,以便 sub() 可以在其中存储修改后的值。如果省略此参数,则默认使用并更改 $0.48 例如:

str = "水,水,无处不在" 子(/at/,“我”,str) 通过用“ith”替换最左边出现的最长的“at”,将 str 设置为“wither, water, around”。

如果特殊字符“&”出现在替换中,它代表正则表达式匹配的精确子字符串。 (如果正则表达式可以匹配多个字符串,那么这个精确的子字符串可能会有所不同。)例如:

{ sub(/candidate/, "& and his wife"); print }

在每个输入行中将第一次出现的“候选人”更改为“候选人和他的妻子”。这是另一个例子:

用户手册的链接是here

【问题讨论】:

  • 您正在寻找gensub
  • 是的! gensub 非常适合我的要求。谢谢你,oguz ismail。
  • 请注意gensub 是 GNU awk 扩展。

标签: regex awk gsub


【解决方案1】:

当您使用 GNU awk 时,您可以为此目的使用 gensub。如果没有 gensub 用于任何通用 awk,它会变得更加乏味。该过程可能是这样的:

ere="(ere1)(ere2)"
match(str,ere)
tmp=substr(str,RSTART,RLENGTH)
match(tmp,"ere1"); part1=substr(tmp,RSTART,RLENGTH)
part2=substr(tmp,RLENGTH)
sub(ere,part1 "before" part2,str)

问题在于它并不总是有效,您必须对其进行一些设计。由于 ERE 的贪婪,可以创建一个简单的失败”:

str="foocode"
ere="(f.*o)(code)"
match(str,ere)                    # finds "foocode"
tmp=substr(str,RSTART,RLENGTH)    # tmp <: "foocode"
match(tmp,"(f.*o)");              # greedy "fooco"
part1=substr(tmp,RSTART,RLENGTH)  # part1 <: "fooco"
part2=substr(tmp,RLENGTH)         # part2 <: "de"
sub(ere,part1 "before" part2,str) # :> "foocobeforede

【讨论】:

    【解决方案2】:

    您最好的选择是使用 GNU awk 来完成以下任一操作:

    $ awk '{$0=gensub(/(##)(code)/,"\\1before\\2",1)} 1' <<<'##code'
    ##beforecode
    
    $ awk 'match($0,/(##)(code)/,a){$0=a[1] "before" a[2]} 1' <<<'##code'
    ##beforecode
    

    第一个只允许您移动文本段,而第二个允许您调用函数、执行数学运算或对匹配的文本执行任何其他操作,然后在原始文本中移动它或对其执行任何其他操作:

    $ awk 'match($0,/(##)(code)/,a){$0=length(a[1])*10 "before" toupper(a[2])} 1' <<<'##code'
    20beforeCODE
    

    在考虑了一会儿之后,我不知道如何仅使用 POSIX awk 结构以任何合理的方式获得所需的行为。这是我尝试过的(matches() 函数):

    $ cat tst.awk
    BEGIN {
        str = "foobar"
        re  = "(f.*o)(b.*r)"
        printf "\nre \"%s\" matching string \"%s\"\n", re, str
    
        print "succ: gensub():  ", gensub(re,"<\\1> <\\2>",1,str)
        print "succ: match():   ", (match(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
        print "succ: matches(): ", (matches(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
    
        str = "foofoo"
        re  = "(f.*o)(f.*o)"
        printf "\nre \"%s\" matching string \"%s\"\n", re, str
    
        print "succ: gensub():  ", gensub(re,"<\\1> <\\2>",1,str)
        print "succ: match():   ", (match(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
        print "fail: matches(): ", (matches(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
    }
    
    function matches(str,re,arr,    start,tgt,n,i,segs) {
        delete arr
        if ( start=match(str,re) ) {
            tgt = substr($0,RSTART,RLENGTH)
            n = split(re,segs,/[)(]+/) - 1
            for (i=1; RSTART && (i < n); i++) {
                if ( match(str,segs[i+1]) ) {
                    arr[i] = substr(str,RSTART,RLENGTH)
                    str = substr(str,RSTART+RLENGTH)
                }
            }
        }
        return start
    }
    

    .

    $ awk -f tst.awk
    
    re "(f.*o)(b.*r)" matching string "foobar"
    succ: gensub():   <foo> <bar>
    succ: match():    <foo> <bar>
    succ: matches():  <foo> <bar>
    
    re "(f.*o)(f.*o)" matching string "foofoo"
    succ: gensub():   <foo> <foo>
    succ: match():    <foo> <foo>
    fail: matches():  <foofoo> <>
    

    当然,这不适用于第二种情况,因为f.*o 的第一个 RE 段与整个字符串 foofoo 匹配,当然,如果您尝试反向使用 RE 段,也会发生同样的事情。我还考虑过像上面那样获取 RE 段,然后从传入的字符串一次一个字符建立一个新字符串,并将第一个 RE 段与 THAT 进行比较,直到它匹配为止,因为这将是 RE 段的最短匹配字符串但是对于像这样的字符串+RE,这将失败:

    str='foooobar'
    re='(f.*o)(b.*r)'
    

    因为 f.*o 会在 foo 真正需要匹配 fooooo 时匹配该算法。

    所以 - 我想你需要继续迭代(注意你迭代的方向 - 我期望从最后是正确的)直到你将字符串分成多个段,每个段都匹配左侧的每个 RE 段-最长的时尚。看起来工作量很大!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-04
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2020-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多