Android中字符乱码问题


在Android系统设备中,如果有包含简体中文或繁体中文标题的歌曲时,有时候会看到乱码的现象,这是怎么回事?
要想知道答案,需要先了解下字符编码相关知识。

字符乱码问题由来

PC出现的早期,不同国家或区域对自己的文字制定了编码规范,大家各自为政,没有标准化
示例:
编码方案A: 代码 100 内容:”###“
编码方案B: 代码 100 内容:”@@@“
问题出现了:将编码方案A编码的内容使用在编码方案B中,会显示预期之外的内容如乱码等。
在此例子中,如果某文字用方案A编码,但是用方案B解码,则本应该显示”###“的文字会变成”@@@“


如何解决编码问题?
为解决各地区不同标准产生的编码问题,需要一种统一的编码方式,这便是ISO 10646和Unicode的由来。

各种不同的编码

1. ISO 10646
ISO 10646标准由国际标准化组织ISO颁布,用来实现全球所有文种的统一编码;
ISO 10646定义了标准字符集(Universal Character Set,UCS);
历史上存在两个独立的尝试创立单一字符集的组织,即国际标准化组织(ISO)和多语言软件制造商组成的统一码联盟即Unicode。
前者开发了ISO/IEC 10646 项目,后者开发了统一码项目。因此最初制定了不同的标准。
1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。

2.Unicode
Unicode:统一码,为每一个字符定义唯一的代码(即一个整数);
目前的 Unicode 字符分为 17 组编排, 每组称为平面(Plane),而每平面拥有65536个代码点;
UCS-2即Unicode 0号平面字符集;
Unicode字符平面映射 http://zh.wikipedia.org/wiki/Unicode%E5%AD%97%E7%AC%A6%E5%B9%B3%E9%9D%A2%E6%98%A0%E5%B0%84
Unicode 编码表 http://zh.wikibooks.org/wiki/Unicode
Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF),如UTF-16,UTF-8等;


UTF-8: 
8-bit Unicode Transformation Format,是一种针对Unicode的可变长度字符编码;

UTF-8使用一至四个字节为每个字符编码:
 - ASCII字符只需一个字节编码;
 - 拉丁文等 需要二个字节编码; 
 - 其他基本多文种平面(BMP)中的字符三字节编码
 - 其他极少使用的Unicode 辅助平面的字符使用四字节编码

Unicode和UTF-8之间的转换关系表 
         UCS-4编码           UTF-8字节流 
U+00000000 – U+0000007F 0xxxxxxx 
U+00000080 – U+000007FF 110xxxxx 10xxxxxx 
U+00000800 – U+0000FFFF 1110xxxx 10xxxxxx 10xxxxxx 
U+00010000 – U+001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-16: 
16bit Unicode Transformation Format,把Unicode的码位转换为16比特长的码元;

UTF-16是UCS-2的父集,在BMP中,UTF-16编码就等于UCS码;
在辅助平面中,UTF-16使用“代理对”的方法进行编码;
代理对编码方法:
  1.码位减去0x10000, 得到的值的范围为20比特长的0..0xFFFFF. 
  2.高位的10比特值加上0xD800,得到高位代理(范围0xD800..0xDBFF);
  3.低位的10比特值加上0xDC00,得到低位代理(范围0xDC00..0xDFFF);

高位代理、低位代理、BMP中的有效字符的码位,三者互不重叠;

字节序:
  - 不同平台使用不同字节序;
  - 为了确定UTF-16文件的大小尾序,在采用UTF-16编码文件的开头,都会放置BOM(Byte Order Mark)信息,FF FE代表UTF-16LE,FE FF代表UTF-16BE;
  - U+FEFF,U+FFFE字符在UNICODE中代表的意义是ZERO WIDTH NO-BREAK SPACE,它是个没有宽度也没有断字的空白;
  - U+6731,UTF-16LE编码为“31 67”,UTF-16BE编码为“67 31”; 

  


3.ASCII
American Standard Code for Information 美国信息交换标准代码;
单字节编码,使用7位二进制数表示,编码范围为0 - 127,可表达128个字符;
ASCII码字符不存在乱码问题,因为绝大部分地区编码都兼容ascii;

