【问题标题】:ifstream creates file if it doesn't existifstream 如果文件不存在则创建文件
【发布时间】:2012-11-15 08:09:30
【问题描述】:

我在编写读取 apache 日志的 Linux 控制台应用程序时遇到了一些问题。

我需要处理 bash 脚本参数,最后一个是日志文件的路径。 我的问题是,如果文件不存在,我想抛出一个异常。

但是当我尝试以只读模式打开文件时,它会创建文件而不是失败!

代码如下:

// logreader.h

#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <stdexcept>


class LogReader
{
    public:
        LogReader(int, const char **);
        virtual ~LogReader();

        // ...

    private:
        std::ifstream log_;
};

// logreader.cpp

#include <logreader.h>

LogReader::LogReader(int argc, const char ** argv):
    log_()
{
    log_.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    for (int i = 1; i < argc; ++i)
    {
        std::string arg(argv[i]);
        if (i == argc - 1)
        {
            try
            {
                log_.open(arg.c_str(), std::ifstream::in);
            }
            catch (std::ifstream::failure)
            {
                throw std::runtime_error("The file " + arg + " wasn't opened");
            }
        }
    }
}

LogReader::~LogReader()
{
}

// main.cpp

#include <logreader.h>

int main(int argc, const char ** argv)
{
    LogReader(argc, argv);
    return 0;
}

脚本调用:

jmcomets $ ./test -g -l
jmcomets $ ls -l
-rw-rw-r-- 1 jmcomets jmcomets     0 Nov 14 22:41 -l 

【问题讨论】:

  • 请创建一个完整的、最小的程序来演示错误。请参阅sscce.org 了解更多信息。
  • 你怎么知道对log.open()的调用创建了文件?
  • 您的代码无法编译。更重要的是,当它被修改为编译时,它并没有显示你看到的错误。您看到的问题不在于 that 代码,而在于您的程序中的一些 other 代码。这就是为什么创建一个最小的完整程序,例如sscce.org,是一种非常有价值的技术。
  • LogReader ctor 中有一个错误:它应该是LogReader(int, const char **)。当我修复它时,代码仍然可以正常工作。我什至用那个时髦的文件名-l 对其进行了测试,它可以工作。
  • 好的,我修复了问题的代码并对其进行了测试,它按预期工作。问题实际上出在其他地方,我会尝试使用调试器来修复它。

标签: c++ linux std


【解决方案1】:

由于您要打开std::ifstream,因此必须根据 27.9.1.9 [ifstream.members] 第 4 段添加 std::ios_base::in(或 std::ios_base::openmode 的任何其他拼写):该标志由致电open()。请注意,std::ofstreamstd::fstream 会自动添加 std::ios_base::out (27.9.1.13 [ofstream.members] paragrpah 3) 或 std::ios_base::in | std::ios_base::out (27.9.1.17 [fstream.members] 第 3 段),两者都会导致如果新文件不存在(并且有写权限等),则在正在创建的新文件中。

如果您发布的代码创建了一个新文件,则标准 C++ 库的实现是错误的:当仅指定标志 std::ios_base::in 时,使用 "r" 和 @ 的打开模式“好像”打开文件987654333@(27.9.1.4 [filebuf.members] 第 5 段)。 fopen() 在打开模式为 "r" 时不会创建新文件(7.21.5.3 第 3 段)。

【讨论】:

    【解决方案2】:

    您可以在ifstream 的异常标志中设置failbit

    std::ifstream log;
    log.exceptions ( std::ifstream::failbit );
    try {
        log.open ("test.txt");
    }
    catch (std::ifstream::failure e) {
        std::cout << "Exception opening/reading file\n";
    }
    

    Source

    我已经测试过了,如果文件无法打开,ifstream 会抛出一个failure 异常,例如找不到文件,没有读取权限。它以只读方式打开。

    【讨论】:

    • 我希望有一种方法可以在所有 fstream 对象上全局启用异常,以便构造函数在无法打开文件时抛出异常...
    • 这很有趣,如果文件对我来说不存在,它会失败并抛出异常。
    • 确实很有趣...如果我在我的类的处理脚本参数的方法中替换这个 sn-p,它会创建文件并且不会引发异常。否则在一个小测试文件中,如果我执行相同的代码块,它会失败并引发异常。这可能来自一些奇怪的初始化,我会在一分钟内回复您。
    【解决方案3】:

    您需要将 ifstream::in 作为第二个参数指定为:

    log.open(arg.c_str(), ifstream::in)
    

    你也可以这样做:

    std::ifstream log(arg.c_str(), ifstream::in);
    

    并跳过对open()的呼叫

    【讨论】:

      【解决方案4】:

      使用与 Linux 兼容的东西进行编辑;

      在写之前尝试用 fopen 打开。如果文件 DNE,FILE 指针将为空。

      FILE * file;
      file = fopen ("myfile.txt","r");
      
      if (file == NULL)
         //throw if fopen didn't already.
      else
          //do stuff with my file
      

      【讨论】:

      • 我很确定 GetFileAttributesA() 不适用于“Linux 控制台应用程序”。
      • 编辑过的东西。
      • 好吧,如果我早些时候投了反对票,我现在就把它收回。不过,我不会赞成这个答案; FILE* I/O 并不是最棒的文件 API。
      • @bames53 够公平的。不过,您实际上不必使用它来读取文件。您可以在 else 开头调用 fclose,然后使用不同的 API 读取文件。
      • 有一个 posix 函数stat()(Windows 上为_stat())可以让您检查文件是否存在。
      猜你喜欢
      • 2012-05-10
      • 1970-01-01
      • 2016-06-18
      • 1970-01-01
      • 1970-01-01
      • 2018-10-09
      • 2012-04-08
      • 2020-08-18
      相关资源
      最近更新 更多