在 perl 中操作反向引用以进行替换
Posted
技术标签:
【中文标题】在 perl 中操作反向引用以进行替换【英文标题】:Manipulating backreferences for substitution in perl 【发布时间】:2012-09-07 14:24:20 【问题描述】:作为尝试用十进制数字替换科学数字的一部分,我想将反向引用保存到字符串变量中,但它不起作用。
我的输入文件是:
,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-64 位),并且对 Perl 非常缺乏经验
【问题讨论】:
在使用匹配或替换运算符来设置它们之前,您先使用$4
和$7
。
对不起,我误会了吗?您的第二个输入行是-11.78E-16
。如果您尝试捕获指数 _sans 符号_您是否希望在靠近底部的所需输出中看到 16
而不是 14
中的 foo 14 bar
?
【参考方案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 ...
我建议您尝试找到一个可以处理科学记数法的模块,而不是尝试破解您自己的模块。
您的代码在工作顺序中可能看起来像这样。如您所见,我在您的评估字符串周围放置了一个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
之类的变量名时这样做会带来两倍的麻烦,因为很容易出现拼写错误。
【讨论】:
我认为“不使用严格就是自找麻烦”说明了一切 @Borodin 双重麻烦。 我还建议了解正则表达式运算符上的x
标志。这使得在正则表达式中使用换行符和 cmets 成为可能,这对复杂的正则表达式有很大帮助。【参考方案2】:
这不是关于反向引用,而是原始问题,将数字从科学记数法转换。我敢肯定在某些情况下会失败:
#!/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
【讨论】:
【参考方案3】:我建议您使用Regexp::Common
模块的Regexp::Common::number
插件,它会为您找到所有实数并允许您替换具有指数标记的那些
这段代码显示了这个想法。使用-keep
选项使模块将每个组件放入$N
变量之一。指数标记 - e
或 E
- 在 $7
中,因此可以根据是否存在来转换数字
use strict;
use warnings;
use Regexp::Common;
my $real_re = $REnumreal-keep;
while (<>)
s/$real_re/ $7 ? sprintf '%.20f', $1 : $1 /eg;
print;
输出
根据您的示例输入,此代码生成以下内容。可以使用替换中的附加代码进一步整理这些值
,0.00000800000000000000,
,-0.00000000000000117800,
,-170000000.00000000000000000000,
【讨论】:
【参考方案4】:问题在于 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+)/
【讨论】:
以上是关于在 perl 中操作反向引用以进行替换的主要内容,如果未能解决你的问题,请参考以下文章