【问题标题】:Search/replacement in string在字符串中搜索/替换
【发布时间】:2017-01-10 15:14:03
【问题描述】:

我有一个 Perl 程序,用户在其中输入一些文本、搜索模式和替换字符串。

我正在使用s/// 运算符将搜索模式替换为替换字符串,但在这种情况下,如果用户输入捕获变量(如$1)或反斜杠转义(如\u 或@987654326 @) 在替换字符串中,替换模式应该处理这些元字符,而是将它们视为字符串字符。

我有以下代码:

#!/usr/bin/perl -w

use strict;

chomp(my $text =    <STDIN>);  #read the text
chomp(my $regex =   <STDIN>); #read the search pattern
chomp(my $replace = <STDIN>); #read the replacement pattern

$text=~s/$regex/$replace/g;  # do replacement

print $text,"\n";

此代码的示例输入是

fred flintstone and wilma flintstone are good couples
(fred|wilma) flintstone
\u\L$1\E Flintstone

上面代码的输出是

\u\L$1\E Flintstone and \u\L$1\E Flintstone are good couples

我在下面的代码中找到了一种纠正方法:

#!/usr/bin/perl -w

use strict;

chomp(my $text    = <STDIN>);
chomp(my $regex   = <STDIN>);
chomp(my $replace = <STDIN>);

$replace = '"' . $replace . '"';   # creating the string as '"\u\L$1\E Flintstone"'

$text = ~s/$regex/$replace/gee; # applying double evaluation

print $text,"\n";

现在这段代码给出了正确的输出

Fred Flintstone and Wilma Flintstone are good couples

我想知道是否有更好的方法来解决这个问题?

【问题讨论】:

标签: regex perl search replace


【解决方案1】:

(首先,您不应该再使用 -w。它已在 2000 年被(词法范围,更可预测)use warnings pragma 取代。)

对于您的问题,您可以使用Data::Munge 模块中的replace,它是“javascript 的String.prototype.replace 的克隆”。

use Data::Munge qw(replace);

$text = replace($text, $regex, $replace, 'g');

这将扩展诸如 $&amp; 或 $1 之类的内容,但不会扩展诸如 \u 之类的反斜杠序列。为此,您可以指定自己的扩展函数,但是您必须手动解析和替换替换字符串中的特殊序列。对于$1 和朋友来说,这很容易,但是像\Ufoo\Q$1\Ebar\Ebaz 这样的东西很难正确处理,特别是如果$1 包含'\E'(甚至perl 本身在历史上也有这方面的问题)。但如果你让那部分工作,很容易插入replace。

【讨论】:

    【解决方案2】:

    双引号字符串不编译就不能插值

    /ee 修饰符带有与eval 相同的警告:执行用户输入的任何类型的代码总是很危险的,即使是简单的字符串也可能包含由用户喜欢的任何 Perl 代码组成的块输入,如"my dangerous string @{ unlink glob '*.*'}"

    我建议您使用出色的String::Interpolate 模块,它导出一个safe_interpolate 函数,该函数将使用Safe 模块在“安全”隔间中执行插值

    #!/usr/bin/perl
    
    use strict;
    use warnings 'all';
    
    use String::Interpolate 'safe_interpolate';
    
    my $text =    'fred flintstone and wilma flintstone are good couples';
    my $regex =   '(fred|wilma) flintstone';
    my $replace = '\u\L$1\E Flintstone';
    
    $text =~ s/$regex/ safe_interpolate($replace) /eg;  # do replacement
    
    print $text,"\n";
    

    【讨论】:

      猜你喜欢
      • 2010-11-14
      • 1970-01-01
      • 1970-01-01
      • 2018-01-02
      • 2020-01-06
      • 2017-04-16
      • 2014-02-11
      相关资源
      最近更新 更多