使用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个步骤:
- 识别有效的实体引用并从正则表达式中“ 隐藏 ”它们
- 使用正则表达式替换未转义的字符
- 恢复以前“ 隐藏 ”的实体引用
通过将实体包含在自定义字符序列中来完成隐藏。 例如“ #||
”
为了说明,我们说这个带有未转义字符的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: &, >, and < 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: &, >, and < 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: &, >, and < 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: &, >, and < too.
缺点:必须仔细选择隐藏有效实体的自定义字符序列,以确保没有有效内容偶然包含相同的序列。 虽然机会很小,但承认,这不是一个全面的解决方案……