繁体   English   中英

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

[英]Perl regex capture groups and nth occurence

我正在学习 perl 正则表达式,并尝试组合捕获组并指定字符串的第 n 次出现。

说我有以下内容:

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

我想将title属性更改为 nth 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=的第一次出现。

我不知道如何修改它以指定第 n 次出现name= 我知道指定第 n 次出现的基础知识(例如xyz替换第二个abc ),...

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

...但是无法将其集成到我上面的代码中。 如何指定 nth name=并移动其以下捕获组来代替title属性?

您可以使用一种模式在{n}部分中设置手动量词,并可选择重复键=值对以找到您感兴趣的那个。

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

模式匹配:

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

查看0 次重复1 次重复2 次重复的正则表达式演示。


{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 

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

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

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

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

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

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

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

这里(?:.*?\h+name=){N}匹配N次出现的子模式,即任何文本后跟 1+ 空格后跟name

可以通过多次传递来简化,而不是一个征服所有的正则表达式

$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使其寻找第二个“名称”。 它打印

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

如果出于某种原因需要,整件事可以写在一行上。


从某种意义上说,这是低效的,因为我们先搜索模式(在while条件中),然后再搜索替换(在主体中)。 它并不像看起来那么糟糕,因为第二种模式相当简单,如果重要的话可以对其进行优化,但它是具有相同模式的两个正则表达式。 然后我们运行另一个来重写标题。

收益是(比较)简单性,其中所有模式都寻求一个孤立的简单短语(带有nametitle )。

不要在一个正则表达式中做所有事情,而是按步骤进行:

perl -lwpe '$n = 2;
            @m=/name="?([^" ]+)"?/g;
            s/title="[^"]+"/title="$m[$n-1]"/;
            s/ name="?\Q$m[$n-1]\E"?//'
  1. 将所有名称提取到@m 数组中;
  2. 用想要的名字替换标题;
  3. 删除名称定义。

注意:我不清楚你为什么说sigma第二个名字。 我会说这是第三个,而delta第二个。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM