给定一个字节流(表示字符)和流的编码,我如何获得字符的代码点?
InputStreamReader r = new InputStreamReader(bla, Charset.forName("UTF-8"));
int whatIsThis = r.read();
上面代码段中的read()返回了什么?它是unicode代码点吗?
答案 0 :(得分:2)
它不读取unicode代码点,而是读取UTF-16代码单元。低于0xFFFF的代码点没有区别,但是高于0xFFFF的代码点各自由2个代码单元表示。这是因为16位不能超过0xFFFF的值。
在这种情况下也是如此:
byte[] a = {-16, -96, -128, -128}; //UTF-8 for U+20000
ByteArrayInputStream is = new ByteArrayInputStream(a);
InputStreamReader r = new InputStreamReader(is, Charset.forName("UTF-8"));
int whatIsThis = r.read();
int whatIsThis2 = r.read();
System.out.println(whatIsThis); //55360 not a valid stand alone code point
System.out.println(whatIsThis2); //56320 not a valid stand alone code point
使用代理值,我们将它们组合在一起以获得0x20000
:
((55360 - 0xD800) * 0x400) + (56320 - 0xDC00) + 0x10000 == 0x20000
有关UTF-16如何运作的更多信息:http://en.wikipedia.org/wiki/UTF-16
答案 1 :(得分:1)
Reader.read()
返回一个可以转换为char
的值,如果没有更多数据可用,则返回-1。
char
(隐含地)是UTF-16BE编码中的16位代码单元。此编码可以表示具有单个char
的基本多语言平面字符。 supplementary range使用两个 - char
序列表示。
Character
类型包含将UTF-16代码单元转换为Unicode代码点的方法:
当您从isHighSurrogate传入两个连续值时,需要两个char
的代码点将满足isLowSurrogate和sequence。 codePointAt方法可用于从代码单元序列中提取代码点。从代码点到UTF-16代码单元的工作方式类似。
代码点流阅读器的示例实现:
import java.io.*;
public class CodePointReader implements Closeable {
private final Reader charSource;
private int codeUnit;
public CodePointReader(Reader charSource) throws IOException {
this.charSource = charSource;
codeUnit = charSource.read();
}
public boolean hasNext() { return codeUnit != -1; }
public int nextCodePoint() throws IOException {
try {
char high = (char) codeUnit;
if (Character.isHighSurrogate(high)) {
int next = charSource.read();
if (next == -1) { throw new IOException("malformed character"); }
char low = (char) next;
if(!Character.isLowSurrogate(low)) {
throw new IOException("malformed sequence");
}
return Character.toCodePoint(high, low);
} else {
return codeUnit;
}
} finally {
codeUnit = charSource.read();
}
}
public void close() throws IOException { charSource.close(); }
}