1

要将 java 字符转换为 xml 实体,我可以对字符串中的每个字符执行以下操作:

buf.append("&#x"+ Integer.toHexString(c | 0x10000).substring(1) +";");

但是,根据其他 stackoverflow 问题,这仅适用于 Unicode 3.0。

如果我使用 UTF-8 阅读器读取字符串,那么该字符串可能包含通过 Unicode 6.0 工作的格式的字符(因为根据 javadoc,Java 7 支持 Unicode 6.0)。

一旦我有了那个字符串,我怎么能把它写成 xml 实体?理想情况下,我会使用一些 api,以便在新版本的 unicode 出现时继续工作。

4

2 回答 2

4

要么你没有使用正确的术语,要么这里有很多混乱。

字符引用符号只是指定一个&#x数字代码点;它独立于任何阅读器或解析器使用的 Unicode 版本。

您的代码实际上只与 Unicode 1.x 兼容,因为它假定字符的数值小于 2 16。从 Unicode 2.0 开始,这不是一个正确的假设。一些字符由一个 Java 表示char,而其他字符由两个 Java 表示char(称为surrogates)。

我不确定什么是“UTF-8 阅读器”。Reader只读char取值,不知道 UTF-8 或任何其他字符集,但InputStreamReader除外,它使用CharsetDecoder使用 UTF-8 编码(或特定 CharsetDecoder 使用的任何编码)将字节转换为字符。

无论如何,没有 Reader 会解析 XML&#x字符引用表示法。为此,您必须使用 XML 解析器。

没有 Reader 或 XML 解析器受 Java 已知的 Unicode 版本影响,因为没有 Reader 或 XML 解析器以任何方式查询 Unicode 数据库。字符在解析时仅被视为数值。从不考虑它们是否对应于任何 Unicode 版本中分配的代码点。

最后,要将 String 写为 XML,您可以使用Formatter

static String toXML(String s) {
    Formatter formatter = new Formatter();
    int len = s.length();
    for (int i = 0; i < len; i = s.offsetByCodePoints(i, 1)) {
        int c = s.codePointAt(i);
        if (c < 32 || c > 126 || c == '&' || c == '<' || c == '>') {
            formatter.format("&#x%x;", c);
        } else {
            formatter.format("%c", c);
        }
    }
    return formatter.toString();
}

如您所见,没有代码取决于 Unicode 版本,因为字符只是数值。每个数值是否是分配的 Unicode 代码点并不相关。

(我的第一个倾向是使用 XMLStreamWriter 类,但从 Java 1.8 开始,使用非 Unicode 编码(如 ISO-8859-1 或 US-ASCII)的 XMLStreamWriter 不能正确地将代理对输出为单个字符实体.0_05.)

于 2014-07-01T03:00:17.347 回答
2

最初 Java 通过将char类型设为 16 位长来支持 Unicode 1.0,但 Unicode 2.0 引入了代理字符机制来支持比 16 位允许的数量更多的字符,因此 Java 字符串变成了 UTF-16 编码;这意味着有些字符需要两个 Java 字符来表示,它们被称为高代理字符和低代理字符。

要知道字符串中的哪些字符实际上是高/低代理对,您可以使用以下实用程序方法Character

Character.isHighSurrogate(myChar); // returns true if myChar is a high surrogate
Character.isLowSurrogate(myChar); // same for low surrogate

Character.isSurrogate(myChar); // just to know if myChar is a surrogate

一旦您知道哪些字符是高或低代理,您需要使用以下方法将每一对转换为 unicode 代码点:

int codePoint = Character.toCodePoint(highSurrogate, lowSurrogate);

由于一段代码值一千字,这是一个示例方法,用于将字符串中的非 us-ascii 字符替换为 xml 字符引用:

public static String replaceToCharEntities(String str) {
    StringBuilder result = new StringBuilder(str.length());

    char surrogate = 0;
    for(char c: str.toCharArray()) {

        // if char is a high surrogate, keep it to match it
        // against the next char (low surrogate)
        if(Character.isHighSurrogate(c)) {
            surrogate = c;
            continue;
        }

        // get codePoint
        int codePoint;
        if(surrogate != 0) {
            codePoint = Character.toCodePoint(surrogate, c);
            surrogate = 0;
        } else {
            codePoint = c;
        }

        // decide wether using just a char or a character reference
        if(codePoint < 0x20 || codePoint > 0x7E || codePoint == '<'
                || codePoint == '>' || codePoint == '&' || codePoint == '"'
                || codePoint == '\'') {
            result.append(String.format("&#x%x;", codePoint));
        } else {
            result.append(c);
        }
    }

    return result.toString();
}

下一个字符串示例是一个很好的测试示例,因为它包含一个可以用 16 位值表示的非 ascii 字符以及一个具有高/低代理对的字符:

String myString = "text with some non-US chars: 'Ñ' and ''";
于 2014-07-01T03:02:16.810 回答