【问题标题】:Replace text using a regex only iff it is a field仅当它是一个字段时才使用正则表达式替换文本
【发布时间】:2017-02-14 14:55:25
【问题描述】:

我有一个逗号分隔的文件,如下所示

Start,Card,App,Description,Internet
2013-01-02 11:54:00,No Data,Off,Our system has  No Data,Off
2013-01-03 05:30:00,Visa,On,The monitoring system is Off,Off
2013-01-05 17:07:00,Master,Off,The system is On,On
2013-01-07 00:08:00,No Data,No Data,No Data,On
2013-01-09 04:40:00,Master,Off,System is Off,On

我想用NA替换No Data,用0替换Off,用1替换On,只有当这些词是字段时,即它们有逗号或换行符开头或结尾的线。所需的输出是

Start,Card,App,Description,Internet
2013-01-02 11:54:00,NA,0,Our system has  No Data,0
2013-01-03 05:30:00,Visa,1,The monitoring system is Off,0
2013-01-05 17:07:00,Master,0,The system is On,1
2013-01-07 00:08:00,NA,NA,NA,1
2013-01-09 04:40:00,Master,0,System is Off,1

例如,我试过了,

sed 's/,Off,/,0,/g' test.txt

仅当字段数大于 1 或小于最大字段数时才会替换。我还尝试使用环视并省略像

这样的标题
sed '2,$s/(?<=^|,)Off(?=$|,)/0/g' test.txt

我认为第二个命令应该可以工作,但没有。我哪里做错了?感谢您的提示。由于该文件是 biggg,因此不能选择像 $4 这样的列的绝对索引。

【问题讨论】:

    标签: regex unix awk replace sed


    【解决方案1】:

    sed 不支持环视,perl 来救援

    $ perl -pe 'if($. > 1){ s/(^|,)\KOff(?=,|$)/0/g; s/(^|,)\KOn(?=,|$)/1/g; s/(^|,)\KNo Data(?=,|$)/NA/g; }' ip.txt
    Start,Card,App,Description,Internet
    2013-01-02 11:54:00,NA,0,Our system has  No Data,0
    2013-01-03 05:30:00,Visa,1,The monitoring system is Off,0
    2013-01-05 17:07:00,Master,0,The system is On,1
    2013-01-07 00:08:00,NA,NA,NA,1
    2013-01-09 04:40:00,Master,0,System is Off,1
    

    或者,在, 上拆分输入行并在各个字段上执行替换

    $ perl -F, -lane 'if($. > 1){ foreach (@F){ s/^No Data$/NA/; s/^Off$/0/; s/^On$/1/ } } print join ",",@F' ip.txt 
    Start,Card,App,Description,Internet
    2013-01-02 11:54:00,NA,0,Our system has  No Data,0
    2013-01-03 05:30:00,Visa,1,The monitoring system is Off,0
    2013-01-05 17:07:00,Master,0,The system is On,1
    2013-01-07 00:08:00,NA,NA,NA,1
    2013-01-09 04:40:00,Master,0,System is Off,1
    


    awk

    $ awk -v FS=, -v OFS=, 'NR>1{ for(i=1; i<=NF; i++){ sub(/^No Data$/,"NA",$i); sub(/^Off$/,"0",$i); sub(/^On$/,"1",$i) } } 1' ip.txt 
    Start,Card,App,Description,Internet
    2013-01-02 11:54:00,NA,0,Our system has  No Data,0
    2013-01-03 05:30:00,Visa,1,The monitoring system is Off,0
    2013-01-05 17:07:00,Master,0,The system is On,1
    2013-01-07 00:08:00,NA,NA,NA,1
    2013-01-09 04:40:00,Master,0,System is Off,1
    

    【讨论】:

      【解决方案2】:

      在 awk 中使用三元运算符(c ? "a" : "b"):

      $ awk -F, '{for(i=NF;i>0;i--) $i=($i=="No Data"?"NA":($i=="Off"?"0":($i=="On"?"1":$i)))} 1' OFS=, file
      Start,Card,App,Description,Internet
      2013-01-02 11:54:00,NA,0,Our system has  No Data,0
      2013-01-03 05:30:00,Visa,1,The monitoring system is Off,0
      2013-01-05 17:07:00,Master,0,The system is On,1
      2013-01-07 00:08:00,NA,NA,NA,1
      2013-01-09 04:40:00,Master,0,System is Off,1
      

      【讨论】:

        【解决方案3】:
        awk -F, '{for(i=1;i<=NF;i++) if(split($i,a,"Off|On|No Data| +")==2) {sub(/No Data/,"NA",$i);sub(/Off/,"0",$i);sub(/On/,"1",$i)}}1' OFS="," file
        

        【讨论】:

        • 这不适用于所有情况,例如,如果字段位于行首.. 另外,如果有像 ,Off xyz 这样的字段怎么办?
        • 如果字段有其他文本而不是要匹配的模式,这仍然不起作用
        【解决方案4】:

        这可能对你有用(GNU sed):

        sed -r ':a;s/(^|,)No Data(,|$)/\1NA\2/g;s/(^|,)Off(,|$)/\10\2/g;s/(^|,)On(,|$)/\11\2/g;ta' file
        

        使用带有交替的反向引用来替换所需的字符串和分隔符。

        注意必须对所需字符串的任一侧的分隔符进行两次测试,因此循环回占位符:a

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-07
          • 1970-01-01
          • 2019-03-30
          • 1970-01-01
          • 2021-01-05
          • 1970-01-01
          相关资源
          最近更新 更多