java中InputStream、OutputStream读取以字节为单位,而Reader、Writer以字符为读写单位。下面例子模仿Reader直接读取utf-8格式编码字符:
public class Utf8Reader {
private InputStream inputStream;
//10000000取反
private static int back10head=127;
//11000000取反
private static int back110head= 191;
//11100000取反
private static int back1110head= 223;
//3个字节表示字符
byte[] threeBytes = new byte[3];
//2个字节表示字符
byte[] twoBytes = new byte[2];
public Utf8Reader(InputStream inputStream){
this.inputStream=inputStream;
}
/**
* 读取一个字符
* @return
* @throws IOException
*/
public int readChar() throws IOException{
//读取一个字节
int read = inputStream.read();
//字节以1110开头,代表用3个字节表示一个字符
if(read>=224){
threeBytes[0]=(byte) read;
//读取接下来的两个字节
inputStream.read(threeBytes, 1, 2);
//将3个字节转化为字符
return parseThreeByte(threeBytes);
//字节以110开头,便是用2个字节表示一个字符
}else if(read>=192){
twoBytes[0]=(byte) read;
//读取接下来的一个季节
inputStream.read(twoBytes, 1, 1);
//将两个字节转化为字符
return parseTwoByte(twoBytes);
//字节以10开头,只能作为多字节字符中的一个字节,不能作为头部
}else if(read>=128){
throw new IOException("非法编码["+read+"]字符以10开头");
//ASCII码或文件结束符-1,直接返回
}else if(read>=0){
return read;
}else{
return -1;
}
}
/**
* 将2个字节转化为1个字符
* 将110xxxxx 10xxxxxx 字节数值转化为 xxxx xxxxxx 字符
* @param twoBytes2
* @return
*/
private int parseTwoByte(byte[] bytes) {
//去掉二字节头部表示
int head=bytes[0]&back110head;
//向右移6位
head=head<<6;
//去掉组成部分头部表示
int tail=bytes[1]&back10head;
return (char) (head|tail);
}
/**
* 将三个字节转化为1个字符
* 将1110xxxx 10xxxxxx 10xxxxxx字节数值转化为 xxx xxxxxx xxxxxx字符
* @param threeBytes2
* @return
*/
private int parseThreeByte(byte[] bytes) {
//去掉三字节头部表示
int head=bytes[0]&back1110head;
//向右移12位
head=head<<12;
//去掉组成部分头部表示
int second=bytes[1]&back10head;
int third=bytes[2]&back10head;
//第二个字符向右移6位
second=second<<6;
return head|second|third;
}
}