我正在使用zlib阅读gzip压缩文件。 然后使用
打开文件gzFile gzopen(const char *filepath, const char *mode);
如何处理在Windows上存储为const wchar_t*
的Unicode文件路径?
在类UNIX平台上,您只需将文件路径转换为UTF-8并调用gzopen(), 但这不适用于Windows。
答案 0 :(得分:12)
zlib的下一个版本将包含此函数,其中_WIN32
是#defined:
gzFile gzopen_w(const wchar_t *path, char *mode);
它的工作原理与gzopen()
完全相同,只是它使用_wopen()
代替open()
。
我故意没有复制_wfopen()
的第二个参数,因此我没有将其称为_wgzopen()
以避免可能与该函数的参数混淆。因此名称为gzopen_w()
。这也避免了使用C保留的名称空间。
答案 1 :(得分:11)
文件名以零结尾的字节序列。内核不需要关心字符编码(除了知道/
的ASCII代码)。
但是,从用户的角度来看,将文件名解释为字符的序列会更方便,这可以通过指定为语言环境字符编码来完成>。通过使UTF-8语言环境可用来Unicode is supported。
在C程序中,文件在char*
等函数中用普通的 fopen
字符串表示。 POSIX API没有宽字符版本。如果您有wchar_t*
文件名,则必须明确将其转换为char*
。
文件名是一系列UTF-16代码单元。实际上,Windows中的所有字符串操作都是在内部以UTF-16完成的。
所有Microsoft的C(++)库(包括Visual C ++运行时库)都使用char*
字符串在特定于语言环境的旧版“ANSI”代码页和wchar_t*
字符串中的约定是UTF-16。 char*
函数只是新wchar_t*
函数的向后兼容包装。
因此,如果您拨打MessageBoxA(hwnd, text, caption, type)
,则与调用MessageBoxW(hwnd, ToUTF16(text), ToUTF16(caption), type)
基本相同。当你拨打fopen(filename, mode)
时,就像_wfopen(ToUTF16(filename), ToUTF16(mode))
。
请注意,_wfopen
是许多用于处理wchar_t*
字符串的非标准C函数之一。这不仅仅是为了方便; 您无法使用标准char*
等效项,因为它们将您限制为“ANSI”代码页(can't be UTF-8)。例如,在windows-1252语言环境中,您不能(轻松地)fopen
文件שלום.c
,因为无法用窄字符串表示这些字符。
一些典型的方法是:
char*
字符串一起使用,并且不要在Windows上提供有关非ANSI字符的支持。char*
字符串,但将其解释为UTF-8而不是ANSI。在Windows上,编写包含UTF-8参数的包装函数,将它们转换为UTF-16,并调用_wfopen
之类的函数。不幸的是,它似乎使用上面的天真方法#1,直接使用open
(而不是_wopen
)。
除了已经提到的解决方案(我最喜欢的是Appleman1234的gzdopen
建议),您可以利用symbolic links为文件提供一个替代的全ASCII名称,然后您可以安全地传递给该文件gzopen
。如果文件已经具有合适的short name,您可能甚至不必这样做。
答案 2 :(得分:4)
您有以下选项
#ifdef _WIN32
#define F_OPEN(name, mode) _wfopen((name), (mode))
#endif
修补程序zlib,以便在Windows上使用_wfopen
而不是fopen
,在zutil.h中使用与上面类似的内容
使用_wfopen
或_wopen
代替gzopen,并将返回值传递给gzdopen
。
使用libiconv或其他库从您给定的Unicode编码更改enconding为ASCII的文件,并将ASCII字符串传递给gzopen。如果libiconv失败,则处理错误并提示用户重命名该文件。
有关iconv的更多信息,请参阅An example of iconv。该示例使用日语到UTF-8,但将目标编码更改为ASCII或ISO 8859-1并不是一个很大的飞跃。
有关zlib和非ANSI字符转换的详细信息,请参阅here
答案 3 :(得分:3)
这是Appleman选项#2的实现。代码已经过测试。
#ifdef _WIN32
gzFile _wgzopen(const wchar_t* fileName, const wchar_t* mode)
{
FILE* stream = NULL;
gzFile gzstream = NULL;
char* cmode = NULL; // mode converted to char*
int n = -1;
stream = _wfopen(fileName, mode);
if(stream)
n = wcstombs(NULL, mode, 0);
if(n != -1)
cmode = (char*)malloc(n + 1);
if(cmode) {
wcstombs(cmode, mode, n + 1);
gzstream = gzdopen(fileno(stream), cmode);
}
free(cmode);
if(stream && !gzstream) fclose(stream);
return gzstream;
}
#endif
我已将 filename
和mode
const wchar_t*
与
FILE* _wfopen(const wchar_t* filename, const wchar_t* mode);
答案 4 :(得分:0)
这是我自己的unicode helper函数版本,测试结果略好于上面版本。
static void GetFlags(const char* mode, int& flags, int& pmode)
{
const char* _mode = mode;
flags = 0; // == O_RDONLY
pmode = 0; // pmode needs to be obtained, otherwise file gets read-only attribute, see
// http://stackoverflow.com/questions/1412625/why-is-the-read-only-attribute-set-sometimes-for-files-created-by-my-service
for( ; *_mode ; _mode++ )
{
switch( tolower(*_mode) )
{
case 'w':
flags |= O_CREAT | O_TRUNC;
pmode |= _S_IWRITE;
break;
case 'a':
flags |= O_CREAT | O_APPEND;
pmode |= _S_IREAD | _S_IWRITE;
break;
case 'r':
pmode |= _S_IREAD;
break;
case 'b':
flags |= O_BINARY;
break;
case '+':
flags |= O_RDWR;
pmode |= _S_IREAD | _S_IWRITE;
break;
}
}
if( (flags & O_CREAT) != 0 && (flags & O_RDWR) == 0 )
flags |= O_WRONLY;
} //GetFlags
gzFile wgzopen(const wchar_t* fileName, const char* mode)
{
gzFile gzstream = NULL;
int f = 0;
int flags = 0;
int pmode = 0;
GetFlags(mode, flags, pmode);
f = _wopen(fileName, flags, pmode );
if( f == -1 )
return NULL;
// gzdopen will also close file handle.
gzstream = gzdopen(f, mode);
if(!gzstream)
_close(f);
return gzstream;
}