【问题标题】:Why this regex not giving expected output?为什么这个正则表达式没有给出预期的输出?
【发布时间】:2012-12-13 18:03:20
【问题描述】:

我有一个包含一些值的字符串,如下所示。我想用一些新文本替换包含特定 customerId 的 html img 标签。我尝试了没有给我预期输出的小型 java 程序。这是程序信息

我的输入字符串是

 String inputText = "Starting here.. <img src=\"getCustomers.do?custCode=2&customerId=3334&param1=123/></p>"
    + "<p>someText</p><img src=\"getCustomers.do?custCode=2&customerId=3340&param2=456/> ..Ending here";

正则表达式是

  String regex = "(?s)\\<img.*?customerId=3340.*?>";

我想放入输入字符串的新文本

编辑开始:

String newText = "<img src=\"getCustomerNew.do\">";

编辑结束:

现在我在做

  String outputText = inputText.replaceAll(regex, newText);

输出是

 Starting here.. Replacing Text ..Ending here

但我的预期输出是

 Starting here.. <img src=\"getCustomers.do?custCode=2&customerId=3334&param1=123/></p><p>someText</p>Replacing Text ..Ending here

请注意,在我的预期输出中,只有包含 customerId=3340 的 img 标签被替换为替换文本。我不明白为什么在输出中我得到两个 img 标签都被替换了?

【问题讨论】:

  • 您正在使用 regex 解析 html,但它永远不会完全正常工作(这通常是对 regex 的限制,而不是您的 regexing 技能)
  • 你使用了错误的工具..使用 html 解析器
  • @ Some1.Kill.The.DJ 你能帮我如何使用像 jsoup 这样的 html 解析器获得预期的结果吗?
  • M Sach,您可以查看我的答案,了解 jsoup 工作的完整示例。

标签: java regex string string-matching


【解决方案1】:

你有“通配符”/“任何”模式(.*),这会将匹配扩展到可能的最长匹配字符串,并且模式中最后一个固定文本是&gt; 字符,它因此匹配输入文本中的最后一个 &gt; 字符,即最后一个!

您应该能够通过将.* 部分更改为[^&gt;]+ 之类的内容来解决此问题,这样匹配就不会超过第一个&gt; 字符。

用正则表达式解析 HTML 肯定会很痛苦。

【讨论】:

  • @Greg 我可以使用 jsoup 库获得预期的输出吗?
  • .*? 实际上与 .* 没有任何不同。零个或多个字符的零个或多个匹配是零个或多个字符,包括任意数量的&gt; 字符。
  • 我不会 Java,抱歉——我刚刚发现了一个典型的 RE 设计错误。
  • 你是在说这样的话 "(?s)\\]+?customerId=3340[^>]+?>" 吗?它似乎有效,但不确定您是否尝试传达相同的正则表达式?
  • @GregA.Woods .*?(.*)? 不同。如果你在重复量词之后有一个?,你就会让它变得不贪婪。这确实有所作为。 Further reading
【解决方案2】:

正如其他人在 cmets 中告诉您的那样,HTML 不是一种常规语言,因此使用正则表达式来操作它通常很痛苦。您最好的选择是使用 HTML 解析器。我以前没有使用过 Jsoup,但是用谷歌搜索一下似乎你需要类似的东西:

import org.jsoup.*;
import org.jsoup.nodes.*;
import org.jsoup.select.*;

public class MyJsoupExample {
    public static void main(String args[]) {
        String inputText = "<html><head></head><body><p><img src=\"getCustomers.do?custCode=2&customerId=3334&param1=123\"/></p>"
            + "<p>someText <img src=\"getCustomers.do?custCode=2&customerId=3340&param2=456\"/></p></body></html>";
        Document doc = Jsoup.parse(inputText);
        Elements myImgs = doc.select("img[src*=customerId=3340");
        for (Element element : myImgs) {
            element.replaceWith(new TextNode("my replaced text", ""));
        }
        System.out.println(doc.toString());
    }
}

基本上,代码获取img 节点列表,其中src 属性包含给定字符串

Elements myImgs = doc.select("img[src*=customerId=3340");

然后循环遍历列表并用一些文本替换这些节点。

更新

如果您不想用文本替换整个img 节点,而是需要为其src 属性赋予一个新值,那么您可以将for 循环的块替换为:

element.attr("src", "my new value"));

或者如果您只想更改 src 值的一部分,那么您可以这样做:

String srcValue = element.attr("src");
element.attr("src", srcValue.replace("getCustomers.do", "getCustonerNew.do"));

这与我发布的 in this thread 非常相似。

【讨论】:

  • 维森特。它运作良好。但是我遇到了一个问题。而不是“我的替换文本”,使用“” jsoup 使元素像这样 <img src="getCustomerNew.do"/&gt ;而不是 ;
  • 看起来它正在对像
  • 所以你不想只替换整个img节点src属性的值?
  • 我只想用一个新的图像标签替换整个图像标签。我的新图像是“”。关键是旧图像标签被新图像标签替换,但是当我执行 doc.toString() 时,我看到新图像标签为 <img src="getCustomerNew.do"/>而不是
  • AFAIK 用相同标签的节点替换现有节点只是为了更改其属性的值是没有意义的。正确的方法是简单地更改属性(我已经简化了我的更新)。我想你得到了那些 <和类似的实体,因为您一直在使用代码的 new TextNode() 部分,它不能用于创建任何类型的节点(例如 img 节点),而只能用于创建文本节点。
【解决方案3】:

发生的情况是您的正则表达式开始匹配第一个 img 标记,然后消耗所有内容(无论是否贪婪),直到找到 customerId=3340 然后继续消耗所有内容直到找到 >

如果您希望它只使用 customerId=3340img,请考虑一下这个标签与它可能匹配的其他标签的不同之处。

在这种特殊情况下,一种可能的解决方案是使用后视运算符(不消耗匹配项)查看 img 标记背后的内容。这个正则表达式可以工作:

String regex = "(?<=</p>)<img src=\".*?customerId=3340.*?>";

【讨论】:

    猜你喜欢
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-21
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    相关资源
    最近更新 更多