Perl正则表达式捕获组和第n次出现

k7fdbhmy  于 2022-11-15  发布在  Perl
关注(0)|答案(4)|浏览(174)

我正在学习perl正则表达式,并尝试合并捕获组和指定字符串的第n次出现。
比如说我有下面的话:

title="alpha" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur name=sigma

我想将title属性更改为第n个name=之后的字符串,例如sigma,同时保留其间的所有内容。此外,name=可能带有双引号,例如name="beta"name=sigma
第一次出现name=

title="beta" lorem ipsum lorem ipsum Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur name=sigma

第二次出现name=

title="sigma" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur

我用途:

find . -type f -exec perl -pi -w -e 's/(title=)"?[^"\s]*"?(.*) name="?([^"\/]+)"?/$1"$3"$2/' \{\} \;

这适用于第一次出现的name=
我不知道如何修改它来指定name=的第n次出现。我知道指定第n次出现的基础知识(例如 * 将第二个abc替换为xyz*),...

s/abc/ ++$count == 2 ? "xyz" : "abc" /eg

...但在将其集成到上面的代码中时遇到了问题。如何指定第n个name=并移动其下面的捕获组来替换title属性?

oyt4ldly

oyt4ldly1#

您可以使用模式在{n}部分中设置一个手动量词,并可以选择重复key=value对以获得您感兴趣的量词。

(title=)"?[^\s="]+"?(\h+(?:.*?[^\s=]+=[^\s=]+){0}.*?)[^\s=]+="?([^\s="]+)"?\h*
                                              ^^^

模式匹配:

  • (title=)"?[^\s="]+"?捕获组1,匹配title=并匹配替换后不想保留的值
  • (捕获组2
  • \h+匹配1个以上空格
  • (?:.*?[^\s=]+=[^\s=]+){0}n 次重复前面的键=值对
  • .*?尽可能不匹配任何字符
  • )关闭组2
  • [^\s=]+=匹配除空格字符或=之外的任何字符1次以上,然后匹配关键部分的=
  • "?([^\s="]+)"?捕获可选双引号之间的group 3中除空格字符="以外的1个以上字符
  • \h*匹配可选的尾随空格

请参见0 repetitions1 repetition2 repetitions的正则表达式演示。
运行{0}{1}{2}命令中的模式

find . -type f -exec perl -pi -w -e 's/(title=)"?[^\s="]+"?(\h+(?:.*?[^\s=]+=[^\s=]+){0}.*?)[^\s=]+="?([^\s="]+)"?\h*/$1"$3"$2/' \{\} \;

将文件中的行更改为:

title="beta" lorem ipsum lorem ipsum Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur name=sigma

title="delta" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus Curabitur ullamcorper finibus consectetur name=sigma

title="sigma" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur
5lwkijsr

5lwkijsr2#

您可以使用此perl解决方案:

# 1st occurrence 
perl -pe 's/(title=)"?[^"\s]*"?((?:.*?\h+name=){0}.*?)\h+name="?([^"\s]+)"?/$1"$3"$2/'

title="beta" lorem ipsum lorem ipsum Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur name=sigma

# 2nd occurrence
perl -pe 's/(title=)"?[^"\s]*"?((?:.*?\h+name=){1}.*?)\h+name="?([^"\s]+)"?/$1"$3"$2/'

title="delta" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus Curabitur ullamcorper finibus consectetur name=sigma

# 3rd occurrence 
perl -pe 's/(title=)"?[^"\s]*"?((?:.*?\h+name=){2}.*?)\h+name="?([^"\s]+)"?/$1"$3"$2/'

title="sigma" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur

这里(?:.*?\h+name=){N}匹配子模式的N次出现,该子模式是任何文本,后跟1+空格,后跟文本name=。此子模式重复了{N}次,如示例所示。
Online Code Demo

p1tboqfb

p1tboqfb3#

可以通过执行多遍而不是一遍征服所有正则表达式来简化

$N = 1;                          # for the first match
$cnt = 0;                        # silence warnings ($cnt used once)
while (/name="?([^"\s]*)"?/g) { 
    if (++$cnt == $N) {          # get to N-th match 
        $n=$1;                   # store it
        s{name="?$n"?}{};        # remove
        last 
     }   
}; 
s{title=("?\K[^"\s]*)"?}{$n"}    # rewrite title with name

完整示例

perl -pwE'        
    $N=shift//1; $cnt = 0;
    while (/name="?([^"\s]*)"?/g) { 
        if (++$cnt == $N) { $n=$1; s{name="?$n"?}{}; last }  
    }; 
    s{title=("?\K[^"\s]*)"?}{$n"}
' file.txt 2

在测试中,我使用file.txt和问题中的行,

title="alpha" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus name=delta Curabitur ullamcorper finibus consectetur name=sigma

命令行输入2使它查找第二个“name”。

title="delta" lorem ipsum lorem ipsum name="beta" Morbi posuere metus purus  Curabitur ullamcorper finibus consectetur name=sigma

如果出于某种原因需要,整个过程可以写在一行上。
这会两次搜索相同的模式(在while条件和它的主体中),所以从这个意义上说,它是低效的。不过这也不是太糟糕,因为模式相当简单,如果需要的话,可以进行优化,而这种低效只有在做了 * 很多 * 或者使用非常复杂的模式时才能感觉到。但是这是两个具有相同模式的正则表达式,看起来并不令人愉快。
这样做的好处是(相对)简单,所有模式都寻求一个孤立的简单短语。

uqxowvwt

uqxowvwt4#

不要在一个正则表达式中完成所有操作,而是分步骤进行:

perl -lwpe '$n = 2;
            @m=/name="?([^" ]+)"?/g;
            s/title="[^"]+"/title="$m[$n-1]"/;
            s/ name="?\Q$m[$n-1]\E"?//'

1.将所有名称提取到@m数组中;
1.用所需名称替换标题;
1.移除名称定义。

  • 注意:* 我不清楚为什么您说sigma是第二个名称。我会说它是第三个,而delta是第二个。

相关问题