最近写linux下的字幕下载软件时,遇到了中文宽字串和多字节串之间的转换问题,这问题困扰了我不久呀,现在终于开窍了。。。
我们知道(请看《Unicode、UTF-8、多字节字符、宽字符》):多字节字符串”你好\n”在源文件以及目标文件中,都是utf-8编码,一个英文字符1字节,一个汉字3字节。因为strlen是根据一字节一字符来计算字符个数的,所以strlen无法处理多字节字符串。
宽字符L”你好\n”在源文件中是utf-8编码,但在目标文件中是UCS编码的,一个字符占4字节。wstrlen便是依据每4字节为一字符,计算有多少字符。
下面看看linux下宽字符串与多字节字符串之间的转换。
首先,Charconvert.c里包含多字节与宽字节字符串的转换函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* filename:Charconvert.c */ char* WideChar2MultiByte(const wchar_t* szIn) { size_t len=wcslen(szIn)+1; char* szOut=(char*)malloc(sizeof(char)*len); wcstombs( szOut, szIn, len); return szOut; } wchar_t* MutliByte2WideChar(const char* szIn) { size_t len=strlen(szIn)+1; wchar_t* szOut=(wchar_t*)malloc(sizeof(wchar_t)*len); mbstowcs(szOut,szIn,len); return szOut; } |
下面是main函数,定义了宽字串wstr和多字节串mstr。并将wstr转换为多字节串mstr2,将mstr转换为wstr2。另外,我把setlocale一句注释了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* filename:main.c */ #include <stdio.h> #include <wchar.h> #include <string.h> #include <locale.h> char* WideChar2MultiByte(const wchar_t* szIn); wchar_t* MutliByte2WideChar(const char* szIn); int main() { wchar_t* wstr=L"/home/zhao/魔境仙踪.chn.srt"; char* mstr="/home/zhao/魔境仙踪.chn.srt"; printf("sizeof(wchar_t):%d\n",sizeof(wchar_t)); printf("len of wstr:%d\n",wcslen(wstr)); printf("len of mstr:%d\n",strlen(mstr)); /* setlocale(LC_ALL,""); */ wchar_t* wstr2=MutliByte2WideChar(mstr); printf("wstr2:%ls\n",wstr2); char* mstr2=WideChar2MultiByte(wstr); printf("mstr2:%s\n",mstr2); } |
编译:gcc main.c Charconvert.c -g
运行结果:
sizeof(wchar_t):4
len of wstr:23
len of mstr:31
wstr2:/home/zhao/
mstr2:/home/zhao/
程序输出结果就是/home/zhao/,中文以后的内容就被截断了,这是什么情况呢
我们用gdb来查看下wstr:
x/16b wstr
0×602010: 47 0 0 0 104 0 0 0
0×602018: 111 0 0 0 109 0 0 0
这里只列出了16字节,其实wstr总共是4*23字节这些数字是10进制的。
其中每32位就是一个字符的UCS码,比如47表示‘/’,数值上跟acsii码相等,占4字节;104表示‘h’,数值上也跟acsii码相等,占4字节
再看下mstr:
x/32b mstr
0×400998: 47 104 111 109 101 47 122 104
0x4009a0: 97 111 47 -23 -83 -108 -27 -94
0x4009a8: -125 -28 -69 -103 -24 -72 -86 46
0x4009b0: 99 104 110 46 115 114 116 0
str刚好总共32字节,这里的十进制数是UTF-8编码,47表示‘/’,占1字节;104表示‘h’,占1字节;-23 -83 -108表示‘魔’,占3字节。
看看mstr2:
x/32b mstr2
0x6020a0: 47 104 111 109 101 47 122 104
0x6020a8: 97 111 47 0 0 0 0 0
0x6020b0: 0 0 0 0 0 0 0 0
0x6020b8: 0 0 0 0 0 0 0 0
从‘魔’字开始的字节都变成零了。wstr2内存里也是这样。所以程序输出结果就是/home/zhao/,中文以后的内容就被截断了。
现在我们把setlocale(LC_ALL,””)这句的注释符号去掉。再编译运行看看:
sizeof(wchar_t):4
len of wstr:23
len of mstr:31
wstr2:/home/zhao/魔境仙踪.chn.srt
mstr2:/home/zhao/魔境仙踪.
呵呵,”mstr2:/home/zhao/魔境仙踪.”少了一截,这又是什么情况呢?
显然是WideChar2MultiByte中长度出问题了。。。
发表评论
要发表评论,您必须先登录。