【问题标题】:How to perform replaceAll excluding comments in java如何在java中执行replaceAll排除注释
【发布时间】:2014-10-17 03:25:29
【问题描述】:

我有一个文件,通常是 XML 文件。我想用“p.q”替换所有出现的“x.y”。但是在这个替换过程中,我想忽略 cmets() 中 x.y 的出现。

我试图使用 String.replaceAll() 来执行这个任务。

例如:

<?xml version="1.0" encoding="UTF-8"?>
<name>This occurrence of x.y should be replaced</name>
<!-- This occurrence of x.y should not be replaced -->

我尝试使用 String.replaceAll("x[\.]y", "p.q") 但我可以看到 cmets 中的出现也被替换了

我可以使用其他替代方法,通过它我可以逐行读取文件并排除以 cmets 开头的行,但我有兴趣使用 replaceAll()

请提供一种实现方式。

【问题讨论】:

  • Obligatory link。不要使用正则表达式,使用 JAXP 提供的众多 XML 解析器之一。
  • 不要使用正则表达式解析 XML。在这种情况下,最简单的方法是使用 XML 解析器。
  • 我个人一直更喜欢简单/标准的 JDOM,但我完全同意 Boris。对 XML 使用正则表达式是灾难的根源。
  • 这个问题相当于只用锤子问如何拆螺丝。就算有办法,也比用螺丝刀更难、更复杂、更危险。
  • @Patricia 喜欢这个类比 - +1。

标签: java xml regex


【解决方案1】:

虽然严格来说这不是您要寻找的答案,但我有一个建议。

我建议使用适当的 XML 解析器(如 Java DOM)来检查和替换节点中的文本,而不是将您的 XML 当作原始的 String 处理。如果它们不是评论,则此类内容应替换节点中的相应文本。

File f = new File("your.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(f);

NodeList eList = doc.getElementsByTagName("*");
for (int e = 0; e < eList.getLength(); e++) {
    Node element = eList.item(e);
    NodeList nList = element.getChildNodes();
    for(int n = 0; n < nList.getLength(); n++){
        Node node = nList.item(n);
        if(node.getNodeType()==Node.TEXT_NODE){
            node.setNodeValue(node.getNodeValue().replace("x.y", "p.q")); 
        }
    }
}

如果内存/效率是一个问题(例如当 your.xml 很大时),您最好使用SAX,它更快(代码更密集)并且不会将 XML 存储在内存中。

编辑您的Document 后,您可能需要use a Transformer 来创建合适的输出。 (Official guide here,蜘蛛鲍里斯的屈膝礼)

希望这会有所帮助。

进一步阅读;

【讨论】:

  • 完全同意,+1。虽然我不得不说将Document 转换为String 来保存它是错误的。 Transformer 专为该任务而设计。有一个tutorial here
  • @BoristheSpider - 谢谢,好电话,我将替换答案的那部分。
  • 我认为可以简单地做if (!(node instanceof Comment)) as Comment extends Node
  • @JaqenH'ghar - 感谢您的帮助。我已经修改了代码:)
  • 感谢您的建议,我正在尝试探索 DOM 解析器来解析和替换字符串..
【解决方案2】:

如果使用正则表达式,一个选项是使用 lookarounds 来检查是否仅替换外部 cmets:

(?s)x\.y(?!(?:(?!<!--).)+-->)

作为 Java 字符串:

"(?s)x\\.y(?!(?:(?!<!--).)+-->)"

使用(?s) DOTALL modifier 使. 也匹配换行符。

Test at regexplanet(点击Java

【讨论】:

  • 这适用于 XML cmets。我试图使用 x\\.y(?!(?:(?!#).)+) 应用相同的模式来排除 .properties 文件 '#' 中的 cmets,但它不起作用。 # 行中的文本也得到匹配.. 我在这里有什么遗漏的吗
  • @AppanaSandeep 这是一项不同的任务。如果你知道例如一行不能长,例如1024(?m) 中试试这个 multiline-mode: (?m)(?&lt;!^ {0,1024}[#!].{0,1024})x.y; see example at regexplanet1024 更改为所需的最大行长。在大多数正则表达式风格中,lookbehind 中没有可用的 * 量词,但在 Java 中可以使用花括号。
  • 这很好用..我能知道这个的解释吗..?有什么链接可以让我了解更多关于正则表达式的信息吗?
  • @AppanaSandeep 在(?m) 多行模式下匹配:^$ 匹配每行的开始和结束。 (?&lt;! 开始消极的回顾。匹配 x.y 仅当从 ^ 行开始时没有(在负后向内):[ ]{0,1024} 0-1024 个空格,然后是 #! 之一(字符类),然后是 .{0,1024} 0-1024 任何字符(在x.y 之前)。可能还想将制表符添加到空间。如果是这样,请将[ ]{0,1024} 更改为[ \t]{0,1024},这样就变成了(?m)(?&lt;!^[ \t]{0,1024}[#!].{0,1024})x.y
  • @AppanaSandeep 要了解有关正则表达式的更多信息,请参阅:SO Regex FAQRexEggregular-expressions.info,在 regex101 上进行测试并阅读说明,阅读 Jeffrey Friedl's book :)
猜你喜欢
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-22
相关资源
最近更新 更多