【问题标题】:remove only *some* fullstops from a csv file从 csv 文件中仅删除 *一些* 句号
【发布时间】:2016-08-09 00:53:11
【问题描述】:

如果我有如下行:

1,987372,987372,C,T,.,.,.,.,.,.,.,.,1,D,.,.,.,.,.,.,.,1.293,12.23,0.989,0.973,D,.,.,.,.,0.253,0,4.08,0.917,1.048,1.000,1.000,12.998
1,987393,987393,C,T,.,.,.,.,.,.,.,.,1,D,.,.,.,.,.,.,0.152,1.980,16.09,0.999,0.982,D,-0.493,T,0.335,T,0.696,0,5.06,0.871,0.935,0.998,0.997,16.252

如何将,., 的所有实例替换为,?,

我想保留数字中的实际小数位,所以我不能这样做

sed 's/./?/g' file

但是在做的时候:

sed 's/,.,/,?,/g' file

这似乎只在某些情况下有效。即,仍有,., 的实例。

有人指点一下吗?

谢谢

【问题讨论】:

    标签: bash perl sed tr


    【解决方案1】:

    这应该可行:

    sed ':a;s/,\.,/,?,/g;ta' file
    

    对于连续的,., 字符串,在替换成功后,下一个要处理的字符将是以下与模式不匹配的.,因此您需要第二遍。

    :a 是即将到来的循环的标签

    ,\., 将匹配逗号之间的点。请注意,点必须被转义,因为. 用于匹配任何字符(,a, 将匹配,.,)。

    g 用于一般替换

    ta 测试之前的替换,如果成功,则循环到 :a 标签以查找剩余替换。

    【讨论】:

    • 感谢 Kenavoz。这已经奏效了。我怀疑是这种情况。如果你不介意,你能解释一下每个部分是如何工作的吗?我只真正了解s/,\.,/,?,/g。为什么要在. 之前逃跑?
    • IMO 如果只需要一个适当的正则表达式模式就可以一次性使用两次,这有点笨拙
    • @Borodin 你会推荐哪个 sed 正则表达式?
    • @Kenavoz:我不建议使用 sed 正则表达式
    • @Borodin 似乎您没有合适的正则表达式来建议,而是使用 perl 的另一种方法。只需将您的评论作为答案。
    【解决方案2】:

    使用 sed 可以通过运行一个循环,如上面的答案所示,但是使用带有环视的perl 命令行很容易解决问题:

    perl -pe 's/(?<=,)\.(?=,)/?/g' file
    
    1,987372,987372,C,T,?,?,?,?,?,?,?,?,1,D,?,?,?,?,?,?,?,1.293,12.23,0.989,0.973,D,?,?,?,?,0.253,0,4.08,0.917,1.048,1.000,1.000,12.998
    1,987393,987393,C,T,?,?,?,?,?,?,?,?,1,D,?,?,?,?,?,?,0.152,1.980,16.09,0.999,0.982,D,-0.493,T,0.335,T,0.696,0,5.06,0.871,0.935,0.998,0.997,16.252
    

    此命令不需要循环,因为我们只是使用后向和前向来断言它们的位置,而不是匹配周围的逗号。

    【讨论】:

      【解决方案3】:

      您有一个使用sed 样式正则表达式的示例。我将提供一个替代方案 - 解析 CSV,然后将每件事视为一个“字段”:

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      
      #iterate input row by row
      while ( <DATA> ) { 
         #remove linefeeds
         chomp;
         #split this row on ,
         my @row = split /,/;
         #iterate each field  
         foreach my $field ( @row ) {
             #replace this field with "?" if it's "."
             $field = "?" if $field eq ".";
         }
         #stick this row together again. 
         print join ",", @row,"\n";
      }
      
      __DATA__
      1,987372,987372,C,T,.,.,.,.,.,.,.,.,1,D,.,.,.,.,.,.,.,1.293,12.23,0.989,0.973,D,.,.,.,.,0.253,0,4.08,0.917,1.048,1.000,1.000,12.998
      1,987393,987393,C,T,.,.,.,.,.,.,.,.,1,D,.,.,.,.,.,.,0.152,1.980,16.09,0.999,0.982,D,-0.493,T,0.335,T,0.696,0,5.06,0.871,0.935,0.998,0.997,16.252
      

      为了说明这个概念,这比它需要的更详细。这可以简化为:

      perl -F, -lane 'print join ",", map { $_ eq "." ? "?" : $_ } @F'
      

      如果您的 CSV 也有引用,那么您可以拆分 Text::CSV 模块,它可以巧妙地处理这个问题。

      【讨论】:

        【解决方案4】:

        只需要一个替换

        $ perl -pe 's/,\.(?=,)/,?/g' dots.csv
        1,987372,987372,C,T,?,?,?,?,?,?,?,?,1,D,?,?,?,?,?,?,?,1.293,12.23,0.989,0.973,D,?,?,?,?,0.253,0,4.08,0.917,1.048,1.000,1.000,12.998
        1,987393,987393,C,T,?,?,?,?,?,?,?,?,1,D,?,?,?,?,?,?,0.152,1.980,16.09,0.999,0.982,D,-0.493,T,0.335,T,0.696,0,5.06,0.871,0.935,0.998,0.997,16.252
        

        【讨论】:

          【解决方案5】:

          您只需要 2 次通过,因为在 ,., 匹配中找到的尾随 , 无法匹配下一个 ,., 上的前导 ,

          $ sed 's/,\.,/,?,/g; s/,\.,/,?,/g' file
          1,987372,987372,C,T,?,?,?,?,?,?,?,?,1,D,?,?,?,?,?,?,?,1.293,12.23,0.989,0.973,D,?,?,?,?,0.253,0,4.08,0.917,1.048,1.000,1.000,12.998
          1,987393,987393,C,T,?,?,?,?,?,?,?,?,1,D,?,?,?,?,?,?,0.152,1.980,16.09,0.999,0.982,D,-0.493,T,0.335,T,0.696,0,5.06,0.871,0.935,0.998,0.997,16.252
          

          以上内容适用于任何操作系统上的任何 sed。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-10-22
            • 2014-05-05
            • 1970-01-01
            • 2014-02-24
            • 2020-07-18
            • 2016-12-23
            • 2016-09-26
            • 2021-03-13
            相关资源
            最近更新 更多