使用Regex修复Java中未转义的XML实体?

我有一些格式错误的XML,我必须解析。 无法解决上游问题。

(当前)问题是&符号并不总是正确转义,所以我需要将&转换为&

如果& 已经存在,我不想把它改为& 。 一般来说,如果任何格式良好的实体已经存在,我不想破坏它。 我不认为通常可以知道可能出现在任何特定XML文档中的所有实体,所以我想要一个像&;这样的解决方案&; 保存完好。

其中是定义初始和结束之间的实体的一组字符; 。 特别是, <> 不是表示XML元素的文字。

现在,在解析时,如果我看到&我不知道我是否会遇到; , 一个 (空间),行尾或其他。 因此,我认为我必须记住因为我outlook一个会告诉我如何处理原始&

我认为我需要Push Down Automaton的强大function才能做到这一点,我认为有限状态机不会因为我认为是内存需求而起作用 – 这是正确的吗? 如果我需要PDA,那么在对String.replaceAll(String, String)的调用中的正则表达式将不起作用。 或者是否有可以解决此问题的Java正则表达式?

请记住:每行可以有多个替换。

(我知道这个问题 ,但它没有提供我正在寻找的答案。)

这是你正在寻找的正则表达式: &([^;\\W]*([^;\\w]|$)) ,相应的替换字符串将是&$1 。 它匹配& ,后跟零个或多个非分号或分词符号(它需要允许零以匹配独立的&符号),然后是不是分号(或行结束)的分词符号。 捕获组允许您使用& 你正在寻找。

以下是使用它的一些示例代码:

 String s = "& & &nsbp; &tc., &tc. &tc"; final String regex = "&([^;\\W]*([^;\\w]|$))"; final String replacement = "&$1"; final String t = s.replaceAll(regex, replacement); 

在沙箱中运行后,我得到以下t的结果:

 & & &nsbp; &tc., &tc. &tc 

如你所见,原始&  维持不变。 但是,如果您使用“&&”进行尝试,则会获得&& ,如果您使用“&&&”进行尝试,则会得到&&& 我把它作为你所暗示的前瞻性问题的症状。 但是,如果您更换该行:

 final String t = s.replaceAll(regex, replacement); 

有:

 final String t = s.replaceAll(regex, replacement).replaceAll(regex, replacement); 

它适用于所有这些字符串以及我能想到的任何其他字符串。 (在一个完成的产品中,你可能会编写一个例程来执行这个双重replaceAll调用。)

我想你也可以使用前瞻来查看&字符后跟字符和分号(例如&(?!\w+;) )。 这是一个例子:

 import java.util.*; import java.util.regex.*; public class HelloWorld{ private static final Pattern UNESCAPED_AMPERSAND = Pattern.compile("&(?!(#\\d+|\\w+);)"); public static void main(String []args){ for (String s : Arrays.asList( "http://www.example.com/?a=1&b=2&c=3/", "Three in a row: &&&", "< is <, > is >, ' is ', etc." )) { System.out.println( UNESCAPED_AMPERSAND.matcher(s).replaceAll("&") ); } } } // Output: // http://www.example.com/?a=1&b=2&c=3/ // Three in a row: &&& // < is <, > is >, ' is ', etc. 

首先要了解实体周围的语法: http : //www.w3.org/TR/xml/#NT-EntityRef

然后查看FilterInputStream的JavaDoc: http : //download.oracle.com/javase/6/docs/api/java/io/FilterInputStream.html

然后实现一个逐字符读取实际输入的。 当它看到&符号时,它会切换到“实体模式”并查找有效的实体引用( & Name ; )。 如果它在Name不允许的第一个字符之前找到一个,那么它会逐字地将它写入输出。 否则它写& 然后是&符之后的所有内容。

不要试图在所有可能的坏数据上做一些事情,而是一次一个地处理坏数据的出现。 有可能生成XML的任何东西都搞乱了一两个字符而不是所有的字符。 这当然是假设。

当&后跟amp;时,尝试更换all&with&EXCEPT。 如果你遇到的下一个不正确编码的字符是<,那么用<替换它们全部。 保持规则集小而易于管理,只处理你知道错误的事情。

如果你尝试做很多事情,你可能最终会更换你不想要的东西并自己弄乱数据。

我只想指出,最好的解决方案是鼓励生成XML的人在其结束时修复编码。 这可能很难问,但如果你专业地向他们解释他们没有生成有效的XML,他们可能愿意修复这些bug。 这将有下一个必须消耗它的人的额外好处,不需要做一些疯狂的自定义代码来解决应该在源头解决的问题。 至少考虑一下。 可能发生的更糟糕的事情是你问,他们说不,你现在就在你身边。

抱歉引起了一个旧线程:
我遇到了同样的问题,我使用的解决方法分为3个步骤:

  1. 识别有效的实体引用并从正则表达式中“ 隐藏 ”它们
  2. 使用正则表达式替换未转义的字符
  3. 恢复以前“ 隐藏 ”的实体引用

通过将实体包含在自定义字符序列中来完成隐藏。 例如“ #||||#

为了说明,我们说这个带有未转义字符的XML片段&

 Testname  random words one & two I am sad&happy; at the same time! its still < ecstatic It is two & three words Short form is 2&three Now for some invalid entity refs: &amp, &gt, and &lt too.  

步骤1:
我们使用正则表达式替换"[&]\(amp|apos|gt|lt|quot\)[;]""#||$1||#" 。 这是因为根据W3C的有效XML实体引用是amp,lt,gt,apos&quot 。 字符串现在看起来像这样:

 Testname  random words one #||amp||# two I am sad&happy; at the same time! its still #||lt||# ecstatic It is two & three words Short form is 2&three Now for some invalid entity refs: &amp, &gt, and &lt too.  

隐藏了有效的实体引用。 &happy; 没有动过。

第2步:
正则表达式将"[&]"替换为"&" 。 字符串现在看起来像这样:

 Testname  random words one #||amp||# two I am sad&happy; at the same time! its still #||lt||# ecstatic It is two & three words Short form is 2&three Now for some invalid entity refs: &amp, &gt, and &lt too.  

第三步:
正则表达式将"#\|\|([az]+)\|\|#"替换为"&$1;" 。 最终更正的字符串现在看起来像这样:

 Testname  random words one & two I am sad&happy; at the same time! its still < ecstatic It is two & three words Short form is 2&three Now for some invalid entity refs: &amp, &gt, and &lt too.  

缺点:必须仔细选择隐藏有效实体的自定义字符序列,以确保没有有效内容偶然包含相同的序列。 虽然机会很小,但承认,这不是一个全面的解决方案……