Я использую C ++ Fstream для чтения файла конфигурации.
#include <fstream>
std::ifstream my_file(my_filename);
Прямо сейчас, если я передаю путь к каталогу, он молча игнорирует это. Например. my_file.good()
возвращает true, даже если my_filename
это каталог. Поскольку это непреднамеренный ввод для моей программы, я хотел бы проверить его и выдать исключение.
Как проверить, является ли только что открытый fstream обычным файлом, каталогом или потоком?
Я не могу найти способ либо:
В некоторое обсуждение на форуме Было высказано предположение, что ни один из них невозможен, поскольку это зависит от ОС и, следовательно, никогда не может быть частью стандарта Fstream C ++.
Единственная альтернатива, о которой я могу подумать, — переписать мой код, чтобы полностью избавиться от ifstream, и прибегнуть к C-методу файлового дескриптора (*fp
), вместе с 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. Отсюда и мой вопрос.
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();
};
}
Существуют разные подходы к решению этой проблемы:
stat()
или напрямую используйте Boost.Filesystem или какую-то похожую библиотеку. Я не уверен на 100%, было ли что-то подобное добавлено в C ++ 11. Обратите внимание, что это создает условие гонки, потому что после проверки, но перед открытием, некоторые злоумышленники могут переключить файл с каталогом.fstream
, в вашем случае, вероятно, FILE*
, Есть также способы создания iostream
(не обязательно fstream
!) из FILE*
, Это всегда зависящие от реализации расширения, поэтому вам нужны #ifdef
магия, чтобы адаптировать ваш код, специфичный для используемой реализации stdlibrary. Я бы осмелился положиться на их присутствие, даже если бы вы не смогли создать streambuf
на самом деле FILE*
если вам нужно портировать на какую-то непонятную систему, которая не предоставляет более простой способ.Думать сложно, из-за OS-зависимости IO-операций.
Я попробовал несколько приемов на OS X 10.10.2, Linux 2.6.32 и FreeBSD 8.2-RELEASE (последние две — несколько более старые ОС, я использовал несколько старых виртуальных машин VirtualBox).
stat()
на пути, или старомодный C open()
с fstat()
,seekg(0, std::ios::beg);
метод, предложенный PSkocik, не работает для меня.my_file.exceptions(std::ios::failbit | std::ios::badbit);
my_file.eof()
также может быть настроен на более серьезные ошибки, так что это плохая проверка на состояние EOF.errno
является лучшим индикатором: если исключение возбуждается, но errno по-прежнему равно 0, это, скорее всего, условие EOF.Это — реализация, которая, кажется, обрабатывает это несколько разумно для 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 errono 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;
}
}
}