Perl: 替换字符串中的命名捕获组默认值

5
以下Perl代码,在第二次尝试解引用捕获组"ext"时失败。
use strict;
use warnings;

foreach(qw/2.1 2/){
#foreach(@ARGV){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?:(?<ext>\.[1-9]?)|\.0)0([^\w]|$)/$+{'ext'}/g;
    print ":".$out.":\n";
}

如果未定义,我如何在替换字符串中设置捕获组的默认值?

我相信有几种解决此问题的其他方法,但是不能为捕获组设置默认值肯定会再次出现 - 所以请帮我解决这个问题。

后续:

..在ikegami的建议下,我已经成功使其工作,所以它的读法是:

$out =~ s{(?:(?<ext>\.[1-9]?)|\.0)0([^\w]|$)}{ $+{'ext'} // "" }eg;

...还有其他办法吗? 尤其是因为这只适用于Perl的“e”正则表达式功能。 标准的正则表达式方式应该也会提到这一点,至少在第一次未捕获时忽略捕获组解引用,不是吗?


关于“还有其他方法吗?”,没有其他方法可以“为捕获组使用默认值”。你必须运行代码。没有代码,计算机程序无法执行任何操作。你想要对每个替换都运行这段代码,而e就是用来运行代码生成替换结果的方式。 - ikegami
关于“难道没有其他方法吗?”,在“使用默认值来捕获组”方面确实没有其他方法。你必须运行代码。没有代码,计算机程序是无法执行任何操作的。你想要为每个替换运行这段代码,而e就是用来运行代码生成替换结果的方式。 - ikegami
关于“难道没有其他办法吗?”,没有其他方法可以“使用默认值来捕获组”。你必须运行代码。没有代码,计算机程序就无法执行任何操作。你想要运行这段代码来进行替换,而e就是你运行代码生成替换结果的方式。 - undefined
你在尝试做什么?为什么要使用//e呢?你到底想要替换什么?那里的捕获组2的目的是什么? - sln
你在尝试做什么?为什么要使用 //e ?你到底想要替换什么?那里的捕获组2的目的是什么? - sln
你在做什么?为什么要使用//e?你到底想要替换什么?那里的捕获组2的目的是什么? - undefined
2个回答

6

使用e修饰符将替换部分作为表达式。

s{...}{ $+{ext} // "default" }eg

然而,您所尝试实现的目标并不需要使用捕获组,更不需要条件捕获组和默认值。
看起来您想要去除所有尾部的零,并且可能还有尾部的句点.。为此,您可以使用以下简单的代码:
my $out = sprintf( "%.2f", $_ ) =~ s/0+\z//r =~ s/\.\z//r;

另一种看待这个问题的方式是只有两个子字符串可以被移除:结尾处的.00,或者结尾处的0。这给出了以下解决方案:
my $out = sprintf( "%.2f", $_ ) =~ s/\.00\z|0\z//r;

1
关于“这是一个仅适用于Perl的解决方案”,问题本身也是如此(使用$+{ext}时会发出未定义警告)。再次强调,任何解决方案都将与特定语言相关。因为正则表达式无法做任何事情。而你想要发生某些事情。/// 关于“我把这个问题交给sin处理了”,他并没有设置默认值。他只是提供了一种不太优雅的解决方法。你刚才说你不想要一个优雅的解决方案,它不提供默认值,所以你接受一个不那么优雅的解决方案却不提供默认值,这有点奇怪。 - ikegami
1
关于“这是一个仅适用于Perl的解决方案”,问题也是如此(使用$+{ext}时会发出未定义警告)。再次强调,任何解决方案都将与特定语言相关。因为正则表达式无法做任何事情。而你希望某些事情发生。///关于“我把这个给了sin”,它并没有设置默认值。他只是提供了一种不太优雅的解决问题的方式。你刚才说你不想要一个不提供默认值的优雅的解决方案,所以你接受一个不提供默认值的不优雅的解决方案有点奇怪。 - ikegami
1
Re "这是一个仅适用于Perl的解决方案”,问题也是如此(使用$+{ext}时会发出未定义警告)。再次强调,任何解决方案都将是特定于语言的。因为正则表达式无法做任何事情。而你想要发生某些事情。/// Re "我把这个问题交给了sin”,他并没有设置默认值。他只是提供了一种不太优雅的解决问题的方式。你刚才说你不想要一个不提供默认值的优雅的解决方案,所以你接受一个不提供默认值的不优雅的解决方案有点奇怪。 - undefined
1
@Ed Sabol,谢谢,问题已修复。 - ikegami
1
@Ed Sabol,谢谢,已修复。 - ikegami
显示剩余14条评论

2
如果你坚持要使用命名组来做这个...
不要使用e修饰符,只需在替换的上下文中定义一个空的ext
use strict;
use warnings;

foreach(qw/2.1 2/){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?:(?<ext>\.[1-9]?)|(?<ext>)\.0)0([^\w]|$)/$+{'ext'}/g;
    print ":".$out.":\n";
}

输出

:2.1:
:2:

更简单的方法是放弃命名捕获,使用分支重置
保持使用的便携性。
use strict;
use warnings;

foreach(qw/2.1 2/){
#foreach(@ARGV){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?|(\.[1-9]?)|()\.0)0([^\w]|$)/$1/g;
    print ":".$out.":\n";
}

相同的输出

:2.1:
:2:

请参考此处的被接受答案以获取详细信息:如何在Perl正则表达式替换中避免警告?

或者 no warnings qw( uninitialized ); - ikegami
你可以将其范围限定在你需要的地方。 - ikegami
是的,对我来说,这是最好的答案。分支重置和空捕获组在选择中 - 我不知道空捕获是可能的,我也不知道你可以使用相同命名的捕获组...非常感谢您的见解!谢谢! - mcaustria
是的,对我来说,这是最好的答案——分支重置,然后在选择分支中使用空捕获组。我不知道空捕获是可行的,我也不知道可以使用相同名称的捕获组......非常感谢你提供的见解!谢谢! - mcaustria
@mcaustria,我故意没有在我的答案中包含这个,因为首先它不是你所问的,其次这种方法并不是实现你想要的功能的好办法。我的答案有更简洁的解决方案。 - ikegami
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接