使用FileInputStream读取文本文件中的内容:
FileInputStream input = new FileInputStream("E:/test/test.txt");
StringBuffer buffer = new StringBuffer();
byte[] bytes = new byte[1024];
for(int n ; (n = input.read(bytes))!=-1 ; ) //n是读取到的字节个数
buffer.append(new String(bytes,0,n));
System.out.println(buffer);
input.close();
文本中内容为“
无乱码!
”,显示内容为“
锘挎棤涔辩爜锛?
”。
查看文本文件的编码格式,发现是UTF-8,说明字节流input的编码格式是UTF-8,同样,bytes也是。
中文Windows的默认系统编码格式是GBK,这导致了第五行将bytes解码为String时编码解码的格式不一。故出现乱码。
解决方法:
1. 使用:
buffer.append(new String(bytes,0,n,"UTF-8"));
但当先前无法得知文件编码格式时也不能用了。
2. 使用字节/字符转换流InputStreamReader,用指定的 charset 读取字节并将其解码为字符:
FileInputStream input = new FileInputStream("E:/test/test.txt");
InputStreamReader isr = new InputStreamReader(input,"UTF-8");
StringBuffer buffer = new StringBuffer();
char[] ch = new char[1024];
for(int n ; (n = isr.read(ch))!=-1 ; )
buffer.append(new String(ch,0,n));
System.out.println(buffer);
isr.close();//注意关闭顺序
input.close();
输出:?无乱码!
两个问题:- 这个问号是哪来的?
- 当先前无法得知文件编码格式时也不能用了
FileInputStream input = new FileInputStream("E:/test/test.txt");
byte[] flag = new byte[3];
input.read(flag);
for (int i=0; i < 3; i++)
{
int tmp = flag[i];
String hexString = Integer.toHexString(tmp);
// 1个byte变成16进制的,只需要2位就可以表示了,取后面两位,去掉前面的符号填充
hexString = hexString.substring(hexString.length() -2);
System.out.print(hexString.toUpperCase());
System.out.print(" ");
}
input.close();
得到:
private static String getCharset(String fileName) throws IOException{
FileInputStream fis = new FileInputStream(fileName);
int p = (fis.read() << 8) + fis.read();
//高16位0填充
String code = null;
switch (p) {
case 0x00efbb:
code = "UTF-8";
break;
case 0x00fffe:
code = "UTF-16LE";
break;
case 0x00feff:
code = "UTF-16BE";
break;
default:
code = "GBK";
}
fis.close();
return code;
}
注:UTF-32(耗空间),UTF-7(电子邮件传输标准之一)等不太常用的没有写,需要时可添加。
补充几点:
1. String类的解码编码问题(参考:http://blog.csdn.net/chenssy/article/details/42913511)
unicode是转换编码的桥梁
String s = "你好! java"; //s里面是Unicode编码的字符串
byte[] bytes = s.getBytes(); //平台默认编码方式,此处GBK。若无,则为ISO-8859-1。 unicode字符变成GBK编码方式的字节数组
String s1 = new String(bytes,"GBK"); //自定义解码方式为GBK。GBK编码方式的字节数组按GBK的解码方式解码,再以unicode字符串显示。
String s2 = new String(bytes,"UTF-8"); //自定义解码方式为UTF-8,与编码方式不同,乱码
String s3 = new String(bytes); //解码操作,此处默认平台编码方式为GBK。若无,则为ISO-8859-1
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
得到:
你好! java
???! java
你好! java
若要实现对unicode字符串进行编码,查看结果,可用:
System.out.println(URLEncoder.encode(str, "UTF-8"));
一个有趣的现象:txt中写“联通”,然后以ANSI( GB2312、GBK、GB18030、Big5、Shift_JIS 等的统称,记事本默认的就是ANSI)存储,再次打开,发现是乱码。原因:
“联通”的GBK编码:
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000
而UTF-8编码格式:第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。发现完全符合(红色部分)!于是就被误认为UTF-8编码的了。
2. 关于UTF-8,GBK,ASCLL等等字符集之间的区分。(参考:http://www.cnblogs.com/happyday56/p/4135845.html)
Unicode是用0至65535之间的数字来表示所有字符,其中0至127这128个数字表示的字符仍然跟ASCII完全一样。但是unicode只定义了数字和字符的对应,并没有定义如何存储。故有了UTF-8、UTF-7、UTF-16等的编码方式。
ASCII及其扩展字符集
作用:表语英语及西欧语言。
位数:1字节,第1位0,后7位能表示128个字符;其扩展使用8位表示,表示256个字符。
范围:ASCII从00到7F,扩展从00到FF。
ISO-8859-1字符集
作用:扩展ASCII,表示西欧、希腊语等。
位数:8位
范围:从00到FF,兼容ASCII字符集。
GB2312字符集
作用:国家简体中文字符集,兼容ASCII。
位数:使用2个字节表示,能表示7445个符号,包括6763个汉字,几乎覆盖所有高频率汉字。
范围:高字节从A1到F7, 低字节从A1到FE。将高字节和低字节分别加上0XA0即可得到编码。
BIG5字符集
作用:统一繁体字编码。
位数:使用2个字节表示,表示13053个汉字。
范围:高字节从A1到F9,低字节从40到7E,A1到FE。
GBK字符集
作用:它是GB2312的扩展,加入对繁体字的支持,兼容GB2312。
位数:使用2个字节表示,可表示21886个字符。
范围:高字节从81到FE,低字节从40到FE。
GB18030字符集
作用:它解决了中文、日文、朝鲜语等的编码,兼容GBK。
位数:它采用变字节表示(1 ASCII,2,4字节)。可表示27484个文字。
范围:1字节从00到7F; 2字节高字节从81到FE,低字节从40到7E和80到FE;4字节第一三字节从81到FE,第二四字节从30到39。
UCS字符集
作用:国际标准 ISO 10646 定义了通用字符集 (Universal Character Set)。它是与UNICODE同类的组织,UCS-2和UNICODE兼容。
位数:它有UCS-2和UCS-4两种格式,分别是2字节和4字节。
范围:实际上只用了31位,最高位必须为0。
UNICODE字符集
作用:为世界650种语言进行统一编码,兼容ISO-8859-1。
位数:字符集统一两个字节。UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16和UTF-32,编码方式可以有不同字节。
- UTF-8:采用变长字节 (1 ASCII, 2 希腊字母, 3 汉字, 4 平面符号) ,最长6字节。编码方式:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。详见百度百科。兼容ASCLL。
- UTF-16:大部分字符都以固定长度的字节 (2字节) 储存,但UTF-16却无法兼容于ASCII编码。有大尾序和小尾序。一般在UTF-16文件的开首,以FF FE代表UTF-16LE,以FE FF代表UTF-16BE。