[英]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}
的命令中运行模式
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
条件中),然后再搜索替换(在主体中)。 它并不像看起来那么糟糕,因为第二种模式相当简单,如果重要的话可以对其进行优化,但它是具有相同模式的两个正则表达式。 然后我们运行另一个来重写标题。
收益是(比较)简单性,其中所有模式都寻求一个孤立的简单短语(带有name
和title
)。
不要在一个正则表达式中做所有事情,而是按步骤进行:
perl -lwpe '$n = 2;
@m=/name="?([^" ]+)"?/g;
s/title="[^"]+"/title="$m[$n-1]"/;
s/ name="?\Q$m[$n-1]\E"?//'
注意:我不清楚你为什么说sigma
是第二个名字。 我会说这是第三个,而delta
是第二个。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.