【问题标题】:Perl - Substitute after nth delimiterPerl - 在第 n 个分隔符之后替换
【发布时间】:2016-05-23 13:33:25
【问题描述】:

我需要一些帮助,我想在文件的行上进行替换,如下所示:

aoipp;dadada.12312;ss;1245454;Xiop;12.12;45.3;47.897;31.5;
asdfafd;14355.54664;peasd;125.1;900.2;76.897;67.456;asdfdf;
perio;777.2;ipoes;900.34;2;1980.45;870.98;67.67;

我想用, 替换每个.,但只能在分隔符; 第五次出现之后。其他一切都需要保持不变。所以所需的输出文件如下所示:

aoipp;dadada.12312;ss;1245454;Xiop;12,12;45,3;47,897;31,5;
asdfafd;14355.54664;peasd;125.1;900.2;76,897;67,456;asdfdf;
perio;777.2;ipoes;900.34;2;1980,45;870,98;67,67;

我有兴趣主要在 perl 中执行此操作,因此我可以将其合并到更大的程序中,但也欢迎使用 bash / awk 中的任何解决方案。提前致谢。

【问题讨论】:

  • 我不知道为什么人们一直对我投反对票。以下所有答案都达到了预期的结果,但我只能接受一个。
  • 可能是因为您没有尝试自己解决问题。
  • 很公平。我不知道处理这个问题所需的正则表达式。下次我会在我开始提问时发布一些失败的尝试。

标签: bash perl awk delimiter substitution


【解决方案1】:

这个 awk 单行应该适合你:

awk -F';' -v OFS=";" '{for(i=6;i<=NF;i++)gsub("[.]",",",$i)}7' file

它从第 6 个字段开始(; 分隔),对于每个字段,将所有 . 替换为 ,

使用您的数据进行测试:

kent$  cat f
aoipp;dadada.12312;ss;1245454;Xiop;12.12;45.3;47.897;31.5;
asdfafd;14355.54664;peasd;125.1;900.2;76.897;67.456;asdfdf;
perio;777.2;ipoes;900.34;2;1980.45;870.98;67.67;

kent$  awk -F';' -v OFS=";" '{for(i=6;i<=NF;i++)gsub("[.]",",",$i)}7' f
aoipp;dadada.12312;ss;1245454;Xiop;12,12;45,3;47,897;31,5;
asdfafd;14355.54664;peasd;125.1;900.2;76,897;67,456;asdfdf;
perio;777.2;ipoes;900.34;2;1980,45;870,98;67,67;

【讨论】:

  • 非常感谢。最后的“7”有点令人困惑,但你的建议是可行的。
  • @onlyf 它只是打印,
  • awks *sub() 函数的第一个参数是正则表达式,而不是字符串,因此请使用正则表达式分隔符 /.../ 而不是字符串分隔符 "...",因为后者会使您的代码像 awk 一样复杂在使用它之前将字符串转换为正则表达式。
【解决方案2】:

我使用数组切片 @fields[ 5 .. $#fields ] 仅访问要更改的元素。

#!/usr/bin/perl
use warnings;
use strict;

my @input = qw( aoipp;dadada.12312;ss;1245454;Xiop;12.12;45.3;47.897;31.5;
                asdfafd;14355.54664;peasd;125.1;900.2;76.897;67.456;asdfdf;
                perio;777.2;ipoes;900.34;2;1980.45;870.98;67.67;
              );

my @expected = qw( aoipp;dadada.12312;ss;1245454;Xiop;12,12;45,3;47,897;31,5;
                   asdfafd;14355.54664;peasd;125.1;900.2;76,897;67,456;asdfdf;
                   perio;777.2;ipoes;900.34;2;1980,45;870,98;67,67;
                 );

sub process {
    my (@input) = @_;
    my @output;
    for my $line (@input) {
        my @fields = split /;/, $line;
        s/\./,/ for @fields[ 5 .. $#fields ];
        push @output, join ';', @fields, q();
    }
    return \@output
}

use Test::More tests => 1;
is_deeply(process(@input), \@expected);

【讨论】:

    【解决方案3】:
    while (my $line = <DATA>) {
        if ($line =~ /^(?:[^;]*;){5}/) {
            substr($line, $+[0]) =~ y/./,/;
        }
        print $line;
    }
    __DATA__
    aoipp;dadada.12312;ss;1245454;Xiop;12.12;45.3;47.897;31.5;
    asdfafd;14355.54664;peasd;125.1;900.2;76.897;67.456;asdfdf;
    perio;777.2;ipoes;900.34;2;1980.45;870.98;67.67;
    

    【讨论】:

      【解决方案4】:
      perl -pe 's/(.*?;){6}\K(.*)/$2 =~ s!\.!,!rg /ge'
      
      1. 跳过所有内容直到 6 号 ; ((.*?;){6}\K),
      2. 并应用替换。 , 到线路的其余部分 ($2 =~ s!\.!,!rg)

      【讨论】:

        【解决方案5】:
          # this should do your work
          sed -i 's/;/,/6g' filename
        
          cat filename
          aoipp;dadada.12312;ss;1245454;Xiop;12.12,45.3,47.897,31.5,
          asdfafd;14355.54664;peasd;125.1;900.2;76.897,67.456,asdfdf,
          perio;777.2;ipoes;900.34;2;1980.45,870.98,67.67,
        

        【讨论】:

        • 我不需要替换 ';'s(分号)我需要替换 '.' (句号)在第五次出现 ';' 之后加上 ','(逗号)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-06-20
        • 2019-12-26
        • 2021-04-26
        • 1970-01-01
        • 2011-05-17
        • 1970-01-01
        相关资源
        最近更新 更多