在Mac OS X下的_wfopen等价函数

18
我正在寻找在Mac OS X下等同于Windows中_wfopen()的函数。有任何想法吗?
我需要这个函数以便移植一个使用wchar*作为文件接口的Windows库。由于这是一个跨平台库,我无法依赖客户端应用程序如何获取文件路径并将其提供给库。
5个回答

15

在Mac OS X中,可以使用UTF-8字符串使用POSIX API。为了将wchar_t字符串转换为UTF-8,可以使用Mac OS X的CoreFoundation框架。

这是一个类,它将从wchar_t字符串生成的字符串包装成UTF-8字符串。

class Utf8
{
public:
    Utf8(const wchar_t* wsz): m_utf8(NULL)
    {
        // OS X uses 32-bit wchar
        const int bytes = wcslen(wsz) * sizeof(wchar_t);
        // comp_bLittleEndian is in the lib I use in order to detect PowerPC/Intel
        CFStringEncoding encoding = comp_bLittleEndian ? kCFStringEncodingUTF32LE
                                                       : kCFStringEncodingUTF32BE;
        CFStringRef str = CFStringCreateWithBytesNoCopy(NULL, 
                                                       (const UInt8*)wsz, bytes, 
                                                        encoding, false, 
                                                        kCFAllocatorNull
                                                        );

        const int bytesUtf8 = CFStringGetMaximumSizeOfFileSystemRepresentation(str);
        m_utf8 = new char[bytesUtf8];
        CFStringGetFileSystemRepresentation(str, m_utf8, bytesUtf8);
        CFRelease(str);
    }   

    ~Utf8() 
    { 
        if( m_utf8 )
        {
            delete[] m_utf8;
        }
    }

public:
    operator const char*() const { return m_utf8; }

private:
    char* m_utf8;
};

使用方法:

const wchar_t wsz = L"Here is some Unicode content: éà€œæ";
const Utf8 utf8 = wsz;
FILE* file = fopen(utf8, "r");

这将适用于读取或写入文件。


如果“delete”是多余的,则仅删除。 - paulm

5
你只想使用可能包含Unicode字符的路径打开文件句柄,是吗?只需将该路径以“文件系统表示”传递给fopen即可。
  • 如果路径来自Mac OS X框架(例如,Carbon或Cocoa的“打开面板”),则无需对其进行任何转换,可以直接使用它。

  • 如果您自己生成路径的一部分,则应从路径中创建CFStringRef, 然后获取它的“文件系统表示”以传递给像openfopen这样的POSIX API。

一般来说,大多数应用程序不需要做太多的工作。例如,许多应用程序可能会将辅助数据文件存储在用户的应用程序支持目录中,但只要这些文件的名称是ASCII,并且您使用标准的Mac OS X API来查找用户的应用程序支持目录,您就不需要对使用这两个组件构建的路径进行大量的偏执转换。

添加编辑:我强烈警告不要随意使用像wcstombs之类的工具将所有内容转换为UTF-8,因为文件系统编码不一定与生成的UTF-8相同。Mac OS X和Windows都使用特定(但不同)的规范分解规则来编码文件系统路径。

例如,它们需要决定“é”将存储为一个还是两个代码单元(LATIN SMALL LETTER E WITH ACUTELATIN SMALL LETTER E后跟COMBINING ACUTE ACCENT)。这些将导致两个不同且长度不同的字节序列,Mac OS X和Windows都会避免将多个具有相同名称(用户感知)的文件放在同一个目录中。

如何执行此规范分解的规则可能非常复杂,因此最好将其留给系统框架为您提供的函数来完成繁重的工作,而不要尝试自己实现它。


1
对于想要在代码中尝试的人,https://en.wikipedia.org/wiki/Unicode_equivalence#Example 显示了写入“é”的两种方法(不同的字符代码)。它们在显示Unicode的Linux文件管理器上看起来相等,通常不允许使用相同名称的两个文件。例如,在bash中键入 touch $'file-\u00e9' $'file-\u0065\u0301' 将会在 ls 中显示为:file-é file-é - nh2

2

@JKP:

并非所有的MacOS X函数都接受UTF8,但文件名和文件路径可以是UTF8,因此所有处理文件访问的POSIX函数(如open、fopen、stat等)都接受UTF8。

请参见这里。引用:

在API级别上,文件名的外观取决于API。当前Carbon API将文件名处理为UTF-16字符数组;POSIX API将其处理为UTF-8字符数组,这就是为什么UTF-8在终端中运行良好的原因。它在磁盘上的存储方式取决于磁盘格式;HFS+使用UTF-16,但在大多数情况下这并不重要。

其他一些POSIX函数也处理UTF8。例如,处理用户名、组名或用户密码的函数使用UTF8来存储信息(因此用户名可以是日语,密码可以是中文,没有问题)。

但并非所有函数都处理UTF8。例如,对于所有字符串函数,UTF8字符串只是普通的C字符串,126以上的字符没有特殊含义。它们不理解多个字节(C中的字符)组成单个Unicode字符的概念。其他API如何处理传递给它们的char *指针因API而异。然而,作为一个规则,你可以说:

要么函数只接受纯ASCII字符(仅在0到126的范围内)的C字符串,要么它将接受UTF8。通常,函数不允许超过126的字符,并且不会以除UTF8之外的任何其他编码解释它们。如果真的是这种情况,则已记录下来,那么必须有一种方法将编码与字符串一起传递。


0

我已经通过wifstream(它使用wchar_t缓冲区)从UTF8配置文件中读取了文件名。

Mac实现与Linux和Windows不同。wifstream将每个字节从文件读取到缓冲区中的单独wchar_t单元格中。因此我们有3个空字节,尽管open需要char字符串。因此,程序员可以使用wcstombs函数将宽字符字符串转换为多字节字符串。

API支持UTF8。为了更好地理解,请使用内存查看器和十六进制编辑器查看您的文件。


0
如果您正在使用Cocoa,使用NSString相当容易。只需使用-initWithBytes:length:encoding:(或者也许是-initWithCString:encoding:)加载UTF16数据,然后通过在结果上调用UTF8String来获取UTF8版本。然后,只需使用新的UTF8字符串作为参数调用fopen即可。
无论语言如何,您都可以使用UTF-8字符串调用fopen - 对于OSX上的C++无法提供帮助 - 抱歉。

我不使用Cocoa,而是使用C++,而非Objective-C。如果您正确地给出一个UTF-8字符串给fopen(),那么我可以将我的UTF-16字符串转换为UTF-8,但在Mac OS X上(再次使用C/C++)如何轻松实现这一点? - Vincent Robert
这不是一个确定的答案,因为我依赖CFString而不是NSString,但基本思路是一样的。谢谢。 - Vincent Robert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接