使用捕获搜索和替换正则表达式

Posted

技术标签:

【中文标题】使用捕获搜索和替换正则表达式【英文标题】:Search-and-replace regex with capture 【发布时间】:2013-08-08 23:11:11 【问题描述】:

我有一个长文本文件,我希望大部分内容保持不变,但某些短语需要翻译。这不完全是一个干净的搜索和替换...例如,我需要更改每次出现的 this...

lis r3, ha16(aLabel)

...进入这个:

lis r3, aLabel@ha

即我需要找到整个ha16(aLabel),从中捕获aLabel(可以是任何标识符文本,直到终止结尾括号),然后发出捕获文本的替换,然后是@ha

我找到了很多 perl 搜索和替换的例子,但我没有遇到过我需要的东西,其他提到“perl”和“捕获”的帖子似乎没有解决我的问题问题......或者他们可能会这样做,而我太愚蠢了,无法意识到这一点。

【问题讨论】:

总是ha16还是可能有其他模式或者总是2个字母和2个数字? @Prix - 我想要一个通用解决方案,但在这种特殊情况下,我需要搜索、捕获和替换两种模式:ha16(identifier) --> identifier@ha 和 @987654328 @ --> identifier@l。 (不,这不是错字,第二次转换删除了 'lo' 中的 'o'。)第二次转换可以在必须保留的同一行上跟在它后面的字符,但第一次没有。 【参考方案1】:

你可以这样做:

#!/usr/bin/perl

use strict;
use warnings;

my $text = 'lis r3, ha16(L_.str10) some more text blah lis r3, lo16(identifier) some more text blah lis r3, ot16(identifier)';
$text =~ s/(\w2)\d2\(([\w\.]+)\)/$1 eq 'lo' ? $2 . '@l' : $2 . '@' . $1/gie;
print $text;

也可以写成:

#!/usr/bin/perl

use strict;
use warnings;
while (<DATA>) 
     s/(\w2)\d2\(([\w\.]+)\)/$1 eq 'lo' ? $2 . '@l' : $2 . '@' . $1/gie;
     #you can also print out the result of the replacement.
     #print $_;


__DATA__
lis r3, ha16(L_.str10) 
some more text blah lis r3, lo16(identifier) 
some more text blah lis r3, ot16(identifier)

简单地说,e 修饰符允许您使用正则表达式右侧的代码来替换模式。 For a more detailed explanation you can read this question.

在此示例中,我使用 (\w2)\d2 匹配括号内标签之前的扩展名,并将 2 个字母分组以供以后使用,并使用 ([\w\.]+) 表示任何字母数字字符加上下划线和点,以匹配您的标签。

右边我在做一个三元运算符来定义扩展:

$1 eq 'lo' ? $2 . '@l' : $2 . '@' . $1

如果第一个元素(即 2 个字母)等于 lo,则使用 @l,否则使用 2 个字母作为 @extension,例如我的示例文本中的 @ha@ot

Live DEMO.

【讨论】:

这是一个很棒的解决方案,非常感谢。我完成的脚本位于:eval.in/41852 您在 40 分钟内将我的 perl 正则表达式功能扩展了 4000%。 @phonetagger 很高兴它对你有用,我一直在使用 e 修饰符,它非常糟糕。 @phonetagger 顺便说一句,这个也应该得到修复吗? -8(r1) 不,ha16()hi16()lo16() 修饰符是 LLVM 汇编器对 GCC 汇编器使用的方言:@ha@hi@l。它们指定了应该进入寄存器的标签/标识符地址部分,因为在 RISC 代码中,您不能在单个指令中加载整个 32 位。所以@ha@hi 加载前16 位,而@l 加载低16 位。 -8(r1) 不是指定地址的标签/标识符,它只是从寄存器 r1 偏移 -8 处的内存内容。 @phonetagger 我明白了,谢谢你让我知道,这看起来很熟悉,但我从来没有想过它可能是那个哈哈。【参考方案2】:

我认为这可以改进为一行,但我会这样做:

$val = "lis r3, ha16(L_.str10)";
if ($val =~ /ha16\((.*?)\)/) 
    # $1 now contains the extracted text
    $capture = $1;
    $val =~ s/ha16\(.*?\)/$capture\@ha/gi;

所涉及的正则表达式的解释:

ha16\((.*?)\)

ha16\( 基本上表示“任何以ha16( 开头的文本”。 ( 被转义为 这是一个正则表达式关键字

(.*?) () 的意思是“捕获与其中的模式匹配的所有内容。 .*? 表示“匹配任何字符(即.)的零个或多个(即*? 表示不贪心

\) 说“一旦达到这一点,就停止匹配”(这是因为 非贪婪?我们使用)

以及替换:

s/ha16\(.*?\)/$1\@ha/gi

这种格式的任何东西:s/&lt;something&gt;/&lt;something&gt;/ 将告诉 perl 进行查找 并更换。 $1 是第一组括号中的匹配项(如果有 不止一个,我们会有一个$2 等等)。最后的gi 说要替换 全局(替换第一个匹配项后不要停止),并且不区分大小写。

【讨论】:

为什么:eval.in/41801 不起作用?它消除了标识符“_globvar”。 啊哈...显然你不能在正则表达式中使用 $1 因为它是一个正则表达式元字符 (***.com/questions/3848221/…) 我已经更新了代码以反映这一点。 你可以使用\1 @RobbertWijtman - 也是一个很好的解决方案,一旦您的编辑修复。【参考方案3】:

有点像..

use strict;
use warnings;

while (<>) 
     s/ha16\((.+)\)/$1\@ha/gi;
     print;

或者更好的是,为多次出现的变化使用映射。

my %map = (
    ha => '@ha',
    hi => '@hi',
    lo => '@l'
);

while (<>) 
   s/(\w2)16\((.+)\)/$2$map$1/gi;
   print;

使用? 消除贪婪,. 几乎匹配任何字符,+ 表示一个或多个。

【讨论】:

以上是关于使用捕获搜索和替换正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

js正则表达式替换捕获内容

正则表达式 [REGEX] - 替换/替换 - 捕获组 1 和 2 中的内容

C# - 正则表达式匹配模式、替换和捕获行号 [来自 Txt 文件]

正则表达式替换重复捕获

如何使用 python 正则表达式替换使用捕获的组? [复制]

Ruby 用捕获的正则表达式模式替换字符串