将正则表达式替换作为变量传递给Perl?
我需要将正则表达式替换作为变量传递:
sub proc { my $pattern = shift; my $txt = "foo baz"; $txt =~ $pattern; } my $pattern = 's/foo/bar/'; proc($pattern);
当然,这是行不通的。我尝试评估替代:
eval("$txt =~ $pattern;");
但这也不起作用。我在这里错过了什么可怕的明显事情?
解决方案
好了,我们可以使用qr //运算符对RE进行预编译。但是我们不能传递运算符(s ///)。
$pattern = qr/foo/; print "match!\n" if $text =~ $pattern;
但是,如果必须传递替换运算符,则只能传递代码或者字符串:
proc('$text =~ s/foo/bar'); sub proc { my $code = shift; ... eval $code; }
或者,代码:
proc(sub {my $text = shift; $text =~ s/foo/bar}); sub proc { my $code = shift; ... $code->("some text"); }
sub proc { my($match, $subst) = @_; my $txt = "foo baz"; $txt =~ s/$match/$subst/; print "$txt\n"; } my $matcher = qr/foo/; my $sub_str = "bar"; proc($matcher, $sub_str);
而是直接回答问题。我们可以做更多的工作,但是当我使用qr //术语而不是$ sub_str作为简单文字时,则替换了扩展的正则表达式。
我最近需要为具有某些特殊SQL类型的语句创建一个解析器(测试解析器),识别这样的行,将其分为三个类型名称:
input: datetime year to second,decimal(16,6), integer
我用来演示此脚本的脚本使用带引号的正则表达式。
#!/bin/perl -w use strict; while (<>) { chomp; print "Read: <$_>\n"; my($r1) = qr%^input\s*:\s*%i; if ($_ =~ $r1) { print "Found input:\n"; s%$r1%%; print "Residue: <$_>\n"; my($r3) = qr%(?:year|month|day|hour|minute|second|fraction(?:\([1-5]\))?)%; my($r2) = qr% (?:\s*,?\s*)? # Commas and spaces ( (?:money|numeric|decimal)(?:\(\d+(?:,\d+)?\))? | int(?:eger)? | smallint | datetime\s+$r3\s+to\s+$r3 ) %ix; while ($_ =~ m/$r2/) { print "Got type: <>\n"; s/$r2//; } print "Residue 2: <$_>\n"; } else { print "No match:\n"; } print "Next?\n"; }
我们可以争论使用诸如$ r1之类的名称。但这确实完成了工作……它不是(也不是)生产代码。
I need to pass a regex substitution as a variable
你?为什么不通过代码引用?例子:
sub modify { my($text, $code) = @_; $code->($text); return $text; } my $new_text = modify('foo baz', sub { $_[0] =~ s/foo/bar/ });
通常,当我们要将"某事做某事"传递给子例程(对于问题为"正则表达式替换")时,答案是将引用传递给一段代码。高阶Perl是一本关于该主题的好书。
eval "$txt =~ $pattern";
这变成
eval "\"foo baz\" =~ s/foo/bar/"
并且替换不适用于文字字符串。
也许我们可能会重新考虑方法。
我们想将一个正则表达式替换传递给一个函数,可能是因为该函数将从其他来源(从文件,套接字等读取)中派生要对其进行操作的文本。但是,我们正在将正则表达式与正则表达式替换混合在一起。
在表达式s / foo / bar /中,实际上我们有一个正则表达式(" / foo /")和一个替换项(" bar"),应替换该表达式所匹配的内容。到目前为止,我们尝试使用的方法都遇到了尝试使用" eval"的问题,这主要是因为表达式中特殊字符可能会干扰" eval"或者在过程中被内插(即被吞噬)评价。
因此,请尝试传递例程两个参数:表达式和替换:
eval "$txt =~ $pattern"
这种方法还有一个好处:如果正则表达式模式中没有任何特殊字符,则可以直接将其传递:
sub proc { my $pattern = shift; my $code = shift; my $txt = "foo baz"; $txt =~ s/$pattern/$code->()/e; print "$txt\n"; } my $pattern = qr/foo/; proc($pattern, sub { "bar" }); # ==> bar baz proc($pattern, sub { "\U$&" }); # ==> FOO baz
或者,如果可以,我们可以使用qr //
quoting-operator来创建一个正则表达式对象并将其作为第一个参数传递:
sub apply_regex { my $regex = shift; my $subst = shift || ''; # No subst string will mean matches are "deleted" # some setup and processing happens... # time to make use of the regex that was passed in: while (defined($_ = <$some_filehandle>)) { s/$regex/$subst/g; # You can decide if you want to use /g etc. } # rest of processing... }
最重要的是,我们确实不需要'eval'或者使用代码引用/闭包来完成此任务。这只会增加复杂性,使调试变得比实际需要的难度更大。
兰迪
s ///不是正则表达式。因此,我们不能将其作为正则表达式传递。
我不喜欢eval
,因为它非常脆弱,带有很多边框。
我认为最好采用一种类似于Javascript的方法:传递一个正则表达式(在Perl中,即" qr //")和一个用于替代的代码参考。例如,传递参数可获得与以下效果相同的效果
apply_regex('foo', 'bar');
你可以打电话
apply_regex(qr{(foo|bar)}, 'baz'); apply_regex(qr/[ab]+/, '(one or more of "a" or "b")'); apply_regex(qr|\d+|); # Delete any sequences of digits
注意,'g'修饰符实际上不是正则表达式的标志(我认为将其添加到正则表达式是Javascript中的设计错误),因此我选择将其传递给第3个参数。
一旦确定了API,就可以下一步进行实现:
s/(\w+)/\u\L/g;
让我们尝试一下:
replace($string, qr/(\w+)/, sub { "\u\L" }, 'g');
结果:
sub replace { my($string, $find, $replace, $global) = @_; unless($global) { $string =~ s($find){ $replace->() }e; } else { $string =~ s($find){ $replace->() }ge; } return $string; }
这对我来说很好。
我们说得对,我们非常接近:
print replace('content-TYPE', qr/(\w+)/, sub { "\u\L" }, 'g');
代码数量不匹配
代码数量不匹配