这几天遇到编码问题,初步整理一下。
计算机中有好多编码,编码问题也是因为文字的编码不统一,编码间的差异造成的。
所谓乱码,其实就是因为字符使用的编码,解码的类型不一样,才出现的一些不认识或者不是想要的数据。
比如:用UTF-8编码的【中国】 换成GBK 解码时会出现【涓湅】;换成GB2312 解码时【涓��】……
一、那我们首先看看有多少编码类型:
1、ASCII 码 :
一共有128种字符编码,是对英文字符(字母+符号)和二进制统一的编码,计算机里是用二进制位进行通信的,所以这一套ASCII 码使英文字符在计算机里基本上可以直接使用了,着实给使用英语为母语的国家带来了很大的方便。
2、Unicode 编码:
Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。而且Unicode 有多种存储方式,使用起来不是很方便。
其实Unicode 对我们判断使用汉语的国家人民还有另一种不便,我们如果判断汉语是不是繁体 或是简体的话 其实就很麻烦了,因为Unicode 存储汉语是有一定的范围的,可是繁简体之间是混插的 没有界限的,所以需要自己转码进行辨别。
3、UTF-8:
UTF-8 其实是Unicode 的一种实现方式(还有UTF-16等等)。在互联网中使用的非常广。因为UTF-8 编码可以使整个互联网中实现同意编码,尽量的减少或避免因为编码的差异造成的不必要的麻烦。
UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则:
1)如果是 单字节的符号,那么 令字节的第一位设为0,后面7位为这个符号的unicode码。对于英语字母来说,UTF-8编码和ASCII码是相同的。
2)如果是 n 字节的符号(n>1),第一个字节的前面的 n位都设为1,第n+1位设为0,后面的字节的前两位均为10。其他的二进制位是由这个字符的 Unicode 码 按照顺序进行填充而得。
如下表:
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
----------------------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
4、ISO-8859-1 :
因为除了应该还有其他语言,尤其是西欧 比如法语 会有重音字符,这就造就了ISO-8859-1 编码,ISO-8859-1 是国际标准化组织内ISO/IEC 8859的第一个8位字符集,它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及符号,借以供使用变音符号的拉丁字母语言使用。所以又称Latin-1或“西欧语言”。
5、GB2313 ,Big5,GBK
这几种是正对使用汉字或者类汉字的字符的编码方式。
①GB2312 码 :
兼容ASCII 码 共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位。每个区位上只有一个字符,因此可用所在的区和位来对汉字进行编码,称为区位码。其中主要是简体中文 和 部分繁体中文及日韩的一些汉字。
②Big5 (大5码):
主要是港澳台使用的繁体字的编码集。
③GBK :
它是GB2312的扩展,加入对繁体字的支持,兼容GB2312。字符有一字节和双字节编码,00–7F范围内是一位,和ASCII保持一致,此范围内严格上说有96个文字和32个控制符号。之后的双字节中,前一字节是双字节的第一位。总体上说第一字节的范围是81–FE(也就是不含80和FF),第二字节的一部分领域在40–FE,其他领域在80–FE。
6、其他的不常用 不再介绍
二、编码之间的转换:
如果字符用一种编码方式编码,再用同一样的编码方式解码,那么输出的字符是正常的;要是解码时用的是另一中编码则会出现乱码。
那么我们用代码试试:
public static void main (String[] args) throws java.lang.Exception{
String str = new String("我".getBytes("UTF-8"),"GB2312");
String str2 = new String("我".getBytes("UTF-8"),"UTF-8");
System.out.println("str: "+str+" "+" str2: "+str2);
}
执行的结果:
str: �� str2: 我
这结果可以有力证明乱码的出现 和 其解决办法。
三、分辨编码范围:
若只判断中文字符时可以 判断是不是属于 CJK(中日韩统一表意文字):
public static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
return true;
}
return false;
}
那么判断文简体还是繁体呢? 我觉得还是得判断编码范围了:
思路是 当文字是在GB2312 范围内 时我们认为是简体中文 ,若在Big5 范围内时 是繁体。如下:
private static final String gbEncode = "GB2312";
private static final String big5Encode = "Big5";
public static boolean isJTChinese(Character c) {
String str = c.toString();
try {
if (str.equals(new String(str.getBytes(gbEncode), gbEncode))) {
System.out.println(c+"是简体");
return true;
}
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
public static boolean isFTChinese(Character c) {
String str = c.toString();
try {
if (str.equals(new String(str.getBytes(big5Encode), big5Encode))) {
System.out.println(c+"是繁体");
return true;
}
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
其他的问题同理可以得到答案。
四、第三方库
现成的一个库可以使用 :language-detection。 language-detection 项目链接 https://code.google.com/p/language-detection/
此工具是一个使用java实现的语言检测工具,使用的是朴素贝叶斯方法,对噪声少的语言句子,准确率能达到99%,目前已经能够支持53种语言。
使用非常方便,只要把下载下来的2个jar 导入工程,然后把含有语言包的 profiles 属性文件加载进去即可使用。
测试代码如下:
import com.cybozu.labs.langdetect.Detector;
import com.cybozu.labs.langdetect.DetectorFactory;
import com.cybozu.labs.langdetect.LangDetectException;
public class LangTest {
public static void main(String[] args) {
Detector detect;
try {
//加载语言包
DetectorFactory.loadProfile(Thread.currentThread().getContextClassLoader().getResource("profiles").getPath());
detect = DetectorFactory.create();
detect.append("我是软件工程师。");
System.out.println(detect.detect());
} catch (LangDetectException e) {
e.printStackTrace();
}
}
}
输出的j结果是:zh-cn
好吧,编码之间的一些转码,语言辨别的问题,暂时先学到这,以后还需继续学习。
文章中有什么不足的还需看官不吝赐教……