替换两个字符串之间的多次出现

问题描述 投票:0回答:6

我需要将a

xx
之间的
每个
字符
zz
替换为
hello

#input
a xxab abzz ca xxbczz aaa axxazza xxczzaxxczz
#output
a xxhellob hellobzz ca xxbczz aaa axxhellozza xxczzaxxczz

这适用于一对,不适用于更多

xx/zz
对(它替换第一个
a
和最后一个
xx
之间的每个
zz
):

sed -r ':rep; s/(xx.*)a(.*zz)/\1hello\2/; trep'

我认为最好的方法是使用更高级的正则表达式,例如

perl

我正在寻找

bash
sed
awk
perl
的解决方案。使用基本/扩展正则表达式是否可以完成此任务?当配对具有更多字符(例如
xxxxxx/zzzzzz
)时,不会变得难以理解的解决方案是首选。

regex bash perl awk sed
6个回答
3
投票

是的,最好使用Perl

perl -pe's/xx(.+?)zz/"xx".$1=~s|a|hello|gr."zz"/ge' file.txt

3
投票

你可以尝试这个Perl方法

perl -E '$_="a xxab abzz ca xxbczz aaa axxazza xxczzaxxczz";
s{xx(.+?)zz}{"xx".$1=~s/a/hello/gr."zz"}xge; 
say $_ ; '

说明

s{
   xx(.+?)zz #grouping the content
 }
 {
   "xx".$1=~s/a/hello/gr."zz" #again making the substitution for $1 and concatenating `xx` and `zz`  
 }xge;

旗帜

g
-> 全球

r
-> 无损修改器

e
-> 评估。

环顾四周

perl -E '$_="a xxab abzz ca xxbczz aaa axxazza xxczzaxxczz";
s{(?<=xx)(.+?)(?=zz)}{$1=~s/a/hello/gr}xge; 
say $_ ; '

2
投票

这可能对你有用(GNU sed):

sed -r ':a;s/zz/\n/;:b;tb;s/(xx[^\na]*)a([^\n]*\n)/\1hello\2/;tb;/zz/ba;s/\n/zz/g' file

这会将

zz
替换为换行符,然后将
a
和换行符之间的所有
xx
替换为
hello

注意可以有任意数量的

xx
不与
zz
配对,并且它们之间的任何
a
都将被替换。


1
投票

这是一种不同的方法,如果没有其他方法与使用更高级的正则表达式功能的单行正则表达式进行比较(

/e
修饰符,如mkHun答案中)。

xx
拆分字符串。迭代术语并替换每个术语部分中的
a
直到
zz
(如果该术语中存在
zz
)。重新组装(连接回)字符串。

我将

a
替换为
-
以便于查看。开始和结束模式位于
$pb
$pe

perl -wE'
    $_ = shift // q(a xxab abzz ca xxbczz aaa axxazza); 
    say; 
    $pb = qr/xx/; $pe = qr/zz/;
    
    ($s, @t) = split /($pb)/; 

    for (@t) { 
        next if /^$pb$/; 
        next if not @m = /(.+?) ($pe.*)/x; 

        $_ = $m[0] =~ s/a/-/gr . $m[1] 
    }; 
    say $s . join "", @t
'

评论

  • 我使用 qr 来分配感兴趣部分的开始和结束模式,以防它们更复杂并且需要正则表达式。

  • split 的分隔符模式 (

    /($pb)/
    ) 中的捕获括号使其也返回分隔符以及其他部分的位置

  • 在每个学期中,我们都需要

    zz
    (或者没有
    xx ... zz
    ,因此无需替换),以及
    zz
    之前的内容(或者无需执行任何操作)

  • zz
    后面可以跟更多文本,直到下一个
    xx
    (我们在其上拆分)

  • 数组中带有项的元素会就地更改(通过分配给

    $_

这是可以作为命令行程序运行的形式,但它应该是一个脚本。它打印(添加评论)

a xxab abzz ca xxbczz aaa axxazza # 原始字符串
a xx-b -bzz ca xxbczz aaa axx-zza # 带替换

我已经测试了更多的字符串,但无论如何请测试更多。


0
投票

您的问题在于

.*
,因为
.
将匹配包括空格在内的每个字符。 您应该使用
\S
来代替,因为它将匹配所有非空白字符:

$ echo 'a xxababzz ca xxbczz aaa axxazza' | sed -r ':rep; s/(xx\S*?)a(\S*?zz)/\1hello\2/; trep'
a xxhellobhellobzz ca xxbczz aaa axxhellozza

0
投票

您必须描述所有不是

zz
(不是
z
z
后跟其他字符的字符)
a
之前和之后,直到
zz
和使用标签和条件测试来处理该行,直到
a
xx
之间不再有
zz
:

sed -E ':a;s/(xx([^z]|z[^z])*z?)a(([^z]|z[^z])*zz)/\1hello\3/g;ta' file

Perl 方式:

perl -pe's/(?:\G(?!^)|xx(?=.*zz))[^za]*(?:z(?!z)[^za]*)*\Ka/hello/g' file

可以轻松更改为:

perl -pe's/(?:\G(?!^)|xxxxxx(?=.*zzzzzz))[^za]*(?:z(?!zzzzz)[^za]*)*\Ka/hello/g' file

处理

xxxxxx
zzzzzz

© www.soinside.com 2019 - 2024. All rights reserved.