嵌套正则表达式和替换

3

我有这样的字符串 <p0=v0 p1=v1 p2=v2 ....>,我想使用正则表达式pXvX交换,得到类似于<v0=p0 v1=p1 v2=p2 ....>的结果。我只想交换<>中的成对出现的内容。

我写了以下代码:

Pattern pattern = Pattern.compile("<(\\w*)=(\\w*)>");
Matcher matcher = pattern.matcher("<p1=v1>");
System.out.println(matcher.replaceAll("$2=$1"));

但是它只适用于单个配对 pX=vX。有人能为我解释如何编写适用于多个配对的正则表达式吗?


4
或许可以从模式中去掉 <> 符号? - Pshemo
我希望它仅交换<>之间的那些对。 - user3329098
你的示例输入字符串仅包含在 <...> 中的数据。是否可能存在在其外部的 x=y 对,例如 <..>p4=v4,而您希望避免这种情况? - Pshemo
5个回答

2

简单,使用组:

String input = "<p0=v0 p1=v1 p2=v2>";
//                                   |group 1
//                                   ||matches "p" followed by one digit
//                                   ||      |... followed by "="
//                                   ||      ||group 2
//                                   ||      |||... followed by "v", followed by one digit
//                                   ||      |||          |replaces group 2 with group 1,
//                                   ||      |||          |re-writes "=" in the middle
System.out.println(input.replaceAll("(p[0-9])=(v[0-9])", "$2=$1"));

输出:

<v0=p0 v1=p1 v2=p2>

但是你的解决方案交换了所有的成对元素,而我希望它只交换在 <> 之间的那些成对元素。 - user3329098
你认为他需要使用有序的 'p' 和 'v' 以及数字常量,还是像他的正则表达式中那样使用 \w 的变化性? - user557597
抱歉,我现在用平板电脑,明天会仔细查看并进行调整。 - Mena
看起来问题已经被回答了...很抱歉我没有完成。 - Mena

0

如果同样的模式可以在标记之外出现,那么替换<>之间的所有内容(我们称之为标记)- 在我看来是不可能的。

相反,为了一次性替换所有内容,我会选择两个正则表达式:

String str = "<p1=v1 p2=v2> p3=v3 <p4=v4>";
Pattern insideTag = Pattern.compile("<(.+?)>");
Matcher m = insideTag.matcher(str);

while(m.find()) {
    str = str.replace(m.group(1), m.group(1).replaceAll("(\\w*)=(\\w*)", "$2=$1"));
}
System.out.println(str);

//prints: <v1=p1 v2=p2> p3=v3 <v4=p4>

匹配器会抓取在<>之间的所有内容,并且对于每个匹配,它将原始字符串中第一个捕获组的内容替换为交换后的内容,但仅当它匹配(\w*)=(\w*)时才这样做。

尝试使用它

<p1=v1 p2=v2 just some trash> p3=v3 <p4=v4>

输出结果

<v1=p1 v2=p2 just some trash> p3=v3 <v4=p4>

0
如果Java可以使用\G锚点,这将适用于非嵌套的<>。
查找: ((?:(?!\A|<)\G|<)[^<>]*?)(\w+)=(\w+)(?=[^<>]*?>)
替换(全局):$1$3=$2 正则表达式解释:
 (                     # (1 start)
      (?:
           (?! \A | < )
           \G                    # Start at last match
        |  
           <                     # Or, <
      )
      [^<>]*? 
 )                     # (1 end)
 ( \w+ )               # (2)
 =
 ( \w+ )               # (3)
 (?= [^<>]*? > )       # There must be a closing > ahead

Perl测试用例
$/ = undef;
$str = <DATA>;
$str =~ s/((?:(?!\A|<)\G|<)[^<>]*?)(\w+)=(\w+)(?=[^<>]*?>)/$1$3=$2/g;
print $str;
__DATA__
<p0=v0 p1=v1  p2=v2 ....>

输出 >>

<v0=p0 v1=p1  v2=p2 ....>

0

这应该可以用来仅交换那些在<和>之间的对:

String string = "<p0=v0 p1=v1 p2=v2> a=b c=d xyz=abc <foo=bar baz=bat>";
Pattern pattern1 = Pattern.compile("<[^>]+>");
Pattern pattern2 = Pattern.compile("(\\w+)=(\\w+)");
Matcher matcher1 = pattern1.matcher(string);
StringBuffer sbuf = new StringBuffer();
while (matcher1.find()) {
    Matcher matcher2 = pattern2.matcher(matcher1.group());
    matcher1.appendReplacement(sbuf, matcher2.replaceAll("$2=$1"));
}
matcher1.appendTail(sbuf);
System.out.println(sbuf);

输出:

<v0=p0 v1=p1 v2=p2> a=b c=d xyz=abc <bar=foo bat=baz>

0

您可以使用这个模式:

"((?:<|\\G(?<!\\A))\\s*)(p[0-9]+)(\\s*=\\s*)(v[0-9]+)"

为确保这些对在一个开角括号之后,该模式以以下内容开头:
(?:<|\\G(?<!\\A))

这意味着:一个开角括号或者在上一次匹配的结尾

\\G 是一个锚点,用于定位在上一次匹配之后或字符串的开头(换句话说,它是正则表达式引擎在字符串中的最后位置,在字符串开始时为零)。为了避免在字符串开头进行匹配,我添加了一个负向回顾后发断言 (?<!\\A) -> 不以字符串开头为前缀

这个技巧强制每个成对出现的字符都要先于另一个成对或者一个<

例如:

String subject = "p5=v5 <p0=v0 p1=v1 p2=v2 p3=v3> p4=v4";
String pattern = "((?:<|\\G(?<!\\A))\\s*)(p[0-9]+)(\\s*=\\s*)(v[0-9]+)";
String result = subject.replaceAll(pattern, "$1$4$3$2");

如果您需要pv具有相同的数字,您可以将其更改为:
String pattern = "((?:<|\\G(?<!\\A))\\s*)(p([0-9]+))(\\s*=\\s*)(v\\3)";
String result = subject.replaceAll(pattern, "$1$5$4$2");

如果尖括号之间的部分可以包含其他不成对的内容:

String pattern = "((?:<|\\G(?<!\\A))(?:[^\s>]+\\s*)*?\\s*)(p([0-9]+))(\\s*=\\s*)(v\\3)";
String result = subject.replaceAll(pattern, "$1$4$3$2");

注意:所有这些模式只检查是否有一个开角括号,但不检查是否有一个闭角括号。如果缺少一个闭角括号,则所有成对的内容将被替换,直到没有更多连续的成对内容为止,适用于前两个模式,并且适用于第三个模式的下一个闭角括号或字符串结尾。

您可以通过在每个模式的末尾添加(?=[^<>]*>)来检查是否存在闭角括号。但是,添加此内容会使您的模式性能非常低。最好使用(?<=<)[^<>]++(?=>)搜索角括号之间的部分,并在回调函数中执行成对替换。您可以查看this post以实现它。


运行良好,但没有解释。我找到了一个教程,有助于理解你的解决方案。http://www.javaranch.com/journal/2003/04/RegexTutorial.htm - user3329098
@user3329098: 抱歉,我遇到连接问题了,我会添加解释。 - Casimir et Hippolyte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接