【问题标题】:Non greedy match replacement with gensub and awk使用 gensub 和 awk 进行非贪婪匹配替换
【发布时间】:2021-07-02 06:09:15
【问题描述】:

我正在尝试使用 AWK 清理一堆带有 POS 标签的句子。每个句子可以没有、一个或多个格式为\POS{word|type} 的标签。我很难处理带有多个标签的句子。我找不到使正则表达式不贪婪的方法。示例

输入

sentence_1,My \POS{tailor,noun} is \POS{rich,adj}.

期望的输出

sentence_1,My tailor is rich.

我现在在哪里

echo "sentence_1,My \POS{tailor,noun} is \POS{rich,adj}."|awk -F "," 'BEGIN{OFS=","} {_id=$1;$1="";s=gensub(/\\POS{(.+?),.+?}/, "\\1", "gm", $0); print _id s}'

我得到错误的输出:

sentence_1,My tailor,noun} is \POS{rich.

句子正则表达式不贪心。我知道 awk 不能处理贪婪的表达式,但你会怎么做呢?提前致谢。

【问题讨论】:

  • 尽管我添加了问号,但似乎表达式很贪婪。第一组是从 \POS{ 到第二个 POS 标签的最后一个逗号
  • 你可以匹配[^}]+}
  • OP 对正则表达式有疑问,并且正则表达式标记存在于基本问题中,不确定为什么将其删除。

标签: regex linux bash awk


【解决方案1】:

对于您展示的示例,您能否尝试在 GNU awk 中进行跟踪、编写和测试,我相信应该可以在任何 awk 中使用。

echo "sentence_1,My \POS{tailor,noun} is \POS{rich,adj}." | 
awk '
{
  first=val=finalVal=""
  count=0
  while(match($0,/[a-zA-Z]+ \\POS{[^,]*/)){
    if(++count==1){
      first=substr($0,1,RSTART-1)
    }
    val=substr($0,RSTART,RLENGTH)
    sub(/\\POS{/,"",val)
    finalVal=(finalVal?finalVal OFS:"")val
    $0=substr($0,RSTART+RLENGTH)
  }
  print first finalVal
}'

或者尝试关注,如果您在\POS{rich,adj}. 之后有任何内容,例如.,那么就这样吧:

echo "sentence_1,My \POS{tailor,noun} is \POS{rich,adj}." | 
awk '
{
  while(match($0,/[a-zA-Z]+ \\POS{[^,]*/)){
    if(++count==1){
      first=substr($0,1,RSTART-1)
    }
    val=substr($0,RSTART,RLENGTH)
    sub(/\\POS{/,"",val)
    finalVal=(finalVal?finalVal OFS:"")val
    $0=substr($0,RSTART+RLENGTH)
  }
  sub(/.*}/,"")
  print first finalVal $0
}'

说明:为上述添加详细说明。

echo "sentence_1,My \POS{tailor,noun} is \POS{rich,adj}." |  ##Using echo to print value.
                                               ##Sending its output as input to awk program.
awk '                                          ##Starting awk program from here.
{
  first=val=finalVal=""                        ##Nullifying variables here.
  count=0                                      ##Setting count to 0 here.
  while(match($0,/[a-zA-Z]+ \\POS{[^,]*/)){    ##Using while loop to run match in it.
  ##Match has regex to match one or more alphabets space \POS{ till comma comes.
    if(++count==1){                            ##Checking condition if count is 1 then do following.
      first=substr($0,1,RSTART-1)              ##Creating first to have everything before matched this should have very first matches before value eg--> sentence_1,My
    }
    val=substr($0,RSTART,RLENGTH)              ##Creating val which is sub string of matched regex.
    sub(/\\POS{/,"",val)                       ##Using substitute \POS{ with NULL.
    finalVal=(finalVal?finalVal OFS:"")val     ##Creating finalVal to have all values in it.
    $0=substr($0,RSTART+RLENGTH)               ##Re-creating whole line to have only rest of the line in it, removing matched part.
  }
  print first finalVal                         ##Printing first and finalVal here.
}'

【讨论】:

  • 哇!非常感谢!
  • @ivallesp,欢迎您的欢呼,祝您学习愉快。
  • @ivallesp,我现在也添加了第二个解决方案,如果您的行中有一些剩余值,您可以使用第二个,如果有任何疑问,请告诉我(还添加了详细说明代码)。
  • 好的,谢谢!我想你可以离开第二个解决方案
【解决方案2】:

这是一个使用否定括号表达式的sed 解决方案:

s='sentence_1,My \POS{tailor,noun} is \POS{rich,adj}.'
sed -E s'/\\POS\{([^,]+),[^}]*\}/\1/g' <<< "$s"

sentence_1,My tailor is rich.

RegEx 解释:

  • \\POS\{:匹配\POS{
  • ([^,]+):匹配 1 个或多个非逗号字符并在 #1 组中捕获
  • ,:匹配逗号
  • [^}]*:匹配 0 个或多个非} 字符
  • \}:匹配一个}
  • /\1:替换为 \1,即捕获组 #1 的反向引用

【讨论】:

    【解决方案3】:

    或者用 gawk 的gensub 稍微“更简单”(?)(最初尝试过):

    $ echo 'sentence_1,My \POS{tailor,noun} is \POS{rich,adj}' | gawk '{s=gensub(/\\POS{([^,]+),[^}]+}/, "\\1", "G", $0); print s}'
    sentence_1,My tailor is rich
    

    【讨论】:

    • 我喜欢这个,你能解释一下有什么变化吗?除了傻瓜
    • 我的意思是,将.+? 替换为[^,]+ 背后的逻辑是什么?
    • [^,]+ 代表:任何字符,但 , 重复 1 次或更多次。我不太确定 .+?awk 发言中的含义 - 我相信这有点过分。
    • 好吧,.+?, 的含义与非 awk 正则表达式中的 [^,]+ 相同 :)。谢谢。由于您的解决方案更简单,因此我将接受的答案切换给您。谢谢
    猜你喜欢
    • 1970-01-01
    • 2015-01-03
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 2017-10-16
    • 1970-01-01
    • 2011-08-29
    相关资源
    最近更新 更多