【问题标题】:Arithmetic replacement in a text file文本文件中的算术替换
【发布时间】:2020-06-14 18:52:54
【问题描述】:

我有一个这样的文件:

id=1+5
id=1+9
id=25100+10
xyz=1+
abc=123456
conf_string=LMN,J,IP,25100+1,0,3,1

我想将 x+y 的实例替换为 (x+y) 的值。即1+5换成6,25100+1换成25001,以此类推。

我通过匹配 /[:digit:]++[+digit:]+/ 之类的正则表达式来尝试使用 gawk 使用以下我可以替换一些实例。

gawk 'BEGIN {FS = "[=+,]"} ; /[:digit:]++[+digit:]+/ {print $1 "=" ($2 + $3)} ! /[:digit:]++[+digit:]+/ {print $0}' /tmp/1.txt 
id=6
id=10
id=25110
xyz=1+
abc=123456
conf_string=LMN,J,IP,25100+1,0,3,1

我不确定如何匹配和替换上例中的 (25100+1)。理想情况下,我想提取<number> + <number> 的所有实例并将其替换为总和。它始终是两个数字的总和。

【问题讨论】:

    标签: awk sed


    【解决方案1】:

    GNU awk:

    $ awk 'BEGIN{r = @/([0-9]+)\+([0-9]+)/}
           { while(match($0, r, m)) sub(r, m[1] + m[2]) } 1' ip.txt
    id=6
    id=10
    id=25110
    xyz=1+
    abc=123456
    conf_string=LMN,J,IP,25101,0,3,1
    
    • r=@/([0-9]+)\+([0-9]+)/ 将正则表达式保存在变量中,[0-9] 将匹配所有数字
    • 如果正则表达式匹配,match($0, r, m) 将为真,匹配的部分将在m 数组中可用
    • m[1] + m[2] 将两个数字相加
    • 对于旧版本,请使用 awk '{while(match($0, /([0-9]+)\+([0-9]+)/, m)) sub(/([0-9]+)\+([0-9]+)/, m[1] + m[2]) } 1' ip.txt,因为不支持将正则表达式保存在变量中

    注意

    • [:digit:] 应该在字符类 [[:digit:]] 中使用
    • ++ 应该是 +\+ 因为你打算第二个匹配 + 字面意思

    另请参阅:How to coerce AWK to evaluate string as math expression?


    使用perl,您可以简单地使用e 标志来评估替换为代码

    perl -pe 's/(\d+)\+(\d+)/$1+$2/ge' ip.txt
    # or    
    perl -pe 's/\d+\+\d+/$&/gee' ip.txt
    

    【讨论】:

    • 非常简洁! +1
    • @Sundeep 我在运行您发送的命令时遇到语法错误。我不太精通 awk 语法,因此无法轻松解决错误。 ``` gawk 'BEGIN{r = @/([0-9]+)\+([0-9]+)/} { while(match($0, r, m)) sub(r, m[1 ] + m[2]) } 1' 3.txt gawk: cmd. line:1: BEGIN{r = @/([0-9]+)\+([0-9]+)/} gawk: cmd. line:1: ^ 语法错误```
    • @javed 可能是因为版本不同,我在gawk version 5.1.0 上测试过...awk '{while(match($0, /([0-9]+)\+([0-9]+)/, m)) sub(/([0-9]+)\+([0-9]+)/, m[1] + m[2]) } 1' 有效吗?
    • 是的,awk 命令有效。我有'GNU Awk 4.1.4',也许这就是错误出现的原因。感谢您更新命令。
    • 我已经接受了这个解决方案,因为它最早对我有用。 @RavinderSingh13 & agc 的其他解决方案也同样有效。谢谢大家帮助我。
    【解决方案2】:

    您能否尝试使用 GNU awk 进行跟踪、编写和测试,并提供示例。此解决方案将处理任何第二个字段以 + 开头或以 + 结尾的情况,例如输出中的第 4 行。在以下链接 https://ideone.com/x2EQ0P

    上测试的编辑代码
    awk '
    BEGIN{
      FS=OFS="="
    }
    {
      num=split($2,array1,",")
      for(i=1;i<=num;i++){
        num1=split(array1[i],array2,"+")
          for(k=1;k<=num1;k++){
            if(num1==1){array1[i]=array2[k] }
            if(array2[k]~/^[0-9]+$/){
               val+=array2[k]
               array1[i]=(array2[1]!=""?"":"+") val (array2[num1]!=""?"":"+")
            }
         }
         val=""
      }
      for(o=1;o<=num;o++){
        value=(value?value ",":"")array1[o]
      }
      $2=value
      value=""
    }
    1
    ' Input_file
    


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

    awk '                                             ##Starting awk program from here.
    BEGIN{                                            ##Starting BEGIN section of this code from here.
      FS=OFS="="                                      ##Setting field separator nd output field separator as = here.
    }
    {
      num=split($2,array1,",")                        ##Splitting 2nd field here.
      for(i=1;i<=num;i++){                            ##Running for loop till value of num here.
        num1=split(array1[i],array2,"+")              ##Splitting 2nd field further to array2 with delimiter + here.
          for(k=1;k<=num1;k++){                       ##Running for loop to all fields wchih are separated by + here.
            val+=array2[k]                            ##Creating val which keeps on adding value of array2 with index k here.
          }
        array1[i]=(array2[1]!=""?"":"+") val (array2[num1]!=""?"":"+")    ##Assigning val to current array1 value after addition of all items in 2nd field.
        val=""                                        ##Nullifying val here.
      }
      for(o=1;o<=num;o++){                            ##Running a for loop till length of 1st array here.
        value=(value?value ",":"")array1[o]           ##Keep on appending value of array1 with index o to var value here.
      }
      $2=value                                        ##Setting value to 2nd field here.
      value=""                                        ##Nullify var value here.
    }
    1                                                 ##Mentioning 1 to print all lines here.
    ' Input_file                                      ##Mentioning Input_file name here.
    

    【讨论】:

    • 这是另一个很好的系统方法。但是,@sundeep 使用match 解决方案将一只兔子从他的帽子里拉了出来。我从两者都倾斜。
    • @DavidC.Rankin,谢谢先生的鼓励。是的,Sundeep 的回答非常好:)
    • @RavinderSingh13 非常感谢您详细解释解决方案。虽然它大部分都有效,但我看到的一个问题是它在最后一行进行了无意的替换。 conf_string=0,0,0,25101,0,3,1。应该是conf_string=LMN,J,IP,25101,0,3,1
    • @javed,好的,我已经根据您显示的示例编辑了代码,并通过ideone.com/x2EQ0P链接在移动设备上对其进行了测试,请检查一次,如果有疑问,请在此处了解。
    • @javed,Javed bhai,这对你有用吗?如有任何疑问,请告知。
    【解决方案3】:

    使用 GNU sed,将一些 shell 脚本 $(( )) 包裹在总和周围,然后是危险的 eval 命令来运行它:

    sed 's#\(.*\)\([0-9]\++[0-9]\+\)\(.*\)#printf "\1$((\2))\3"#e' file
    

    ...或者,使用更少的反斜杠\

    sed -r 's#(.*)([0-9]+\+[0-9]+)(.*)#printf "\1$((\2))\3"#e' file
    

    任一输出:

    id=6
    id=10
    id=251010
    xyz=1+
    abc=123456
    conf_string=LMN,J,IP,25101,0,3,1
    

    【讨论】:

    • 上面的代码假定输入是干净的,并且没有充满单引号和双引号——如果是这样,则需要在前面加上额外的代码来引用这些代码,以免它们引起麻烦eval.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 1970-01-01
    相关资源
    最近更新 更多