如何在 Linux 中打开包含非 Ascii 字符串的 wchar_t* 文件?
Posted
技术标签:
【中文标题】如何在 Linux 中打开包含非 Ascii 字符串的 wchar_t* 文件?【英文标题】:How to open a file with wchar_t* containing non-Ascii string in Linux? 【发布时间】:2011-06-08 06:14:36 【问题描述】:环境:Gcc/G++ Linux
我在文件系统中有一个非 ascii 文件,我要打开它。
现在我有一个 wchar_t*,但我不知道如何打开它。 (我信任的 fopen 只打开 char* 文件)
请帮忙。非常感谢。
【问题讨论】:
文件名不是 ASCII,还是内容不是 ASCII,或者两者兼而有之? 是的,两者都有。有 wfstream 可以将 wchar 读/写到文件中,但 wfstream 也只打开 char* 文件。 将 wchar 转换为 utf8 char 并尝试 fopen() 吗? 【参考方案1】:有两种可能的答案:
如果您想确保所有 Unicode 文件名都是可表示的,您可以硬编码文件系统使用 UTF-8 文件名的假设。这是“现代”Linux 桌面应用程序方法。只需使用库函数(iconv
可以很好地工作)或您自己的实现将您的字符串从 wchar_t
(UTF-32)转换为 UTF-8(但请查找规范,这样您就不会像 Shelwien 那样搞错了) ,然后使用fopen
。
如果您想以更面向标准的方式做事,您应该使用wcsrtombs
将wchar_t
字符串转换为区域设置编码中的多字节char
字符串(希望在任何情况下都是UTF-8现代系统)并使用fopen
。请注意,这要求您之前使用setlocale(LC_CTYPE, "")
或setlocale(LC_ALL, "")
设置区域设置。
最后,不完全是一个答案,而是一个建议:
将文件名存储为wchar_t
字符串可能是一个可怕的错误。您应该将文件名存储为抽象字节字符串,并且只将它们转换为 wchar_t
即时在用户界面中显示它们(如果它甚至有必要的话;许多 UI 工具包本身使用纯字节字符串并进行解释作为你的角色)。通过这种方式,您消除了许多可能令人讨厌的极端情况,并且您永远不会遇到某些文件因其名称而无法访问的情况。
【讨论】:
谢谢。这正是我正在寻找的方式。【参考方案2】:Linux 不是 UTF-8,但无论如何它是您唯一的文件名选择
(文件可以有任何你想要的东西里面。)
关于文件名,linux 并没有真正需要担心的字符串编码。文件名是需要以 null 结尾的字节字符串。
这并不完全意味着 Linux 是 UTF-8,但它确实意味着它与宽字符不兼容,因为它们可能在一个不是结束字节的字节中有一个零。
但是 UTF-8 保留了 no-nulls-except-at-the-end 模型,所以我必须相信实用的方法是文件名“转换为 UTF-8”。
文件的内容是高于 Linux 内核级别的标准的问题,所以这里没有任何你可以或想要做的 Linux-y 事情。文件的内容将完全由读写它们的程序关心。 Linux只是存储和返回字节流,它可以拥有你想要的所有嵌入式nuls。
【讨论】:
这不应该令人沮丧。这实际上是最简单的。只需在任何地方使用 UTF-8,您就无需担心。【参考方案3】:将 wchar 字符串转换为 utf8 char 字符串,然后使用 fopen。
typedef unsigned int uint;
typedef unsigned short word;
typedef unsigned char byte;
int UTF16to8( wchar_t* w, char* s )
uint c;
word* p = (word*)w;
byte* q = (byte*)s; byte* q0 = q;
while( 1 )
c = *p++;
if( c==0 ) break;
if( c<0x080 ) *q++ = c; else
if( c<0x800 ) *q++ = 0xC0+(c>>6), *q++ = 0x80+(c&63); else
*q++ = 0xE0+(c>>12), *q++ = 0x80+((c>>6)&63), *q++ = 0x80+(c&63);
*q = 0;
return q-q0;
int UTF8to16( char* s, wchar_t* w )
uint cache,wait,c;
byte* p = (byte*)s;
word* q = (word*)w; word* q0 = q;
while(1)
c = *p++;
if( c==0 ) break;
if( c<0x80 ) cache=c,wait=0; else
if( (c>=0xC0) && (c<=0xE0) ) cache=c&31,wait=1; else
if( (c>=0xE0) ) cache=c&15,wait=2; else
if( wait ) (cache<<=6)+=c&63,wait--;
if( wait==0 ) *q++=cache;
*q = 0;
return q-q0;
【讨论】:
别费心fopen,用你正常的流构造函数或成员。 谢谢你,我解决了我的问题。唯一的问题是在 Linux 中 wchar_t 等于 uint32。我做了一些修改,效果很好。 这个答案中的功能非常不合规且不安全。如果您想使用它们,请查找 UTF-8 和 UTF-16 的正确定义。 (请注意,UTF-16 与 OP 的问题无关,因为wchar_t
除了在 Windows 上之外不是 UTF-16,即使在那里它也相当破损......)【参考方案4】:
查看此文档
http://www.firstobject.com/wchar_t-string-on-linux-osx-windows.htm
我认为 Linux 遵循 POSIX 标准,它将所有文件名都视为 UTF-8。
【讨论】:
【参考方案5】:当您说“文件系统中的非 ascii 文件”时,我认为它是包含非 ascii 字符的文件名,而不是文件本身。文件包含什么并不重要。
你可以用普通的 fopen 做到这一点,但你必须匹配文件系统使用的编码。
这取决于您使用的 Linux 版本和文件系统以及设置方式,但如果幸运的话,文件系统很可能使用 UTF-8。所以把你的 wchar_t(它可能是一个 UTF-16 编码的字符串?),将它转换成一个用 UTF-8 编码的 char 字符串,然后将它传递给 fopen。
【讨论】:
【参考方案6】:// locals
string file_to_read; // any file
wstring file; // read ascii or non-ascii file here
FILE *stream;
int read = 0;
wchar_t buffer= '0';
if( fopen_s( &stream, file_to_read.c_str(), "r+b" ) == 0 ) // in binary mode
while( !feof( stream ))
// if ascii file second arg must be sizeof(char). if non ascii file sizeof( wchar_t)
read = fread( & buffer, sizeof( char ), 1, stream );
file.append(1, buffer);
file.pop_back(); // since this code reads the last character twice.Throw the last one
fclose(stream);
// and the file is in wstring format.You can use it in any C++ wstring operation
// this code is fast enough i think, at least in my practice
// for windows because of fopen_s
【讨论】:
以上是关于如何在 Linux 中打开包含非 Ascii 字符串的 wchar_t* 文件?的主要内容,如果未能解决你的问题,请参考以下文章
Cin 和 getline 不会正确保存非 ascii 字符
QML 图像不会在图像源中加载具有非 ASCII 字符的图像