要将java字符转换为xml实体,我可以对字符串中的每个字符执行以下操作:
buf.append("&#x"+ Integer.toHexString(c | 0x10000).substring(1) +";");
但是,根据其他stackoverflow问题,这仅适用于Unicode 3.0。
如果我使用UTF-8 Reader读取字符串,那么可能是String包含通过Unicode 6.0处理的格式的字符(因为Java 7根据javadoc支持Unicode 6.0)。
一旦我有了String,我怎么能把它写成xml实体?理想情况下,我会使用一些随着新版unicode问世而继续工作的api。
答案 0 :(得分:4)
要么您没有使用正确的术语,要么存在很多混淆。
&#x
字符引用表示法只指定一个数字代码点;它独立于任何读者或解析器使用的Unicode版本。
您的代码实际上只与Unicode 1.x兼容,因为它假定字符的数值小于2 16 。从Unicode 2.0开始,这不是一个正确的假设。一些字符由单个Java char
表示,而其他字符由两个Java char
表示(称为surrogates)。
我不确定" UTF-8读卡器"是。 Reader仅读取char
值,并且不知道UTF-8或任何其他字符集,InputStreamReader除外,CharsetDecoder使用Formatter将字节转换为字符使用UTF-8编码(或特定CharsetDecoder使用的任何编码)。
无论如何,没有Reader会解析XML &#x
字符引用表示法。您必须使用XML解析器。
没有Reader或XML解析器受Java已知的Unicode版本的影响,因为没有Reader或XML解析器以任何方式查询Unicode数据库。在解析字符时,字符仅被视为数值。它们是否与任何Unicode版本中的已分配代码点相对应,从不考虑它们。
最后,要将String写为XML,您可以使用{{3}}:
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类,但事实证明使用非Unicode编码的XMLStreamWriter如ISO-8859-1或US-ASCII不能将代理对正确输出为单个字符实体,如Java 1.8.0_05。)
答案 1 :(得分:2)
最初Java支持Unicode 1.0,使 char 类型长16位,但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();
}
下一个字符串示例是一个很好的测试用例,因为它包含一个非ascii字符,可以用16位值表示,还有一个带有高/低代理项对的字符:
String myString = "text with some non-US chars: 'Ñ' and ''";