给定一个字节流(表示字符)和流的编码,我将如何获得字符的代码点?
InputStreamReader r = new InputStreamReader(bla, Charset.forName("UTF-8"));
int whatIsThis = r.read();
上面代码片段中的 read() 返回了什么?它是unicode代码点吗?
它不读取 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
Reader.read()
char
如果没有更多数据可用,则返回一个可以转换为或 -1 的值。
Achar
是(隐式)UTF-16BE 编码中的 16 位代码单元。这种编码可以用单个char
. 补充范围使用两个char
序列表示。
该Character
类型包含将 UTF-16 代码单元转换为 Unicode 代码点的方法:
当您从一个序列中传入两个连续值时,需要两个char
s 的代码点将满足isHighSurrogate和isLowSurrogate。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(); }
}