使用捕获搜索和替换正则表达式
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/<something>/<something>/
将告诉 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;
使用?
消除贪婪,.
几乎匹配任何字符,+
表示一个或多个。
【讨论】:
以上是关于使用捕获搜索和替换正则表达式的主要内容,如果未能解决你的问题,请参考以下文章
正则表达式 [REGEX] - 替换/替换 - 捕获组 1 和 2 中的内容
C# - 正则表达式匹配模式、替换和捕获行号 [来自 Txt 文件]