【问题标题】:Perl regex find and replacePerl 正则表达式查找和替换
【发布时间】:2011-08-01 18:54:27
【问题描述】:

我是 perl 的新手,我正在尝试找出查找和替换的方法。我有一个大的 csv 文件(实际上是用分号分隔的)。文件中的某些数字(整数和小数)在数字后有一个负号。我需要将负号移到数字之前。

例如:改变

ABC;10.00-;XYZ

ABC;-10.00;XYZ

我不确定如何在 perl 中执行此操作。有人可以帮忙吗?

问候, 阿南德

【问题讨论】:

    标签: regex perl replace substitution


    【解决方案1】:

    除非我对我的数据和正则表达式非常确定,否则我不会涉足带有正则表达式的大型 csv 文件。在我看来,使用 CSV 模块是最安全的方式。

    此脚本将输入文件作为参数,并使用 .new 扩展名写入更正后的文件。

    如果您发现输出文件发生了不希望的更改,可以尝试取消注释 keep_meta_info 行。

    use strict;
    use warnings;
    use autodie;
    use Text::CSV;
    
    my $out_ext = ".new";
    my $csv = Text::CSV->new( { 
            sep_char => ";",
            #   keep_meta_info => 1,
            binary => 1,
            eol => $/,
        } ) or die "" . Text::CSV->error_diag();
    
    for my $arg (@ARGV) {
        open my $input, '<', $arg;
        open my $output, '>', $arg . $out_ext;
        while (my $row = $csv->getline($input)) {
            for (@$row) {
                s/([0-9\.]+)\-$/-$1/;
            }
            $csv->print($output, $row);
        }
    }
    

    【讨论】:

      【解决方案2】:

      我假设您不必担心在分隔文件中引用或转义。我将从标准输入/输出中读取,如果需要,请更改为适当的文件

      while( my $line = <STDIN> )
      {
          chop( $line );
          my @rec = split( ';', $line );
          map( s/^(\d*\.?\d+)\-$/-$1/, @rec );
          print join(';',@rec) . "\n";
      }
      

      如果您确实需要担心转义和引用,请使用Text::CSV_XS 而不是&lt;STDIN&gt;splitjoin 操作

      【讨论】:

      • chop 应该是chomp。您可能希望在 csv 文件上使用 split 时要小心,因为它可能会导致对文件进行不必要的更改。来自perldoc perlfuncBy default, empty leading fields are preserved, and empty trailing ones are deleted.
      【解决方案3】:

      一般情况下,替换命令为s/old/new/flags:

      s/(           # start a capture group
          \d+       # first part of the number
          (\.\d+)?  # possibly a decimal dot and the fractional part
        )-          # end capture group, match the minus sign
       /-$1/gx      # move minus to the front
      

      g 标志表示“全局”(替换所有出现),x 表示“扩展易读性”(允许模式中的空格和 cmets)。您必须在数据上测试表达式以查看您可能遗漏了哪些极端情况,通常需要几次迭代才能获得正确的情况。样品:

      $ echo "10.5-;10-;0-;a-" | perl -pe 's/(\d+(\.\d+)?)-/-$1/g'
      -10.5;-10;-0;a-
      

      另见perldoc perlop(搜索“替换”以跳转到右侧部分)。

      【讨论】:

      • 这不会搞砸日期吗?例如。 2011-01-01 会变成-2011-0101
      • 是的,这很有可能。这就是为什么我说我总是先尝试真实数据的模式,看看我可能错过了什么。使用简单的正则表达式替换进行多次迭代 IMO 通常比寻求更通用的解决方案更容易。
      • 除非它是一个大文件,就像OP所说的那样,在这种情况下你需要相当小心。不过你是对的,因为我们无法像 OP 自己那样调整代码。
      • 例如,在开头和结尾添加分号的前瞻可能是谨慎的。
      • 是的,这是个好主意。 “大”文件并不真正意味着“复杂”。文件可能是某个测量值或其他什么的转储文件,格式非常简单,只有很多数据。但我想我们是互相理解的,剩下的就在海报上弄清楚了。
      猜你喜欢
      • 2016-10-29
      • 1970-01-01
      • 1970-01-01
      • 2013-08-22
      • 1970-01-01
      • 1970-01-01
      • 2015-03-25
      • 2011-06-16
      相关资源
      最近更新 更多