遇到一個字串顯示不正常的問題,語言設定中文時不會有問題,設定成日文就會。
找到最後發現程式某個地方用到反轉字串,使用了 _tcsrev
來完成。
由於專案比較舊,編碼集是用 MBCS 而不是 Unicode,
當設定成 MBCS 時,_tcsrev
被當作 _mbsrev
,
如果文字內容是 "a一b" (BIG5 編碼是 0x61 0xa4 0x40 0x62),
經過 _mbsrev
轉換後,在不同的語言設定下,會有不同的結果:
// 轉換的程式
CString str = "a一b";
// str = "a一b" (0x61 0xa4 0x40 0x62)
LPTSTR p = str.GetBuffer(0);
_mbsrev((unsigned char *)p);
電腦語言設定中文 | 電腦語言設定日文 | |
---|---|---|
使用 _mbsrev |
0x62 0xa4 0x40 0x61 |
0x62 0x40 0xa4 0x61 |
很好奇為什麼會有這樣差別,去看了一下 _mbsrev
文件, 發現文件有另外一個 function _mbsrev_l
unsigned char *_mbsrev(
unsigned char *str
);
unsigned char *_mbsrev_l(
unsigned char *str,
_locale_t locale
);
看起來是因為 _mbsrev
的實作中預設會使用電腦系統的語言,而 _mbsrev_l
來讓使用者指定語言,
測試如下:
電腦語言設定中文 | 電腦語言設定日文 | |
---|---|---|
使用 _mbsrev |
0x62 0xa4 0x40 0x61 |
0x62 0x40 0xa4 0x61 |
使用 _mbsrev_l ,locale 設為中文(zh-TW ) |
0x62 0xa4 0x40 0x61 |
0x62 0xa4 0x40 0x61 |
使用 _mbsrev_l ,locale 設為日文(ja-JP ) |
0x62 0x40 0xa4 0x61 |
0x62 0x40 0xa4 0x61 |
果然,如果用 _mbsrev
就直接看電腦語言,發現 0xa4 0x40
是中文字 一
的 Big5 編碼,
反轉的時候不會反轉到他;而 _ismbblead_l
就是讓使用者指定語言的。
所以如果有一個 Big5 編碼的文字檔要在日文電腦處理,專案又設定成 MBCS 時,要用反轉字串的功能,需要用 _mbsrev_l
來指定語言。
那 _mbsrev
裡面是怎麼根據 locale
的不同而知道那些字是兩個 byte,而不反轉呢?
找到 source code 發現是這樣處理的:
unsigned char * _RTLENTRY _EXPFUNC _mbsrev(unsigned char *s)
{
unsigned char *p, *q;
unsigned char t;
/* first, reverse 1st and 2nd byte couple of double byte code */
for (p = s; *p; p++)
{
if (_ismbblead(*p))
{
p++;
if (*p == '\0')
break;
t = *p;
*p = p[-1];
p[-1] = t;
}
}
/* second, reverse byte stream */
for (q = s, p--; q < p; q++, p--)
{
t = *q;
*q = *p;
*p = t;
}
return s;
}
可以看到如果 _ismbblead
回傳 true 的話,
就代表接下來兩個 byte 先反轉一次,最後在反轉回來,就等於沒反轉。
那 _ismbblead
又是什麼呢?
函式定義如下:
int _ismbblead(
unsigned int c
);
int _ismbblead_l(
unsigned int c,
_locale_t locale
);
文件指出 如果整數c是多位元組字元的第一個位元組,傳回非零值
,
中文、日文、韓文都是屬於 DBCS (double-byte character set),
也就是說如果要檢查的這個 byte,剛好是該編碼的 Lead byte (first byte),就會回傳 true
。
一樣會有兩個,其中 _ismbblead
是直接看電腦設定的語言;
_ismbblead_l
是可以讓使用者指定語言,測試如下:
其中 Lead byte 的定義在維基百科[4][5]可以找到,整理如下:
系統語言 | locale | 編碼方式 | Lead byte |
---|---|---|---|
中文 | zh-TW |
BIG5 |
0x81-0xfe 、 0xa1-0xf9 |
日文 | ja-JP |
Shift-JIS |
0x81-0x9F 、0xE0-0xEF 、0xF0-0xFC |
而我用 _ismbblead_l
測試結果也如上,只要參數是給該語系的 Lead byte,就會回傳 true
。
- 專案的編碼方式盡量能用
Unicode
。 - 如果被迫只能用
MBCS
,又要支援多國語言的話,使用到 locale 相關的函式需要注意。 - 好用的 Debug 工具 - hexdump,可以直接用
十六進位表示法
來檢視文字檔內容。
.