【问题标题】:In Perl, how can I use `s///` to make multiple replacements?在 Perl 中,如何使用 `s///` 进行多次替换?
【发布时间】:2019-05-14 17:09:25
【问题描述】:

我的意见:

 $brackets = '\left \right \center \right \left \middle';

在上面的字符串中,我需要将每个\left 字符串替换为( 左大括号,并将每个\right 字符串替换为) 右大括号。

我可以用几种方法替换来获得输出。但是我想知道我们如何在r 修饰符中做到这一点。这是我目前所拥有的

 $brackets=~s{\\(left|right)}{$&=~s/\\left/\(/r && $&=~s/\\right/\)/gr; }ge;

我不知道我在哪里可以找到我的错误。

【问题讨论】:

  • Perl 大师在这里相信你不应该“把所有东西都塞进一个正则表达式”。不过,总而言之,你的问题很好。
  • 在另一个 s/// 的替换部分中再放两个 s/// 简直令人作呕。
  • “魔术正则表达式”是我们经常看到的东西——有人试图“编写”一个正则表达式来做所有事情。并且正则表达式很强大,所以你通常可以。它只是......也非常难以理解,并且难以阅读和维护,所以通常你应该做的是分解问题并围绕它编写“真正的”程序代码。
  • 如果你只需要处理(和)替换,你可以使用s/\\((left)|right)/$2 ? '(' : ')'/eg
  • 让您了解为什么您的正则表达式不起作用:/r 修饰符返回新修改(或未更改)的字符串(此值在此处始终为 true),因此您的 && 返回其右侧:s/\\right/\(/,表示左侧绝对没有效果。你可以做($&=~s/\\right/)/r)=~s/\\left/(/r。但请看答案:用一个复杂的正则表达式做所有事情是个坏主意。

标签: regex perl


【解决方案1】:

对于这个只涉及要替换的两个子字符串的字面问题,您应该使用两个单独的s/// 作为@Patrick85 recommends。只有当您有 3 个或更多这样的替换时,下面的方法才开始有意义。在这种情况下,将映射隐藏在散列中将使您的代码更清晰。

使用哈希查找替换:

#!/usr/bin/env perl

use strict;
use warnings;

my %replacement = (
    '\left' => '(',
    '\right' => ')'
);

my $s = '\left \right \center \right \left \middle';
my $t = $s =~ s/( \\ (?: left | right ))/$replacement{$1}/rgx;

print "$s\n$t\n";

试图将所有内容都塞进一行会有很多问题:

$brackets=~s{\\(left|right)}{$&=~s/\\left/\(/r && $&=~s/\\right/\)/gr; }ge;

很难阅读。此外,无法一概而论。如果您需要将 N 个字符串映射到它们的替换,会发生什么?让我们尝试通过添加一些空格来更清楚地写下这段代码:

$brackets =~ s{
    \\
    (left | right)
}{
    $& =~ s/\\left/\(/r &&
    $& =~ s/\\right/\)/gr; 
}gex;

即使这符合您的要求,您现在也已将单个 s/// 与简单的哈希查找替换为对源字符串中的每个匹配项执行两个额外的 s/// 操作的东西。

对于您显示的示例字符串,这个字符串将执行 八 个额外的 s/// 操作。

这样不好。

【讨论】:

  • 不错的正则表达式:)。你能解释一下你的正则表达式吗?因为我不理解你的洞正则表达式,但我想理解它。 'rx' 标志是什么?而这部分'(\\(?:左|右))'让我感到困惑
  • 这个正则表达式:my $t = $s =~ s/(\(left|right))/$replacement{$1}/rg;也有效。现在我只需要知道标志 'x' 的作用以及为什么使用 '?:'
  • x 告诉正则表达式引擎忽略空格(包括换行符)。它使格式化模式更容易。
  • 可以去掉一些冗余代码:use Data::Munge qw(list2re); ... my $pattern = list2re keys %replacements; my $t = $s =~ s/($pattern)/$replacements{$1}/gr;
【解决方案2】:

使用两个简单的替换而不是一个复杂的替换:

#!/usr/bin/env perl
use strict;
use warnings;

my $brackets = '\left \right \center \right \left \middle';

$brackets =~ s/\\left/(/g;
$brackets =~ s/\\right/)/g;

print $brackets."\n";

输出:

( ) \center ) ( \middle

【讨论】:

  • 请浏览我的问题中突出显示的行。
  • @ssr1012 老实说,你为什么不想这样做呢?这是正确的、显而易见的方法。它比您问题中的正则表达式更清晰,代码也更短。为什么换行符的存在让你如此困扰?
  • $replacement 你从@Sinan Ünür 复制了文本。
  • 对于这个字面问题,我建议只使用两个s///。仅当您有三个或更多替换时,使用外部查找表才开始有意义。另见How do I strip blank space from the beginning/end of a string?。一对简单的s///胜过一个复杂的。
【解决方案3】:

建立一个替换列表相当容易。

use strict;
use warnings;

my $brackets = '\left \right \center \right \left \middle';

for ( $brackets ) {
    s/\\left/(/g;
    s/\\right/)/g;
}

print $brackets, "\n";

显然你仍然可以添加到列表中并删除空格。

More information about this method:

【讨论】:

  • 我似乎很清楚你不明白 for() 块的作用,因此你为什么要问“这里发生了什么”
  • @dan1111: “这里发生了什么,为什么人们会支持它?” 这更多地表明你对 Perl 缺乏熟悉,而不是质量作者的代码。查看Switch Statements in perldoc perlsyn,您将阅读“foreach 是设置主题化器的非实验性方法”。 (实验方法是 given。)本着 TMTOWTDI 的精神,我认为这个答案非常好,唯一的缺点是它在很大程度上是对旧解决方案的重写。
  • @Borodin。感谢您的投入。非常感谢。
  • @Dads:它被称为 topicaliser。阅读我笔记中的链接到 dan1111。
【解决方案4】:

只是我需要关闭这个我的问题。 @Wiktor Stribiżew [in Comment] 就我的问题提供了答案。

$brackets = '\left \right \center \right \left \middle';

我的要求:

$brackets=~s{\\(left|right)}{$&=~s/\\left/\(/r && $&=~s/\\right/\)/gr; }ge;

答案:

$brackets=~s/\\((left)|right)/$2 ? '(' : ')'/eg;

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多