扩展ASCII:
标准ASCII码只用到一个字节中的7位,如果使用第8位,则可扩展编码范围128 - 255,扩展后的标准即为ISO8859-1,也称为 Latin-1

4. ISO8859
- ISO 8859,全称ISO/IEC 8859,是一个8位字符集的标准,现时定义了15个字符集
- ASCII收录了空格及94个“可印刷字符”,足以给英语使用。但是,其他使用拉丁字母的语言(主要是欧洲国家的语言),都有一定数量的附加符号字母,故可以使用ASCII及控制字符以外的区域来储存及表示。
- 兼容ASCII,0x20 - 0x7F为ASCII,0xA0-0xFF为ISO 8859-x字符
- Unicode 0x00-0xFF范围的字符由 0x00-0x7F(ASCII) + 0x80-0x9F(控制符) + 0xA0-0xFF(ISO 8859-1)组成

各种ISO 8859字符集 
ISO/IEC 8859-1 (Latin-1) - 西欧语言 
ISO/IEC 8859-2 (Latin-2) - 中欧语言 
ISO/IEC 8859-3 (Latin-3) - 南欧语言。世界语也可用此字符集显示。 
ISO/IEC 8859-4 (Latin-4) - 北欧语言 
ISO/IEC 8859-5 (Cyrillic) - 斯拉夫语言 
ISO/IEC 8859-6 (Arabic) - 阿拉伯语 
ISO/IEC 8859-7 (Greek) - 希腊语 
ISO/IEC 8859-8 (Hebrew) - 希伯来语(视觉顺序) 
ISO 8859-8-I - 希伯来语(逻辑顺序) 
ISO/IEC 8859-9(Latin-5 或 Turkish)- 它把Latin-1的冰岛语字母换走,加入土耳其语字母。 
ISO/IEC 8859-10(Latin-6 或 Nordic)- 北日耳曼语支,用来代替Latin-4。 
ISO/IEC 8859-11 (Thai) - 泰语,从泰国的 TIS620 标准字集演化而来。 
ISO/IEC 8859-13(Latin-7 或 Baltic Rim)- 波罗的语族 
ISO/IEC 8859-14(Latin-8 或 Celtic)- 凯尔特语族 
ISO/IEC 8859-15 (Latin-9) - 西欧语言,加入Latin-1欠缺的芬兰语字母和大写法语重音字母,以及欧元(€)符号。 
ISO/IEC 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。 
由于英语没有任何重音字母(不计外来词),故可使用以上十五个字集中的任何一个来表示。


6.代码页
代码页(Code Page),也称“内码表”,是特定语言的字符集的一张表;
早期,字符集编码信息存放在ROM中,被称为OEM代码页(IBM PC使用);
微软针对不同的使用地区与国家,定义了一系列的支持不同语言字符集的代码页,被称作"Windows (或ANSI) 代码页";
不同的厂商对同一个字符集编码使用各自不同的名称。例如,UTF-8在IBM称作代码页1208, 在微软称作代码页65001, 在SAP称作代码页4110;
微软系统中,中日韩语言代码页:
932 — 日文 (Shift-JIS)
936 — 简体中文(GBK) 
949 — 韩文 (EUC-KR)
950 — 繁体中文(Big5)


GB-x:简体中文编码
GB2312:
中华人民共和国国家标准简体中文字符集,共收录6763个汉字,覆盖中国大陆99.75%的使用频率

对于人名、古汉语等方面出现的罕用字,GB 2312不能处理每个汉字及符号以两个字节来表示,兼容ASCII


GB13000:
Unicode BMP平面汉字。1993年,Unicode 1.1版本推出,收录中国大陆、台湾、日本及韩国通用字符集的汉字,总共有20,902个。

中国大陆将之定为GB13000


GBK:

汉字内码扩展规范,K为汉语拼音 Kuo Zhan(扩展)中“扩”字的声母

相当于GB 2312 + GB 13000。由于GB 2312-80只收录6763个汉字,有不少汉字并未有收录在内,于是厂商微软利用GB 2312-80未使用的编码空间,收录GB13000.1-93全部字符制定了GBK编码字符有一字节和双字节编码。对于单字节,00–7F范围即ASCII,对于双字节,第一字节的范围是81–FE,第二字节的范围在40–7E及80–FE


