java中的乱码及编码问题

使用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();

输出:?无乱码!

两个问题:

  • 这个问号是哪来的?
  • 当先前无法得知文件编码格式时也不能用了

3. 问号是标识文件编码的符号BOM,知道这个后,两个问题可以一起解决了: 可以检测文件前三个或两个字节的十六进制来判断编码格式。
UTF-8的标识为“EF BB BF”:
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();
得到:
EF BB BF
验证成功,更一般的,使用:
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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值