作者zlw (洞房不败)
看板Programming
标题Re: [问题] Big5跟UTF-8不需要考虑endian跟相容ASC …
时间Mon Feb 2 17:33:03 2009
※ 引述《LPH66 ((short)(-15074))》之铭言:
: 就我对字元编码的所知来回答看看
: 这部份可能是对的
: 由於 unicode 本身并不是 DBCS/MBCS 的编码
: 不需要相容於原有的编码之中
: 因此才必须自己决定要用什麽方式来储存编码
: (因其每个编码的大小是 16-bit = 2 byte
: 等於你要想办法存一个 C 语言中的 short 那麽大的数字
: 当然要考虑 endianness 了)
: 而 DBCS/MBCS 的编码为了要能相容於旧编码 (在此即ASCII)
: 因此需要有一个头的机制来分出从哪一个开始
: 当然基本单位就变成和旧编码 (ASCII) 一样是 1 个 byte 了
: 以 UTF-8 的机制为例就是看到一个 byte 范围在 0xC2~0xF4 之间表示一个头的开始
: 而 Big-5 则是 > 0x7F 做为头这样
: 所以严格说起来 UTF-16 也可以算是 D"B"CS
: (或许该改叫 DCCS, Double-Codepoint Character Set?) (←这是我胡诌的词别当真XD)
: 因为 UTF-16 有所谓的 surrogate pair 的设计 用来表示 U+10000 以上的字元
: 而这个设计是以 U+D800~U+DBFF 做为头 U+DC00~U+DFFF 做为尾的设定
: 和 DBCS 其实还满像的
: 只不过这里的基本单位是 2 byte 因此还是得考虑 endianness
: (也就是为什麽会分出 UTF-16LE UTF-16BE 的原因了)
感谢前辈提供这麽多资讯,对我很有帮助。查了一些资料跟做了一些测试後,比较确定了
闲聊一下。原本对DBCS跟MBCS的名词不太熟悉,看了一些资料後发现,原来这个名词的
定义是有争议的,真的是一团混乱。我想在此补充给还不知道的人
一般多数人认为的 DBCS 就是像 Big5、GBK 这类编码,对於ASCII原有的字照样显示
一个位元组,而ASCII没有的文字,基本上就是把第一个位元组的最高位元改成1,使其
变成lead byte,後面再接一个位元组,两个位元组变成一个对应的字。
可是,像Big5字元集里面,不是每个字都是2 Bytes而会变动,所以有人主张改叫MBCS
(但比较少人用)
又有人主张DBCS就应该是每个字都是2 Byte才算,所以Big5又变成不算DBCS,
反而Unicode才是DBCS (也是比较少数看法)
: 这要稍微扯到 Windows 的判定规则了
: 没有特别处理的话 就我所知
: Windows 是将 native 的编码视为所谓 "ANSI" 编码
: (就是记事本的 "ANSI" 存档选项)
: 这 native 的编码应该就是相容於过去所谓的 code page 的东西
: 像 Big5 => CP950, GB2312 => CP936, Windows的西欧字母 => CP1252 这样
查了资料写说,由於 ANSI 是古早提出 codepage 之类的组织如何如何...
所以对 Windows 使用者来说,ANSI这个字可能不是指那个组织,而就是象徵
windows code page 这东西。
但我不太明白这个 ANSI 编码,是不是指现在不流行 code page 这个概念,
现在强调资料的编码?
所以记事本预设编码是 ANSI,这等於预设编码是采用过去的 code page 概念,
也就是说当编码为ANSI时,则隐含文件开启时会依照控制台的地区设定,然後去找
对应的 code page 对照表来解读,是这样吗?
: 也因此系统里会留着一份各个 code page 的对照表
: (C:\Windows\System32\C_xxx.nls 就是了
: UAO (aka. unicode补完) 也是改动这里的 C_950.nls)
: 在需要时 (下面会提到) 会自动抓这里的对照表来转码
: 而记事本其实它是一支 unicode 程式
: (你可以在记事本里打一些这里打不出来的字符和组合
: 例如你可以在同一篇文件中参杂繁中简中日韩四种文字都没问题)
: 只是它内建有 unicode 侦测 当开档的时候会自动侦测它是不是 unicode
: 如果定是 UTF-16LE/UTF-16BE/UTF-8 三者之一的话
: 就会以那种格式来开启文件
: 否则就会判定为 ANSI 才会让系统以目前的预设编码来找 code page 转码
: 这时才会去看系统设定非 unicode 程式用什麽编码来找
: (以我这台 Vista 的选项来说
: 中文(繁体,台湾) 就是 CP950 同理 中文(简体,中国) 就是 CP936 )
: 顺带一提, UTF-8 也是一个 MBCS 的编码
: 所以它也有自己的 code page 号码: CP65001
: 不过因为 UTF-8 稍微特殊 它并没有对应的 C_65001.nls 在系统里
: 而是直接在系统程式里解决掉了
: 这单纯就是转码的问题而已
: 原因只是因为 Big-5 没有韩文
: 程式要向系统抓目录 系统看到程式使用了非 unicode 的(旧式) API
: 就把抓来的名字转成 "ANSI" 编码送给程式
: 这里的 "ANSI" 编码一样视控制台的那个设定而定
: 那因为 Big-5 里没有韩文 所以韩文目录就变成 ????? 了
: 那当程式要开启叫做 ????? 的目录时 因为程式给系统的是 "?????" 这个名字
: 系统自然回说找不到目录 所以开启失败
: 当你把设定改成韩文後
: 系统抓来的名字转成 "ANSI" 编码时就会去抓 EUC_KR (CP949) 来转码
: 而 EUC_KR 里面就有韩文 所以程式收到了正确转成 EUC_KR 的韩文字
: 再用这些韩文字向系统要求开启目录
: 系统从非 unicode 的 API 收到的字串就由 EUC_KR 转回 unicode
: 就正确的开启了目录
所以像大陆人用的 Windows,因为他们的code page里面包含日文,那同样一个日文假名
目录放在同样硬碟,用同样一个「不支援Unicode」的套装软体去开启。
他们的Windows可以正确把UTF-16目录名称转成对应的GBK名称给那个软体,也就是对应的
一连串 byte 假设像是 0xFF 0x01 0xFF 0x02。那个软体只要把 0xFF 0x01 0xFF 0x02
传给 Windows 就又能转回正确 UTF-16 目录名称存取了。
但我们Windows因为code page不包含假名,Windows没办法转换,只好转成一堆 '?'给
该套装软体。而因为'?'本来就可以对应到UTF-16,所以软体传回去,Windows就会转成
错误的路径。
但是装了UAO後,因为现在Windows可以在Big5的code page找到对应的位置了,假设是
0xFF 0x03 0xFF 0x04,软体再传回去给Windows时就能找到正确的目录。然後造成由於
可以转换日文,而使原本明明不支援Unicode的软体,变得好像支援Uniocde了
: 也就是说, 系统在发现程式和他沟通是使用旧的 API 时
: 就知道这支程式想要旧的行为
: 因此会自动把送来的字串先过一次转码转成 unicode 再送给後端
: 後端回来的字串再过一次转码转成 "ANSI" 编码才送回去给程式
: 这就是上面提的"需要时转码"那个"需要时"
: 而控制台的设定就是设定这里要转成哪一个 code page 而已
谢谢您的说明,查了些 Windows API 资料跟测试了一下,已经搞懂这个了
补充如下
--
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow){
MessageBoxW(NULL, L"测试 Hello World", L"123", MB_OK);
/*宽字元版本(wchar_t)。字最终被编译器存成 Unicode 编码
*/
MessageBoxA(NULL, "测试 Hello World", "123", MB_OK);
/*原始版本(char)。还是存成 C-Style String,但程式运行时OS会依地区设定
解读,比如用Applocale以简体开启,会得到简体字的「代刚」
*/
return 0;
}
--
Windows API 是用C语言编写,所以跟 ANSI C 一样是用 wchar_t 这所谓的宽字元来支援
Unicode,宽字元目前情况下就是等於 unsigned short (2 Bytes)
因此 Windows API 的那些函数很多都分成两个版本,名称结尾A表示ANSI,就是之前说的
code page。而W结尾表示wide character,表示可以处理Unicode。
我把那些要改地区设定才能开启韩文目录的软体,用 Dependency Walker 去查询有
Import 进去的 Windows API 函数,发现都是以A结尾,所以才会那样没错。
虽然说如果是透过 LoadLibrary、GetProcAddress 从dll里面呼叫的函数是没办法从
Import 函数里面找到,但是像一般开档应该不是这样呼叫的。
所以大概能用 Dependency Walker 这类程式去判断一个软体使用ANSI还是宽字元
的函数,判断该软体属於支援 Unicode 还是不支援 Unicode。
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 124.8.150.197