【问题标题】:In Perl, best way to insert a char every N chars在 Perl 中,每 N 个字符插入一个字符的最佳方法
【发布时间】:2014-10-20 20:18:13
【问题描述】:

我想在 Perl 中找到在字符串中每 N 个字符插入一个字符的最佳方法。 假设我有以下内容:

my $str = 'ABCDEFGH';

我想每两个字符插入一个空格,这样我得到:

my $finalstr = 'AB CD EF GH';

无辜的​​方式是:

my $finalstr;
while ($str =~ s/(..)//) {
    $finalstr .= $1.' ';
}

(但最后一个空格并没有让我开心。)

我们可以做得更好吗?是否可以使用单个替换模式 s///,尤其是使用相同的字符串 $str(而不是使用 $finalstr)?


下一步:做同样的事情,但在要剪切的模式之前和之后的文本(当然要保留),例如'>':

my $str       = 'blah <<ABCDEFGH>> blah';
my $finalstr1 = 'blah <<AB CD EF GH>> blah';
my $finalstr2 = 'blah << AB CD EF GH >> blah';  # alternate

【问题讨论】:

  • 嗨,这种问题属于 SO。 (我已将其标记为版主注意,因此他们会尽快将其迁移到那里,请不要在那里重复发布,请稍等)

标签: algorithm perl string-matching


【解决方案1】:

使用positive lookahead and lookbehind assertions插入空格:

my $str = 'ABCDEFGH';

$str =~ s/..\K(?=.)/ /sg;

use Data::Dump;
dd $str;

输出:

"AB CD EF GH"

限制翻译的增强

如果您只想将此修改应用于字符串的一部分,请将其分成几个步骤:

my $str = 'blah <<ABCDEFGH>> blah';

$str =~ s{<<\K(.*?)(?=>>)}{$1 =~ s/..\K(?=.)/ /sgr}esg;

use Data::Dump;
dd $str;

输出:

"blah <<AB CD EF GH>> blah"

【讨论】:

  • 完成第 1 项工作,小心不要在字符串末尾插入空格,谢谢。当要处理的字符串部分在'>'内时,它是否也适用?如何根据这些模式启动和停止这种替换?
  • 最简单的解决方案是将工作分成两个步骤。
  • 嵌套字符串翻译...哇,我刚刚学习了 Perl 的特殊功能 :) 感谢您的聪明回答。
  • @RichardHuxton \K 最好记录在 perlre - Extended Patterns 中。只需搜索 \K,因为它位于 Look-Around Assertions 小节中
  • @Teuxe 注意 /r Modifier 需要 perl v5.14 或更高版本。尽管在早期的 perl 版本中使用 s{(...)}{ (my $text = $1) =~ s/.../.../; $text} 也可以获得相同的效果
【解决方案2】:

使用替换的最佳解决方案可能是s/\G..\K/ /sg。为什么?

  • \G 锚定在字符串的当前“位置”。该位置是最后一个匹配结束的位置(通常设置为字符串的开头。如果有疑问,请设置pos($str) = 0)。因为我们使用了/g 修饰符,这将是之前替换结束的地方。
  • .. 匹配任意两个字符。请注意,我们还使用了/s 修饰符,它使. 真正匹配任何字符,而不仅仅是[^\n] 字符类。
  • \K 将正则表达式的前一部分视为后视,在将被替换的子字符串中不包括先前匹配的字符串部分。所以\G..\K匹配两个任意字符后的零长度字符串。
  • 我们将零长度字符串替换为一个空格。

我会让正则表达式引擎处理替换,而不是手动附加$1 . " "。此外,我的后视解决方案避免了使用像 $1 这样的捕获的成本。

【讨论】:

  • 对于第 1 点几乎没问题,除了最后一个不需要的空格; cf米勒的回答。谢谢。
【解决方案3】:

您需要具有多种功能的//g 修饰符。参见例如here 了解全局匹配的复杂性。

【讨论】:

    【解决方案4】:

    你的意思是......

    $str =~ s/(..)/$1 /sg;
    

    更新:对于更复杂的替换,就像您在问题的第二部分中提出的那样,您可以使用e 修饰符,它允许您评估任意perl代码:

    sub insert_spcs {
        my $str = shift;
        join ' ', $str =~ /(..?)/sg
    }
    
    my $str = 'blah <<ABCDEFGH>> blah';
    $str =~ s/<<(.*?)>>/'<< '.insert_spcs($1).' >>'/se;
    

    【讨论】:

    • 我相信 'g' 选项每次都会从开头解析字符串,因此它会是无限递归。但实际上它完成了第一点的工作,谢谢。
    • 我的 $str = 'blah > blah'; $对于第二点:my $str = 'blah > blah'; $str =~ s/>/>/sg;打印 $str."\n";产生输出: blah > blah 这不符合我的要求。
    • 从代入运算符右侧调用函数很有趣,它使操作的含义清晰易懂。
    【解决方案5】:

    我个人会用m//g 分割文本并使用join

    my $input = "ABCDEFGH";
    my $result = join " ", ( $input =~ m/(..)/g );
    say "RESULT <$result>";'
    

    产量

    RESULT <AB CD EF GH>
    

    【讨论】:

    • $input 有奇数个字符时,它会失败。可以固定为$input =~ m/(..?)/g
    【解决方案6】:

    其他答案更好,但只是为了咯咯笑:

    join ' ', grep length, split /(..)/, 'ABCDEFGH';
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-13
      • 2014-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多