簡體   English   中英

使用正則表達式修復 Java 中未轉義的 XML 實體?

[英]Fixing unescaped XML entities in Java with Regex?

我有一些格式錯誤的 XML 必須解析。 無法在上游修復問題。

(當前)問題是&符號並不總是正確轉義,所以我需要將&轉換為&

如果&amp; 已經存在,我不想將其更改為&amp;amp; . 一般來說,如果任何結構良好的實體已經存在,我不想破壞它。 一般來說,我認為不可能知道可能出現在任何特定 XML 文檔中的所有實體,所以我想要一個解決方案,其中包含&<characters>;之類的任何東西。 被保留。

其中<characters>是一些字符集,定義了初始&和結束之間的實體; . 特別是, <>不是文字,否則將表示 XML 元素。

現在,在解析時,如果我看到&<characters>我不知道我是否會遇到; , 一個 (空格)、行尾或另一個& 所以我認為我必須記住<characters> ,因為我期待一個可以告訴我如何處理原始&的字符。

我認為我需要下推自動機的力量來做到這一點,我認為有限的 State 機器不會工作,因為我認為是 memory 要求 - 對嗎? 如果我需要 PDA,那么調用String.replaceAll(String, String)中的正則表達式將不起作用。 或者有沒有可以解決這個問題的 Java 正則表達式?

請記住:每行可能有多個替換。

(我知道這個問題,但它沒有提供我正在尋找的答案。)

這是您要查找的正則表達式: &([^;\\W]*([^;\\w]|$)) ,相應的替換字符串將是&amp;$1 它匹配& ,后跟零個或多個非分號或分詞符(它需要允許零個來匹配獨立的 & 符號),然后是一個不是分號(或行尾)的分詞符。 捕獲組允許您使用&amp;進行替換你正在尋找的。

這是一些使用它的示例代碼:

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

在沙箱中運行它后,我得到以下 t 的結果:

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

如您所見,原來的&amp; &nbsp; 維持不變。 但是,如果你用 "&&" 試試,你會得到&amp;& ,如果你用 "&&&" 試試,你會得到&amp;&&amp; ,我認為這是您所暗示的前瞻問題的症狀。 但是,如果您替換該行:

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&amp;c=3/",
            "Three in a row: &amp;&&amp;",
            "&lt; is <, &gt; is >, &apos; is ', etc."
        )) {
            System.out.println(
                UNESCAPED_AMPERSAND.matcher(s).replaceAll("&amp;")
            );        
        }
     }
}

// Output:
// http://www.example.com/?a=1&amp;b=2&amp;c=3/
// Three in a row: &amp;&amp;&amp;
// &lt; is <, &gt; is >, &apos; is ', etc.

首先了解實體的語法: http://www.w3.org/TR/xml/#NT-EntityRef

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

然后實現一個逐字符讀取實際輸入的方法。 當它看到一個 & 符號時,它會切換到“實體模式”並尋找一個有效的實體引用 ( & Name; )。 如果它在Name中不允許的第一個字符之前找到一個,則將其逐字寫入 output。 否則它寫&amp; 緊隨其后的是&符號之后的所有內容。

與其嘗試對所有可能的不良數據進行一般性的處理,只需處理一次出現的不良數據。 有可能生成 XML 的東西會弄亂一兩個字符,但不是全部。 這當然是一個假設。

嘗試將所有 & 替換為 & 除了 & 后跟 amp; 時。 如果您遇到的下一個編碼不正確的字符是 <,則將它們全部替換為 <。 保持規則集小而易於管理,只處理你知道是錯誤的事情。

如果您嘗試做很多事情,最終可能會替換您不打算做的事情並自己弄亂數據。

我只想指出,最好的解決方案是鼓勵生產 XML 的人最終修復編碼。 問這個問題可能很尷尬,但如果你專業地向他們解釋他們沒有生成有效的 XML,他們可能願意修復錯誤。 這將為下一個必須使用它的人帶來額外的好處,不需要做一些瘋狂的自定義代碼來解決應該從源頭解決的問題。 至少考慮一下。 可能發生的更糟糕的事情是你問,他們說不,你就在你現在的位置。

很抱歉激起了一個舊線程:
我遇到了同樣的問題,我使用的解決方法分為 3 個步驟:

  1. 識別有效的實體引用並從正則表達式中“隱藏”它們
  2. 使用正則表達式替換非轉義字符
  3. 恢復以前“隱藏”的實體引用

隱藏是通過將實體包含在自定義字符序列中來完成的。 例如“ #||<ENTITY_NAME>||#

為了說明,假設我們有這個帶有未轉義字符&的 XML 片段:

<NAME>Testname</NAME>
<VALUE>
    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.
</VALUE>

步驟1:
我們使用正則表達式將"[&]\(amp|apos|gt|lt|quot\)[;]"替換為"#||$1||#" 這是因為根據 W3C 的有效 XML 實體引用是amp,lt,gt,’ & quot 字符串現在看起來像這樣:

<NAME>Testname</NAME>
<VALUE>
    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.
</VALUE>

只有有效的實體引用被隱藏了。 &happy; 原封不動。

第2步:
正則表達式是否將"[&]"替換為"&amp;" . 字符串現在看起來像這樣:

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

第三步:
正則表達式是否將"#\|\|([az]+)\|\|#"替換為"&$1;" . 最終更正后的字符串現在如下所示:

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


缺點:必須仔細選擇隱藏有效實體的自定義字符序列,以確保沒有任何有效內容會偶然包含相同的序列。 雖然機會很小,但承認,這不是一個完全可靠的解決方案......

我使用了上面的UNESCAPED_AMPERSAND解決方案,但我不得不將正則表達式更改為

private static final Pattern UNESCAPED_AMPERSAND =
        Pattern.compile("&(?!(#\\d+|#x[0-9a-fA-F]+|\\w+);)");

添加|#x[0-9a-fA-F]+以說明十六進制字符引用。

(我想評論那個解決方案,但顯然我不能。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM