EUC-JP编码常见于日文网页、数据库。但是,用过GNU的recode或者iconv的可能会有体会,就是往往一个euc-jp编码的文件并不能成功地转换为其他编码格式,碰到像罗马数字这样的字符就会出错。这是因为这些都属于euc-jp标准的扩充,虽然实际在使用但是GNU的工具并不支持。
在Windows下,可以采用MultiByteToWideChar和WideCharToMultiByte来转换euc-jp编码的文件。这里euc-jp编码对应codepage 20932。但是这样仍然不能覆盖全部的字符集。包含全部扩充文字(NEC選定IBM拡張文字等)的euc-jp编码对应于codepage 51932,但是Windows的API并不支持。据说只有IE使用的MLang的ConvertINetString之类的函数才能使用codepage 51932。但是我试过在XP英文版下用gcc编译的程序仍然不能使用cp51932。
测试代码1:
// gcc -Wall conve.c mlang.dll
#include
#include
#include
#include
#include
#define MAX_BUF 256
extern HRESULT ConvertINetString(LPDWORD, DWORD, DWORD, LPCSTR, LPINT, LPSTR, LPINT);
int main(int argc, char *argv[])
{
DWORD mode = 0, srcenc, dstenc;
BYTE src[MAX_BUF], dst[MAX_BUF*2];
UINT srclen, dstlen;
FILE *fp, *gp;
int line = 0;
srcenc = 65001;
dstenc = 936;
if (argc > 1)
srcenc = atoi(argv[1]);
if (argc > 2)
dstenc = atoi(argv[2]);
printf("From %lu to %lu/n", srcenc, dstenc);
fp = stdin;
gp = fopen("log.tmp", "w");
while (!feof(fp) && fgets(src, MAX_BUF, fp))
{
srclen = strlen(src);
if (srclen > 0 && src[srclen - 1] == '/n')
--srclen;
printf("Line %d: ", ++line);
fprintf(gp, "Line %d: %.*s/n", line, srclen, src);
fflush(gp);
switch (ConvertINetString(&mode, srcenc, dstenc,
src, &srclen, dst, &dstlen))
{
case S_OK:
break;
case S_FALSE:
fprintf(stderr, "The specified conversion is not supported on the system./n");
//exit(1);
break;
case E_FAIL:
fprintf(stderr, "An error occurred./n");
break;
}
printf("%d/n", dstlen);
printf("%.*s/n", dstlen, dst);
}
fclose(gp);
return 0;
}
测试代码2:
// gcc -Wall test.c -lole32 -luuid
// needs patched mlang-uuid.c to build libuuid
#include
#include
#include
#include
#include
#include
#include
#define MAX_BUF 256
int main(int argc, char *argv[])
{
IMultiLanguage *p;
DWORD mode = 0, srcenc, dstenc;
BYTE src[MAX_BUF], dst[MAX_BUF*2];
UINT srclen, dstlen;
FILE *fp;
int line = 0;
CoInitialize(NULL);
if ( FAILED(CoCreateInstance(&CLSID_CMultiLanguage ,NULL,
CLSCTX_ALL, &IID_IMultiLanguage2 ,(void**)&p)) )
{
return -1;
}
srcenc = 65001;
dstenc = 936;
if (argc > 1)
srcenc = atoi(argv[1]);
if (argc > 2)
dstenc = atoi(argv[2]);
printf("From %lu to %lu/n", srcenc, dstenc);
fp = stdin;
while (!feof(fp) && fgets(src, MAX_BUF, fp))
{
srclen = strlen(src);
if (srclen > 0 && src[srclen - 1] == '/n')
--srclen;
printf("Line %d: ", ++line);
//assert( p->lpVtbl->ConvertString != NULL);
switch ( p->lpVtbl->ConvertString(p, &mode, srcenc, dstenc,
src, &srclen, dst, &dstlen) )
{
case S_OK:
break;
case S_FALSE:
fprintf(stderr, "The specified conversion is not supported on the system./n");
//exit(1);
break;
case E_FAIL:
fprintf(stderr, "An error occurred./n");
break;
}
printf("%d/n", dstlen);
printf("%.*s/n", dstlen, dst);
}
p->lpVtbl->Release(p);
CoUninitialize();
return 0;
}
既然用API的方法不行,那么究竟怎么办呢?
解决方法其实很简单:打上日本人做的libiconv补丁就行了。地址: http://www2d.biglobe.ne.jp/~msyk/software/libiconv-patch.html。然后就可以使用CP51932作为完整的euc-jp编码来进行转换了。这就跟iconv的GBK不完整,用GB18030就全了类似。