在 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 个索引处的字符/字母/符号/数字