14

C++ fstream を使用して構成ファイルを読み取ります。

#include <fstream>
std::ifstream my_file(my_filename);

現在、ディレクトリのパスを渡すと、これは黙って無視されます。たとえば、ディレクトリmy_file.good()であっても true を返します。my_filenameこれは私のプログラムにとって意図しない入力であるため、チェックして例外をスローします。

開いたばかりの fstream が通常のファイル、ディレクトリ、またはストリームであるかどうかを確認するにはどうすればよいですか?

私はどちらかへの方法を見つけることができないようです:

  • 指定された ifstream からファイル記述子を取得します。
  • 他のメカニズムを使用して、ifstream でこの情報を見つけてください。

いくつかのフォーラム ディスカッションでは、これは OS に依存するため、どちらも不可能であり、したがって fstream C++ 標準の一部になることはできないことが示唆されました。

私が考えることができる唯一の代替手段は、コードを書き直して ifstream を完全に取り除き、ファイル記述子 ( *fp)の C メソッドに頼ることfstat()です。

#include <stdio.h>
#include <sys/stat.h>
FILE *fp = fopen(my_filename.c_str(), "r");
// skip code to check if fp is not NULL, and if fstat() returns != -1
struct stat fileInfo;
fstat(fileno(fp), &fileInfo);
if (!S_ISREG(fileInfo.st_mode)) {
    fclose(fp);
    throw std::invalid_argument(std::string("Not a regular file ") + my_filename);
}

私はfstreamの方が好きです。したがって、私の質問です。

4

4 に答える 4

3
void assertGoodFile(const char* fileName) {
   ifstream fileOrDir(fileName);
   //This will set the fail bit if fileName is a directory (or do nothing if it is already set  
   fileOrDir.seekg(0, ios::end);
   if( !fileOrDir.good()) {
      throw BadFile();
   };
}
于 2015-03-27T21:55:42.010 に答える
2

IO 操作は OS に依存するため、思考は複雑です。

OS X 10.10.2、Linux 2.6.32、および FreeBSD 8.2-RELEASE でいくつかの手法を試しました (後の 2 つは少し古い OS で、古い VirtualBox VM をいくつか使用しました)。

  • 私はまだ確実な方法を見つけていません。本当にチェックしたい場合はstat()、パスで使用するか、昔ながらの C open()with fstat().
  • seekg(0, std::ios::beg);PSkocik によって提案された方法は、私にはうまくいきませんでした。
  • fstream の場合、最善の方法は、ファイルを開いて読み取り、エラーを待つことです。
  • 特に OS X では、必要なすべての例外を発生させるために、両方のbadbitANDを設定する必要があります。failbit例えばmy_file.exceptions(std::ios::failbit | std::ios::badbit);
  • これにより、通常のファイルを読み取った後にファイルの終わり (EOF) 例外が発生するため、これらの通常の例外を無視するコードが必要になります。
  • my_file.eof()より重大なエラーが発生した場合にも設定される可能性があるため、EOF状態のチェックには不十分です。
  • errnoより良い指標です。例外が発生しerrnoても 0 のままである場合、それは状態である可能性が最も高いEOFです。
  • これは常に正しいとは限りません。FreeBSD 8.2 では、ディレクトリ パスを開くとバイナリの gobbledygook が返されるだけで、例外は発生しません。

これは、私がテストした 3 つのプラットフォーム全体である程度適切に処理できると思われる実装です。

#include < iostream>
#include < fstream>
#include < cerrno>
#include < cstring>

int main(int argc, char *argv[]) {
   for (int i = 1; i < argc; i++) {

      std::ifstream my_file;
      try {
         // Ensure that my_file throws an exception if the fail or bad bit is set.
         my_file.exceptions(std::ios::failbit | std::ios::badbit);
         std::cout << "Read file '" << argv[i] << "'" << std::endl;
         my_file.open(argv[i]);
         my_file.seekg(0, std::ios::end);
      } catch (std::ios_base::failure& err) {
         std::cerr << "  Exception during open(): " << argv[i] << ": " << strerror(errno) << std::endl;
         continue;
      }
      
      try {
         errno = 0; // reset errno before I/O operation.
         std::string line;
         while (std::getline(my_file, line))
         {
            std::cout << "  read line" << std::endl;
            // ...
         }
      } catch (std::ios_base::failure& err) {
         if (errno == 0) {
            std::cerr << "  Exception during read(), but errno is 0. No real error." << std::endl;
            continue; // exception is likely raised due to EOF, no real error.
         }
         std::cerr << "  Exception during read(): " << argv[i] << ": " << strerror(errno) << std::endl;
      }
   }
}
于 2015-03-28T00:37:35.103 に答える