Як отримати повідомлення про помилку, якщо ifstream open не вдається


99
ifstream f;
f.open(fileName);

if ( f.fail() )
{
    // I need error message here, like "File not found" etc. -
    // the reason of the failure
}

Як отримати повідомлення про помилку як рядок?


3
можливий дублікат перевірки помилок
Matthieu Rouget


3
@ Алекс Фарбер: Звичайно. cerr << "Error code: " << strerror(errno); // Get some info as to whyпредставляється актуальним для питання.
Matthieu Rouget

@MatthieuRouget: Перевірте можливий дублікат, який я розмістив - здається, це нестандартна поведінка, реалізована лише gcc.
arne

1
@MatthieuRouget: strerror(errno)працює. Опублікуйте це як відповідь, я прийму.
Alex F

Відповіді:


72

Кожен системний виклик, який не вдається оновити errnoзначення.

Таким чином, ви можете отримати більше інформації про те, що відбувається, коли ifstreamвідкриття не працює, використовуючи щось на зразок:

cerr << "Error: " << strerror(errno);

Однак, оскільки кожен системний виклик оновлює глобальне errnoзначення, у вас може виникнути проблема в багатопотоковій програмі, якщо інший системний виклик викликає помилку між виконанням f.openта використанням errno.

У системі зі стандартом POSIX:

errno є локальним для потоку; встановлення його в одному потоці не впливає на його значення в будь-якому іншому потоці.


Редагувати (дякую Арне Мерцу та іншим людям у коментарях):

e.what() спочатку здавалося, що це C ++ - ідіоматичніше правильний спосіб реалізації цього, однак рядок, повернутий цією функцією, залежить від реалізації, і (принаймні в libstdc ++ G ++) цей рядок не має корисної інформації про причину помилки ...


1
e.what()здається, не дає багато інформації, див. оновлення моєї відповіді.
Арне Мерц,

17
errnoвикористовує локальне сховище потоків у сучасних операційних системах. Однак немає жодних гарантій того, що fstreamфункції не будуть збиватися errnoпісля помилки. Основні функції можуть взагалі не встановлюватися errno(прямі системні дзвінки на Linux або Win32). Це не працює у багатьох реальних реалізаціях.
strcat

1
У MSVC e.what()завжди друкує одне і те ж повідомлення " iostream stream error"
rustyx,

warning C4996: 'strerror': This function or variable may be unsafe. Consider using strerror_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\string.h(168) : see declaration of 'strerror'
sergiol

1
@sergiol Це брехня. Ігноруйте їх або вимкніть попередження.
SS Anne,

29

Ви можете спробувати дозволити потоку видавати виняток у разі відмови:

std::ifstream f;
//prepare f to throw if failbit gets set
std::ios_base::iostate exceptionMask = f.exceptions() | std::ios::failbit;
f.exceptions(exceptionMask);

try {
  f.open(fileName);
}
catch (std::ios_base::failure& e) {
  std::cerr << e.what() << '\n';
}

e.what()однак, здається, не дуже корисний:

  • Я спробував це на Win7, Embarcadero RAD Studio 2010, де він дає "ios_base :: failbit set", тоді як strerror(errno)дає "Немає такого файлу чи каталогу".
  • На Ubuntu 13.04, gcc 4.7.3 виняток говорить "basic_ios :: clear" (завдяки arne )

Якщо у вас e.what()не виходить (я не знаю, що це скаже вам про помилку, оскільки це не стандартизовано), спробуйте використати std::make_error_condition(лише C ++ 11):

catch (std::ios_base::failure& e) {
  if ( e.code() == std::make_error_condition(std::io_errc::stream) )
    std::cerr << "Stream error!\n"; 
  else
    std::cerr << "Unknown failure opening file.\n";
}

Дякую. Я не тестував цього, оскільки strerror(errno)розміщене в коментарях працює і дуже просто у використанні. Я думаю, що e.whatце спрацює, оскільки errnoпрацює.
Alex F,

Тоді побачити annotaions про багатопоточності в Matthieus відповідь - я думаю, що e.what()буде то , що strerrorповертається, в поточно - чином. Обидва, ймовірно, залежать від платформи.
Арне Мерц,

1
@AlexFarber: Я думаю, що відповідь Арне краща за мою. Моє рішення - це не C ++ - спосіб вирішення вашої проблеми. Однак я не знайшов офіційної інформації про те, як бібліотека С ++ відображає помилки системних викликів exception.what(). Це може бути гарною можливістю зануритися у вихідний код libstdc ++ :-)
Matthieu Rouget

Я спробував це: спробував відкрити неіснуючий файл і повідомлення про виняток прочитано basic_ios::clear, нічого іншого. Це насправді не корисно. Ось чому я не публікував повідомлення;)
arne

@arne, яка платформа, компілятор, ОС?
Арне Мерц,

22

Наслідуючи відповідь @Arne Mertz, станом на C ++ 11 std::ios_base::failureуспадковується від system_error(див. Http://www.cplusplus.com/reference/ios/ios_base/failure/ ), який містить як код помилки, так і повідомлення, strerror(errno)яке повернеться.

std::ifstream f;

// Set exceptions to be thrown on failure
f.exceptions(std::ifstream::failbit | std::ifstream::badbit);

try {
    f.open(fileName);
} catch (std::system_error& e) {
    std::cerr << e.code().message() << std::endl;
}

Це друкується, No such file or directory.якщо fileNameне існує.


8
Для мене в MSVC 2015 це просто друкується iostream stream error.
rustyx

2
Для мене GCC 6.3 також друкує iostream error. На якому компіляторі ви тестували це? Чи справді якийсь компілятор надає зручну для читання причину відмови?
Руслан

2
Clang 6 на LIBC ++ на MacOS: unspecified iostream_category error.
акім

Xcode 10.2.1 (Clang) / libc ++ (C ++ 17) на MacOS 10.14.x: також "Невизначена помилка категорії потоку_категорій". strerror (errno) Здається, це єдиний спосіб отримати це право. Я гадаю, я міг би спочатку вловити його, запитавши std :: filesystem, якщо path.exists (), і вивчивши std :: error_code, який він повертає.
SMGreenfield

7

Ви також можете кинути знак, std::system_errorяк показано в коді тесту нижче. Здається, цей метод дає більше читабельного результату, ніж f.exception(...).

#include <exception> // <-- requires this
#include <fstream>
#include <iostream>

void process(const std::string& fileName) {
    std::ifstream f;
    f.open(fileName);

    // after open, check f and throw std::system_error with the errno
    if (!f)
        throw std::system_error(errno, std::system_category(), "failed to open "+fileName);

    std::clog << "opened " << fileName << std::endl;
}

int main(int argc, char* argv[]) {
    try {
        process(argv[1]);
    } catch (const std::system_error& e) {
        std::clog << e.what() << " (" << e.code() << ")" << std::endl;
    }
    return 0;
}

Приклад виводу (Ubuntu з дзвінком):

$ ./test /root/.profile
failed to open /root/.profile: Permission denied (system:13)
$ ./test missing.txt
failed to open missing.txt: No such file or directory (system:2)
$ ./test ./test
opened ./test
$ ./test $(printf '%0999x')
failed to open 000...000: File name too long (system:36)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.