GB18030:
国家标准GB 18030-2005《信息技术 中文编码字符集》,是中华人民共和国现时最新的内码字集
与GB 2312-1980完全兼容,与GBK基本兼容,支持GB 13000及Unicode的全部统一汉字,共收录汉字70244个

Big5:繁体中文编码
Big5,又称为大五码或五大码,繁体中文地区中最常用的汉字字符集,共收录13,060个汉字
双字节字符集,“高位字节”使用了0xA1-0xF9,“低位字节”使用了0x40-0x7E,及0xA1-0xFE (CP950)
由于很多日常用字未被收录(“着”,“柏”,“喆”等),所以在市面上支持Big5码的软件(如仓颉输入法),有不少都自行在原本的编码外,添加一些符号及用字


编码检测

如何知道当前字符是何种编码?
不同代码页使用的代码范围不一样,通过检测代码范围来得到一个字符可能的编码。
注意,中日韩等象形文字的编码范围有可能会有重叠部分,因此某个字符检测可能得到多个结果。
如果是判断某个字符串,则可检测每个字符可能的编码,对每一个结果进行与操作,则可得到更准确的结果。


编码          范围(只有部分信息,实际请参考代码页)
Shift-JIS     高位字节0x81-0xFC,低位字节0x40-0x7E,…,0x -0xFC,
GBK           高位字节0x81-0xFE,低位字节0x40-0x7E,0x80-0xFE
Big5          高位字节0xA1-0xF9,低位字节0x40-0x7E,0xA1-0xFE
EUC-KR        高位字节0x81-0xFD,低位字节0x41-0x5A,0x61-0x7A,0x81-0xFE

注意编码范围不是连续的,因此在实现过程中,需要根据代码页把其所有范围列出来,检测编码时判断字符是否在该范围
Android使用 微软的代码页:http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/
例:
GBK编码范围
{
 {0x8140,0x817E},
 {0x8180,0x81FE},
 {0x8240,0x827E},
 ...
}


编码转换

ICU:International Components for Unicode,  Unicode国际化组件;

官网:http://site.icu-project.org/

开源软件,有两种版本:ICU4J(Java)和ICU4C(C,C++)
ICU主要功能:
  代码页转换
  字符比较器(Collation)
  日期,时间,货币,数字等格式转换
  时区计算
  Unicode支持
  正则表达式
  处理文本排向(Bidi )
  文本边界

ICU提供一个转换器,可以将代码页编码(如GBK)转换成Unicode编码
同一种编码可能有多个别名,创建转换器时可以输入别名
转换器别名信息可从android源码中获取,路径为: android/external/icu4c/data/mappings/convrtrs.txt 
或者从 该地址查看Demo: http://demo.icu-project.org/icu-bin/convexp


使用ICU4C完成不同编码的转换
步骤:
1.创建转换器
convDest = ucnv_open(name,…); //目标转换器,打开转换器如”GBK”,”UTF-8”,返回converter对象
convSrc = ucnv_open(name,…);  //源转换器

2.转换
ucnv_convertEX(convDest,convSrc,pDest,pDestLen,pSrc,pSrcLen,…)

3.关闭转换器
ucnv_close(convDest);
ucnv_close(convSrc);


结论

了解了字符编码相关知识后,再来看乱码现象。
目前智能手机,平板等设备基本使用unicode字符集,对于unicode字符,可以正常显示,而非unicode字符则需要先转换成unicode,否则会显示乱码。
在转换之前,需要检测编码。由于中日韩文字的编码有重叠部分,检测时有可能得到多个结果,因此在检测结果上还要加个条件,即根据当前设置的语言来决定最终结果。
所以如果设置当前语言为简体中文,查看繁体中文或日韩文编码信息的歌曲时会看到乱码。
抛开效率问题,这种情况其实可以解决,通过判断检测结果是否唯一,如果是则将对应编码转换成utf8即可。

有些mp3歌曲信息是unicode编码,所以无论设置哪种语言都可以显示正确。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值