【问题标题】:print $1 =~ s/-// in perl在 perl 中打印 $1 =~ s/-//
【发布时间】:2012-01-02 00:53:01
【问题描述】:

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

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

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

这并没有在实际项目中使用。这仅用于学习目的。



【问题讨论】:

  • 你能解释一下你不想添加另一行代码的限制吗?如果没有这样的约束,这显然是微不足道的。
  • @GregHewgill,真的没有限制。刚刚学习 Perl,从我目前看到的情况来看,似乎必须有一种简单的方法来做到这一点。

标签: regex perl


【解决方案1】:

让我们退后一步

$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///) 看到没有任何固有的正则表达式:

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

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


这回答了人们如何才能做到这一点,所以让我们看看为什么它以前不起作用

为什么$1不能修改?

根据perldoc perlvar(强调添加):

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

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

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

换句话说,$1 不能被修改,这是 s/// 试图做的。

但是,$1 的副本可以修改,这在Jonathan Leffler's solution 中有所涉及。

【讨论】:

    【解决方案2】:

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

    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;)生效的情况下(ab)使用 Perl。

    【讨论】:

    • 你的第三个代码块是迄今为止我见过的最好的。如果它在使用 use strict; 时有效,那么它正是我想要的。
    • 虽然在printf 列表中玩弄$x 很可爱,但它看起来相当危险且难以辨认,而且并不比简单地执行my $x = $1; $x =~ s/-//g; say $x; 更有效。
    • 我不主张任何形式的好风格;这是可恶的风格。它确实实现了提问者想要实现的目标,这是它唯一的防御措施。
    • @JonathanLeffler 同意。但是,最好记下它是如何以及为什么不是好的样式,然后提供一个解决方案。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:
      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

      【讨论】:

      • @druciferre,do 允许多个语句,而不是 $x 的声明。后者可以在没有do 的情况下完成。
      【解决方案5】:

      如何在不添加额外行的情况下执行此操作?据我所知 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 除外)。 for 将一次提取所有匹配项(在列表上下文中使用/g),并且匹配项将在循环变量中使用别名(第一种情况下为$_,第二种情况下为my $str)。

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

      【讨论】:

      • 不幸的是split 没有返回与$mfgr =~ /.../g 相同的数组。它返回("", "AUD", "", "BMW", "", "CH-", "", "FO-", "", "TOY")
      • @Zaid 你是对的。不知道我在想什么。除夕夜的第二天。
      【解决方案6】:

      你总是可以这样做来得到一个由你的分隔符分割的项目数组..

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

      要使用 str_replace

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

      像这样

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

      【讨论】:

      • 本例中没有分隔符,每三个字符是使用的。这不是一段功能代码,它仅用于学习目的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-26
      • 1970-01-01
      • 1970-01-01
      • 2013-08-18
      • 2023-01-10
      • 2010-09-16
      相关资源
      最近更新 更多