CreateFile vs fopen vs ifstream的优势和;坏处?

时间:2012-05-28 09:16:42

标签: c++ c

CreateFile vs fopen vs ofsteam - 优势&缺点

我听说CreateFile功能强大但仅适用于Windows 你能告诉我应该使用什么(在Windows上)以及为什么?

6 个答案:

答案 0 :(得分:8)

这取决于你在做什么。对于顺序读取和写入文本文件,iostream绝对是最佳选择。对于任何涉及交易安全或非标准设备的内容,您必须直接访问系统(CreateFileopen)。即便如此,对于顺序读取和写入文本,最好的解决方案是定义自己的streambuf,并将其与iostream一起使用。

我无法想到fopen更适合的任何背景。

答案 1 :(得分:4)

除非你需要Windows文件功能提供的功能(例如重叠的I / O),否则我的建议是使用C ++中的iostreams或C中的FILEfopen和朋友)。< / p>

除了更具可移植性之外,您还可以使用格式化输入/输出作为文本文件,对于C ++,很容易使类的输出/输入运算符重载。

答案 2 :(得分:2)

除非您绝对需要OS API函数提供的额外功能(例如CreateFile),否则我会建议使用标准库函数(如fopenofstream)。这样您的程序将更加便携。

使用CreateFile我能想到的唯一真正优势是I / O重叠,可能是更精细的访问权限。

答案 3 :(得分:2)

如果要使用Windows文件内存映射,则应使用CreateFile(例如HANDLE传递给CreateFileMapping API的返回值为{{ 1}})。此外,CreateFile提供比C和C ++标准文件API更高的自定义选项。

但是,如果您想编写可移植代码,或者如果您不需要特定于Windows的功能,那么C和C ++标准文件API就可以了。 在某些测试中,在处理大数据时,我注意到C ++ I / O流与原始C文件API的一些性能开销;如果碰巧遇到这样的情况,你可以简单地将原始C文件API包装在一些C ++ RAII类中,并且仍然在C ++代码中使用它。

答案 4 :(得分:1)

在大多数情况下,您最好在C ++中使用C或ofstream中的fopen。 CreateFile对共享和缓存提供了一些额外的控制,但不提供格式化功能。

答案 5 :(得分:0)

我从

复制了我的答案

fopen or CreateFile in Windows

由于某种原因而被关闭,这让我感到不安......

  1. fopen()没有定义的方法来返回系统错误代码。可能有一种未定义的方式来访问errno,但这可能与系统错误代码相同或不同。
  2. 另外,我认为没有一种定义的方法来访问真正的系统句柄(HANDLE类型),而你可能想要使用它传递给许多win64系统调用中的一个,这些调用期望这样一个系统句柄(例如内存映射IO)
  3. 使用open()整数表示文件句柄,它不是系统句柄(在Windows上)。
  4. fopen()在出错时不会抛出异常。为了获得一些RAII,你需要将它包装成一个类。
  5. 将CreateFile()包装到类中,并不比将fopen()或open()包装到类中更昂贵。
  6. 使用C ++功能(std :: ofstream,std :: ifstream)来写入/读取文件会遇到与fopen()相同的问题:
    • 默认情况下不会抛出错误。为了启用此功能,您需要调用某个方法而不是能够使用某些构造函数参数 - 您需要派生此类的RAII方法(为了将其用作引发错误的成员/基类)
    • 如果能够从抛出的异常中检索系统错误代码,或者从what()返回的消息告诉您有关系统错误的任何信息,则未定义。
    • 使用此流接口,没有真正的可插入接口来定义读取或写入的源或目标。重载流接口非常麻烦且容易出错。
  7. 使用C编程(注意或忽略返回代码并手动编写清理代码)是非常邪恶的来源(还记得心脏病吗?)......
  8. 结论:

    1. 为CreateFile()/ CloseHandle()编写资源包装器。资源包装器是一个类,它在构造函数中执行do-action,在析构函数中执行undo-action,并在发生错误时抛出异常。每个操作系统中都有很多这样的系统调用对,尤其是在Win64中。
    2. 编写一个系统错误异常类(在CreateFile()失败的情况下用于上述类以及所有其他系统错误)或调查新的system_exception类(在C ++ 0x中)实际正在做什么如果足够的话。
    3. 为ReadFile()和WriteFile()编写一个函数包装器,它将系统错误转换为抛出的系统异常对象......
    4. 潜在地定义您自己的接口以写入某处并从某处读取,以便您可以实现与源/目标类型无关的其他内容来读取/写入。
    5. 编写一个缓存类,允许您从某个地方缓存读取或写入某个地方也是儿童游戏。当然,缓存类不应该知道也不关心你要写入/读取的源/目的地。
    6. 不要害怕这些小任务。您实际上会知道,代码中发生了什么,与调用它的代码相比,这些代码片段的代码数量应该可以忽略不计(代码行数)。此外,如果您将RAII用于所有内容,则调用这些实用程序类的代码与不使用RAII并且必须使用两步甚至更多步骤初始化并且相当少的错误倾向相比将会相当少。用其他操作系统的等效实用程序类替换这些实用程序类也是子进程(在UNIX上使用open()/ close()/ read()/ write())。

      并且为了前几千年的缘故,请不要阅读谷歌编程指南!