存档

  • GBK、Shift-JIS、BIG5编码检测算法

    字符串的编码检测需要使用自定义的映射表,使用系统自带的Codepage是不大可能有准确率的,系统Codepage会将它所有没定义的字符映射为空格。 GBK、Shift-JIS、BIG5的码表空间都是不连贯的,而它们的有效空间也不完全重合,这为检测编码类型提供了可能性。 检测算法: 1、建立字符映射表:将任一ANSI编码的所有字符做全映射,从0×00到0xFFFF都有Unicode字符对应,但需要注意的是没有定义的字符统统映射到Unicode的0xFFFD(共三个映射表,既可用于检测也可用于转换)。 2、预设字符串的编码是Shift-JIS。 3、使用Shift-JIS的映射表从字符串第一个字符开始检测直至最后一个字符。如果遇到有字符映射到0xFFFD,设置预设编码是GBK,立刻停止步骤3,跳至步骤4。 4、如果预设编码是GBK,使用GBK的映射表从字符串第一个字符开始检测直至最后一个字符。如果遇到有字符映射到0xFFFD,设置预设编码是BIG5,立刻停止步 骤4,跳至步骤5。 5、如果预设编码是BIG5,使用BIG5的映射表从字符串第一个字符开始检测直至最后一个字符。如果遇到有字符映射到0xFFFD,设置预设编码是未知编码,立刻停止步 骤5,跳至步骤6。 6、返回预设编码。 这个算法的编码检测优先度是Shift-JIS>GBK>BIG5,也即如果顺利通过当前检测,则跳过后面所有检测。事实上,有大量字符串是能通过所有检测的。例如只有一个字符的字符串,假设这个字符是0×8140,在三个编码当中,都不会映射到Unicode的0xFFFD,因此能通过所有检测。但这没意义了。设定了优先度后是为了告诉用户最可能的一种编码。 为什么设定Shift-JIS>GBK>BIG5? Shift-JIS的码表空间是0×00-0x7F、0xA1-0xDF、0×8140-0xFC4B GBK的码表空间是0×00-0x7F、0×8140-0xFEFE BIG5的码表空间是0×00-0x7F、0×8140-0xFEFE 但双字节段(0×8140以上)都不是全部已定义,Shift-JIS在0×8140以上的有效字符数是7724,GBK是21885,BIG5是19782。 GBK的覆盖面最大,有效空间基本覆盖了Shift-JIS,因此一个字符串如果能通过Shift-JIS检测,也差不多能通过GBK检测。如果将GBK的优先度设得比Shift-JIS高,那么大量真正是Shift-JIS编码的字符串就压根没机会返回给用户了。从反方向看,GBK中存在数量庞大的字符Shift-JIS没定义,Shift-JIS是高度覆盖不住GBK的,一个GBK文本从概率上没那么容易检测成Shift-JIS。也即:如果一个文本的真正编码是Shift-JIS,那么优先使用Shift-JIS检测自然不会有问题;如果它是GBK,那么优先使用Shift-JIS检测也不大会返回Shift-JIS。因此Shift-JIS应当优先于GBK。 Shift-JIS和BIG5的关系的考虑也类似。 从转换日系音乐cue、日文小说的宅用途出发,也应当将Shift-JIS设置为最高。 下面三张图是是Shift-JIS编码的小说“文学少女”と死にたがりの道化的转换结果。左边是当作本地编码的处理结果,可以无视。右边才是转换结果。 (程序和文本下载:http://code.google.com/p/unicue/downloads/detail?name=Ansi2Unicode_1.01.zip) 使用Shift-JIS映射表转换,结果自然是正确的。 强行使用GBK映射表转换,没有出现标记0xFFFD(0xFFFD:�),也即能通过GBK检测 强行使用Big5转换,出现0xFFFD标记,也即通不过BIG5检测。BIG5跟Shift-JIS的有效空间重合度没那么高,区分相对容易一点 另外一个GBK文本强行使用Shift-JIS转换的结果。很容易就出现了0xFFFD标记 那么GBK和BIG5的优先度应该谁高呢?这里就见仁见智了。GBK的字符数比BIG5多,从概率上GBK相对容易覆盖住BIG5,BIG5相对不容易覆盖住GBK。倘若采用Shift-JIS和GBK之间的比较方法,应该是BIG5的优先度比GBK高。但从实际情况来看,真正BIG5编码的文本强行使用GBK映射表转换比较容易出现0xFFFD标记,真正GBK编码的文本强行使用BIG5映射表转换反而不容易出现0xFFFD标记。 GBK文本强行使用BIG5映射表转换的结果,不容易出现0xFFFD标记 BIG5文本使用BIG5映射表转换,正常的结果 BIG5文本强行使用GBK映射表转换,很容易出现0xFFFD标记 现实结果和表面现象完全相反。如果一个文本的真正编码是GBK,那么优先使用GBK检测自然不会有问题;如果它是BIG5,那么优先使用GBK检测也不大会返回GBK。因此余倾向于GBK的优先度高于BIG5。

    1,687 次浏览 | 1 条评论
    2010年6月8日 | 归档于 程序
  • Ansi to Unicode 1.0发布

    一个可用的版本终于出来了。 完成Shift-JIS、GBK、Big5到Unicode的自定义映射表,可以实现这三个编码的文本到Unicode的转换。 对转换cue编码来说,应该还算便利,但仅支持保存为UTF-8编码,余也不打算支持保存为本地编码 功能还很简单,会慢慢完善。下一步工作是利用自定义的这三个映射表判断文本的原始编码,这可能会有点技术 全部源码析出,包括制作映射表文件的代码。 程序及源码下载:http://code.google.com/p/unicue/ 欢迎反馈意见。

    655 次浏览 | 没有评论
    2010年6月6日 | 归档于 程序
  • 一个线性时间复杂度的字符编码转换算法

    win32 api提供的字符编码转换函数MultiByteToWideChar和WideCharToMultiByte的效率如何不大清楚,一般用途的情况下,字符串不会太长,线性还是O(n^2)还是指数复杂度,时间差是不会感觉出来的。不过这篇博文提出的算法,是确确实实的线性时间复杂度,也即O(n)。n是字符串的长度。余没有读过FireFox的字符转换代码,但应该也是线性时间复杂度。话说回来,这玩意如果能设计成O(n)以上,那还真是活见鬼了。这个算法的意义是,如果你不满意系统提供的转换函数时,可以自己整一套出来。 这个算法已经用在UniCue的Big5ToUnicode程序中。源码下载:http://unicue.googlecode.com/svn/trunk/,装个svn客户端会下得畅快点。 字符编码转换的关键是建立一个字符映射表。比如: #Big5     Unicode 0×8140 0x4E17 0×8141 0x4E22 0×8142 0x4E2C 0×8143 0x4E55 0×8144 0x4E62 0×8145 0x4E8A 字符映射表可以是一个双向表,通过一方编码查询对方编码。但双向表的效率很低,双向转换还不如分成两个独立的单向转换。 上面的表其实是以Big5为主键的单向表,可以实现Big5到Unicode的查询(反过来查询不现实)。可以将它保存为txt文本,每次转换打开txt查询,不过只有傻子才这样做。 这里是一个相对完全的Big5到Unicode的映射:uao250-b2u.txt,由Mozilla TW提供。Windows系统自带的映射表CP950字符不全,Big5码中的日文假名到Unicode的映射缺失,导致使用MultiByteToWideChar转换或者通过IE转换会丢失字符。 既然不能使用txt文本来查询映射,那如何查询速度才快呢? 如果按Big5码的顺序将对应的Unicode字符全部放置在内存中,起始地址(假设是A)的Unicode字符对应的就是Big5的0×8140,如果来了一个Big5字符假设是0x9C5A,那么它所对应的Unicode字符的内存地址就是A+(0x9C5A-0×8140)*2(注意一个Unicode字符占据两个字节),访问这个地址读取两个Byte就得到Unicode字符。指针加偏移量,访问时间是常数,整个字符串转换不就是线性时间复杂度了嘛。 我们打开uao250-b2u.txt,共19782个Big5字符,最后一个字符0xFEFE,第一个字符0×8140,0xFEFE-0×8140再加1,是32191。等等!这可与文本中的字符数目不吻合啊?原来Big5编码并没有连续的占据0×8140~0xFEFE的全部空间,我们在使用uao250-b2u.txt建立映射表时必须将未定义的空间也全部填写上,映射到Unicode的什么字符其实没关系,不过看到有文章推荐是映射为0xFFFD。 单向映射表可以写代码时就码进去,当然还真有人这么干,也可以保存为硬盘上的一个文件,在进行转换时才读取文件,拷贝文件中的二进制流重建映射表。 下面是生成映射表文件的代码,可以以LittleEndian或BigEndian方式存放Unicode字符,但推荐LittleEndian方式。LittleEndian方式的映射表文件在拷贝到内存时,高低位顺序是正确的,可以直接赋给WCHAR字符。代码需配合Google svn上的uao250-b2u.txt(删除了Mozilla TW版的第一行和最后的空行)使用。输出得到的映射表文件b2u-little-endian.map大小是64382字节。 自定义的字符映射表这样就创建完毕啦。使用就很简单了。 使用了MFC中的CString和CFile的转换函数,当然也可以不用MFC这一套 Big5toUnicode中的b2u-CString.h: 可执行的测试程序在此:http://code.google.com/p/unicue/downloads/list

    921 次浏览 | 1 条评论
    2010年5月26日 | 归档于 程序
  • UniCue项目始动

    UniCue项目正式启动。 很久以前就想做一个编码转换工具。在xp时代用apploc+记事本配合转换cue还算便捷,但到了win7 apploc失效。某萌烈弄了一个FixCue,成为余win7转换cue居家必备之工具,但估计某烈也只是弄了Shift-JIS到utf-8的转换。GBK的cue可用记事本直接打开再utf-8另存,只因为中文简体windows默认Codepage是CP936。如果遇到呆丸的自抓,一般都会以big5编码保存cue,一点办法都没,微软的CP950不含日文假名到Unicode的映射,导致日文假名在转换过程中丢失。很多转换程序都是使用win32 api,如Notepad++,存在同样问题。 简体中文系统里只好自己编程解决。不用系统的codepage,意味着要自己制作字符映射表和编写转换函数。这里面涉及到一些设计策略,放在以后的博文再谈。 经过一晚上的奋斗,终于成功实现BIG5码到Unicode的转换。Unicode到UTF-8使用win32 api或者自写函数都很简单。 籍此一年前就计划的UniCue正式启动。预览版已经发布,代码放在Google上托管:http://code.google.com/p/unicue/

    1,046 次浏览 | 2 条评论
    2010年5月15日 | 归档于 程序
文章标签 ‘Codepage’