在perl中打印$ 1 = ~s / - //

时间:2012-01-01 05:56:48

标签: regex perl

这会导致错误“修改在...尝试的只读值”

$mfgs = "AUDBMWCH-FO-TOY";
while( $mfgs =~ /(.{3})/g ) {
    print $1 =~ s/-// . "\n";
}

如何在不添加额外行的情况下执行此操作?到目前为止,我可以告诉Perl没有内置的str_replace()函数。我可以写一个,但正如我所说,我试图找出如何做到这一点,没有额外的代码行。

这不是在实际项目中使用。这仅用于学习目的。



6 个答案:

答案 0 :(得分:6)

让我们退后一步

$mfgs = "AUDBMWCH-FO-TOY";
while( $mfgs =~ /(.{3})/g ) {
    print $1 =~ s/-// . "\n";
}

只是一次读取$mfgs字符串三个字符并删除连字符。

可以重新编写以下内容:

say for map { s|-||r }  $mfgs =~ /.../g ; # Works in Perl 5.14+

use List::MoreUtils 'apply';
say for apply { s|-|| } $mfgs =~ /.../g ; # If that 'r' flag isn't there

或使用音译操作符(tr///)看到没有任何固有的regexy正在进行:

say for map tr!-!!dr , $mfgs =~ /.../g ;

如果三个字符的块中最多只有一个连字符,则两种方式都会产生相同的结果。这是因为tr/-//dr会删除所有连字符,而s/-//r只会删除第一次出现的连字符。


这回答了人们如何做到这一点,所以让我们看看为什么它在

之前无效

为什么不能修改$1

根据perldoc perlvar(强调补充):

  

<强> $<digits> ($1, $2, ...)

     

包含相应捕获集的子模式   来自上一次成功模式匹配的括号,不计算在内   已经退出的嵌套块中匹配的模式。

     

这些变量是 只读 且动态范围。

换句话说,$1无法修改,这是s///试图做的事情。

但是,$1 的副本可以进行修改,Jonathan Leffler's solution会对此进行修改。

答案 1 :(得分:3)

我不确定您为什么要这样做 - 数据结构与以下相比是次优的:

my @mfgs = ( "AUD", "BMW", "CH", "FO", "TOY" );

然而,这有效:

use strict;
use warnings;
my($mfgs, $x) = ("AUDBMWCH-FO-TOY");
while ($mfgs =~ /(.{3})/g)
{
    printf "%s\n", ($x = $1, $x =~ s/-//, $x);  
}

如果没有限制或警告,您可以使用:

my $mfgs = "AUDBMWCH-FO-TOY";
while ($mfgs =~ /(.{3})/g)
{
    printf "%s\n", ($x = $1, $x =~ s/-//, $x);
}

但是,最好不要费心学习如何在没有use strict;use warnings;(或use diagnostics;)的情况下使用Perl。

答案 2 :(得分:3)

在Perl 5.14中,有一个新的/r修饰符可用于s///替换(以及tr/// / y///音译),这会导致返回的结果为一个新的字符串,不会改变原文。

use 5.014;

$mfgs = "AUDBMWCH-FO-TOY";
while ($mfgs =~ /(.{3})/g) {
    say $1 =~ s/-//r;
}

答案 3 :(得分:2)

print( do { ( my $x = $1 ) =~ s/-//; $x }, "\n" );
print( ( apply { s/-// } $1 ), "\n" );
say( do { ( my $x = $1 ) =~ s/-//; $x } );  # 5.10+
say( apply { s/-// } $1 );                  # 5.10+
say( $x =~ s/-//r );                        # 5.14+

apply来自List::MoreUtils

答案 4 :(得分:2)

  

如何在不添加额外行的情况下执行此操作?据我所知   Perl没有内置的str_replace()函数。我可以   写一个,但正如我所说,我试图弄清楚如何做到这一点   没有额外的代码行。

您是否阅读过perlfunc?此外,这不是关于函数,它是$1是只读变量。请参阅perldoc perlvar

$<digits> ($1, $2, ...)
  

包含相应捕获集的子模式   来自上一次成功模式匹配的括号,不计算在内   已经退出的嵌套块中匹配的模式。

     

这些变量是只读的和动态范围的。


尽我所知,这是实现相关代码的最经济方式:

tr/-//d, say for $mfgs =~ /.{3}/g;

或长版:

my $mfgs = "AUDBMWCH-FO-TOY";
for my $str ($mfgs =~ /.{3}/g) {
    $str =~ tr/-//d;
    say $str;
}

请注意,在这种情况下不需要捕获括号。来自perldoc perlop

  

/ g修饰符指定全局模式匹配 - 即匹配   字符串中尽可能多的次数。它的表现取决于   上下文。在列表上下文中,它返回子字符串的列表   与正则表达式中的任何捕获括号匹配。如果   没有括号,它返回所有匹配的列表   字符串,好像整个模式周围都有圆括号。

另请注意whilefor (foreach)之间的区别。 while将在匹配时迭代(在标量上下文中使用/g),隐式使用\G断言,但不会将匹配存储在变量中(除了{ {1}})。 $1会一次提取所有匹配项(在列表上下文中使用for),匹配项将在循环变量(/g中显示为第一种情况,$_在第二个)。

  

在标量上下文中,m // g的每次执行都会找到下一个匹配项,   如果匹配则返回true,如果没有进一步匹配则返回false。   可以使用pos()读取或设置最后一次匹配后的位置   功能;见pos。失败的匹配通常会重置搜索位置   到字符串的开头,但你可以通过添加来避免这种情况   / c修饰符(例如m // gc)。修改目标字符串也会重置   搜索位置。

答案 5 :(得分:0)

您可以随时执行此操作以获取由分隔符分割的项目数组。

@a = split("-", $mfgs);

要str_replace,请使用

$var =~ s/search/replace/g;

喜欢这样

$mfgs =~ s/-/\n/g;
printf("%s\n", $mfgs);