在perl中处理替换的反向引用

时间:2012-09-07 14:24:20

标签: regex perl

作为尝试用十进制数替换科学数字的一部分,我想将反向引用保存到字符串变量中,但它不起作用。

我的输入文件是:

,8E-6,
,-11.78E-16,
,-17e+7,

然后我运行以下内容:

open FILE, "+<C:/Perl/input.txt" or die $!;
open(OUTPUT, "+>C:/Perl/output.txt") or die;

while (my $lines = <FILE>){

  $find = "(?:,)(-?)(0|[1-9][0-9]*)(\.)?([0-9]*)?([eE])([+\-]?)([0-9]+)(?:,)";
  $noofzeroesbeforecomma = eval("$7-length($4)");
  $replace = '"foo $noofzeroesbeforecomma bar"';

  $lines =~ s/$find/$replace/eeg;
  print (OUTPUT $lines);
}

close(FILE);

我得到了

foo  bar
foo  bar
foo  bar

我希望

foo 6 bar
foo 14 bar
foo 7 bar

$noofzeroesbeforecomma似乎是空的或不存在的。

即使进行了以下调整,我也会得到一个空结果

$noofzeroesbeforecomma = $2;

只在替换字符串中直接插入$2会给我一些东西(不幸的是,这不是我想要的)。

有人可以帮忙吗?

我在64位Windows 7机器上运行Strawberry Perl(5.16.1.1-64bit),对Perl缺乏经验

4 个答案:

答案 0 :(得分:1)

您的主要问题是没有使用

use strict;
use warnings;

warnings本来会告诉你的

Use of uninitialized value $7 in concatenation (.) or string at ...
Use of uninitialized value $4 in concatenation (.) or string at ...

我建议你尝试找一个可以处理科学记数法的模块,而不是试图破解你自己的模块。

您的代码在工作顺序中可能看起来像这样。如您所见,我在您的eval字符串周围添加了q(),以避免在$7$4存在之前对其进行评估。我也删除了eval本身,因为虽然eval上的双eval有点过分。

use strict;
use warnings;

while (my $lines = <DATA>) {
    my $find="(?:,)(-?)(0|[1-9][0-9]*)(\.)?([0-9]*)?([eE])([+\-]?)([0-9]+)(?:,)";
    my $noof = q|$7-length($4)|;
    $lines =~ s/$find/$noof/eeg;
    print $lines;
}


__DATA__
,8E-6,
,-11.78E-16,
,-17e+7,

<强>输出:

6
14
7

作为旁注,不使用strict就是在寻找麻烦。在使用$noofzeroesbeforecomma之类的变量名称时执行此操作需要两倍的麻烦,因为它很容易拼写错误。

答案 1 :(得分:0)

这不是反向引用,而是原始问题,从科学记数法转换数字。我确信在某些情况下会失败:

#!/usr/bin/env perl

use strict;
use warnings;
use bignum;

for (<DATA>) {
    next unless /([+-]?\d+(?:\.\d+)?)[Ee]([+-]\d+)/;
    print $1 * 10 ** $2 . "\n";
}

__DATA__
,8E-6,
,-11.78E-16,
,-17e+7,

输出:

0.000008
-0.000000000000001178
-170000000

答案 2 :(得分:0)

我建议您使用Regexp::Common::number模块的Regexp::Common插件,它会为您找到所有实数,并允许您替换那些带有指数标记的模块

此代码显示了这个想法。使用-keep选项使模块将每个组件放入$N变量之一。指数标记 - eE - 位于$7中,因此可以根据是否存在来转换数字

use strict;
use warnings;

use Regexp::Common;

my $real_re = $RE{num}{real}{-keep};

while (<>) {
  s/$real_re/ $7 ? sprintf '%.20f', $1 : $1 /eg;
  print;
}

<强>输出

根据您的示例输入,此代码生成以下内容。可以使用替换

中的其他代码进一步整理这些值
,0.00000800000000000000,
,-0.00000000000000117800,
,-170000000.00000000000000000000,

答案 3 :(得分:0)

问题在于Perl可以处理所有这些类型的表达式。由于Perl中的标准数据项是字符串,因此您只需捕获表达式即可使用它。所以,拿这个表达式:

/(-?\d+(?:.\d+)?[Ee][+-]?\d+)/

从周围文本中提取它并使用sprintf格式化它,就像Borodin所示。

然而,如果它可以帮助您更好地了解您尝试做的事情,那么效果会更好

my ( $whole, $frac, $expon )
    = $line =~ m/(?:,)-?(0|[1-9]\d*)(?:\.(\d*))?[eE]([+\-]?\d+)(?:,)/
    ;
my $num = $expon - length( $frac );
  • 为什么不用指数来捕捉符号,如果你打算用算术算法呢?

  • 最好为捕获命名,并在没有必要时避开eval

  • 替换 - 原样 - 没有多大意义。

  • 实际上,由于符号或数字都不区分大小写,因此只需在开头添加(?i),并避免使用E“字符类”[Ee]

    /((?i)-?\d+(?:.\d+)?e[+-]?\d+)/