在 Perl 中,每 N 个字符插入一个字符的最佳方法

Posted

技术标签:

【中文标题】在 Perl 中,每 N 个字符插入一个字符的最佳方法【英文标题】:In Perl, best way to insert a char every N chars 【发布时间】: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。 (我已将其标记为版主注意,因此他们会尽快将其迁移到那里,请不要在那里重复发布,请稍等) 【参考方案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(?=.)/ /sgresg;

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';

【讨论】:

以上是关于在 Perl 中,每 N 个字符插入一个字符的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Perl 中的正则表达式匹配字符串中第 n 个索引处的字符/字母/符号/数字

在 Perl 中将文件转换为字符串的最佳方法是啥?

Perl:如何将字符串的最后 n 位与 n 位或更多位连续匹配?

C ++ - 处理字符串的最佳线程数[关闭]

如何在javascript中每n个字符后插入一个字符?

如何在 Perl 中重复 N 次字符串?