需要正则表达式来查找两个标记之间的子字符串。

5

我猜这个问题可能已经有了答案,但是我找不到,所以...

我需要从一个较大的字符串中提取两个标记之间的字符串,其中第二个标记可能会再次出现...(伪代码...)

myString = "A=abc;B=def_3%^123+-;C=123;"  ;

myB = getInnerString(myString, "B=", ";" )  ;

method getInnerString(inStr, startToken, endToken){
   return inStr.replace( EXPRESSION, "$1");
}

因此,当我使用表达式 ".+B=(.+);.+" 运行时,我得到了 "def_3%^123+-;C=123;",这可能是因为它只查找字符串中最后一个 ';' 的实例,而不是在遇到第一个 ';' 时停止。

我尝试使用 (?=) 在搜索第一个 ';',但它给我相同的结果。

我似乎找不到一个Regexp参考,解释如何指定“下一个”标记而不是结尾处的标记。

非常感谢任何和所有的帮助。


类似的SO问题:


dmckee,你的编辑似乎毫无意义。是的,其他人也看到了类似的问题,但这并不一定意味着它们有帮助。 - Evan Fosmark
1
@Evan:如果SO是一个好答案的存储库,则单个问题的多个实例会对其产生干扰,除非它们彼此链接。因此,我会进行链接,大多是向后引用,但有时也会向前引用。我会编辑格式不佳的部分,没有任何借口。-- 致敬 - dmckee --- ex-moderator kitten
@dmckee - 这不是搜索机制应该做的吗?你只是剪切/粘贴了“正则表达式内部”之类的搜索结果吗?请不要这样做 - 链接和问题本身一样长,非常分散注意力。 - user3458
@Arkadiy:我愿意听取反对意见,但是搜索和“相关”侧边栏都不太好用。事实上,我认为重复内容的不断累积是搜索效果差的证明。 - dmckee --- ex-moderator kitten
@Arkadiy:我从我已经回答过的变体中获取我的列表,或者从我收藏的列表中获取,或者记住标题的足够信息以便通过搜索找到(还有那些在收藏栏中的)。 - dmckee --- ex-moderator kitten
Arkadiy,Evan的回答比其他人(我自己找不到的)要简洁得多。然而,如果有人给我这些链接,我也会从中推断出我需要的内容,所以那同样是有帮助的。不确定问题在哪里。 - Yevgeny Simkin
3个回答

7

如果不在贪婪模式中指定 ?,那么你正在使用一种贪婪模式。试试这个:

".+B=(.+?);.+" 

谢谢!它像魔法一样运行,尽管我已经阅读了“?”的描述,但我不确定为什么它会产生这种效果。 - Yevgeny Simkin
不,非贪婪量词通过在前面进行一些额外的工作来消除回溯。 - Alan Moore
这是哪种正则表达式实现?我在RegexBuddy中使用了所有的正则表达式风格进行了测试,每个都需要回溯并需要82步才能找到匹配。 - Gumbo
正则表达式开头的.+导致了所有的回溯。但是这个问题以及结尾处的一个问题只需要在那里,因为OP正在进行“替换”,而他应该进行“查找”。 - Alan Moore
不,你不需要匹配字符串的那些部分。我将发布一个单独的答案来解释(我也会涉及回溯问题)。 - Alan Moore
显示剩余3条评论

5

试试这个:

B=([^;]+);

这个匹配规则会匹配在B=;之间的所有内容,除非它是一个;。因此,它将匹配在B=和第一个;之间的所有内容。


2

以下是对 Evan 回答评论的延续内容的翻译:

当使用你(纠正后的)正则表达式时,会发生以下情况:首先,.+ 匹配整个字符串。然后,它回溯,放弃大部分刚刚匹配的字符,直到找到 B= 可以匹配的位置。接着,(.+?) 匹配(并捕获)它看到的所有内容,直到下一部分,即分号,可以匹配。最后,.+ 吞掉剩余的字符。

你真正感兴趣的只是 "B=" 和 ";" 以及它们之间的任何内容,那么为什么要匹配字符串的其余部分呢?唯一需要这样做的原因是你必须用捕获组的内容替换整个字符串。但是,如果你可以直接访问组的内容,为什么还要这么麻烦呢?这里有一个演示(使用 Java 编写,因为我无法确定你使用的编程语言):

String s = "A=abc;B=def_3%^123+-;C=123;";

Pattern p = Pattern.compile("B=(.*?);");
Matcher m = p.matcher(s);
if (m.find())
{
  System.out.println(m.group(1));
}

为什么用“替换”而不是“查找”更为直接?可能是因为您的API使其更容易;这就是为什么我们在Java中这样做的原因。Java的String类有几种基于正则表达式的便捷方法:replaceAll()replaceFirst()split()matches()(如果正则表达式与整个字符串匹配,则返回true),但没有find()方法。也没有方便的方法来访问捕获组。我们无法像Perl一行代码那样优雅地匹配:
print $1 if 'A=abc;B=def_3%^123+-;C=123;' =~ /B=(.*?);/;

所以我们只能使用这种hack方法:

System.out.println("A=abc;B=def_3%^123+-;C=123;"
    .replaceFirst(".+B=(.*?);.+", "$1"));

清楚地说,我不是说不要使用这些技巧,也不是说 Evan 的回答有什么问题 -- 没有。我只是认为我们应该明白为什么要使用它们,以及在使用时做出的权衡。


我正在使用Java,对比Perl,它的正则表达式选项让我感到非常愤怒。另外两个令人恼火的弱点是缺少最后的/选项(在那里你可以添加i、g等),而是必须运行一些奇怪的.IGNORE_CASE常量,或者是过于繁琐的。 - Yevgeny Simkin
为了转义所有的'',需要使用额外的\,这是一种丑陋的必要性,使得(已经很难用人眼检查的)正则表达式更难以查看。更不用说,如果您需要将一个字符串运行多个正则表达式,那么结果字符串很可能会失去一个'\ '级别。 - Yevgeny Simkin
我承认我刚开始使用Java中的正则表达式,但是我注意到你的评论中提到在perl中使用的不够优雅(我熟悉这个),而且我完全同意。最后, - Yevgeny Simkin
从代码设计的角度来看,Evan提供的原始示例更漂亮,尽管在循环方面更浪费。 - Yevgeny Simkin
从Perl转向Java肯定会很痛苦,毕竟Java更加严格和冗长。尽量接受它的特点吧。至于修饰符,我几乎从不使用IGNORE_CASE等等;只需在正则表达式本身的开头加上(?i)即可。 - Alan Moore

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