Cách nhận thông báo lỗi khi mở ifstream không thành công


99
ifstream f;
f.open(fileName);

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

Làm thế nào để nhận được thông báo lỗi dưới dạng chuỗi?


3
có thể trùng lặp của C ++ ifstream Error Checking
Matthieu Rouget


3
@Alex Farber: Chắc chắn rồi. cerr << "Error code: " << strerror(errno); // Get some info as to whycó vẻ liên quan đến câu hỏi.
Matthieu Rouget

@MatthieuRouget: Kiểm tra bản sao có thể tôi đã đăng - có vẻ như đây là hành vi không chuẩn chỉ được thực hiện bởi gcc.
arne

1
@MatthieuRouget: strerror(errno)hoạt động. Đăng điều này như câu trả lời, tôi sẽ chấp nhận nó.
Alex F

Câu trả lời:


72

Mọi lệnh gọi hệ thống không cập nhật errnogiá trị.

Do đó, bạn có thể có thêm thông tin về những gì sẽ xảy ra khi ifstreammở không thành công bằng cách sử dụng một cái gì đó như:

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

Tuy nhiên, vì mọi lệnh gọi hệ thống đều cập nhật errnogiá trị chung, bạn có thể gặp sự cố trong ứng dụng đa luồng, nếu lệnh gọi hệ thống khác gây ra lỗi giữa việc thực thi f.openvà sử dụng errno.

Trên hệ thống với tiêu chuẩn POSIX:

errno là luồng cục bộ; việc đặt nó trong một chuỗi không ảnh hưởng đến giá trị của nó trong bất kỳ chuỗi nào khác.


Chỉnh sửa (cảm ơn Arne Mertz và những người khác trong nhận xét):

e.what() thoạt đầu có vẻ là một cách thực thi điều này đúng theo ngữ nghĩa hơn trong C ++, tuy nhiên chuỗi được trả về bởi hàm này phụ thuộc vào việc triển khai và (ít nhất là trong libstdc ++ của G ++) chuỗi này không có thông tin hữu ích về lý do đằng sau lỗi ...


1
e.what()dường như không cung cấp nhiều thông tin, hãy xem cập nhật cho câu trả lời của tôi.
Arne Mertz

17
errnosử dụng lưu trữ cục bộ luồng trên các hệ điều hành hiện đại. Tuy nhiên, không có gì đảm bảo rằng các fstreamchức năng sẽ không bị tắc nghẽn errnosau khi xảy ra lỗi. Các chức năng cơ bản có thể hoàn toàn không được thiết lập errno(lệnh gọi hệ thống trực tiếp trên Linux hoặc Win32). Điều này không hoạt động trên nhiều triển khai trong thế giới thực.
strcat

1
Trong MSVC, e.what()luôn in cùng một thông điệp " 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 Đó là những lời nói dối. Bỏ qua chúng hoặc tắt cảnh báo.
SS Anne

29

Bạn có thể thử để luồng có một ngoại lệ khi không thành công:

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()tuy nhiên, dường như không hữu ích lắm:

  • Tôi đã thử nó trên Win7, Embarcadero RAD Studio 2010, nơi nó cho "ios_base :: failbit set" trong khi strerror(errno)cho "Không có tệp hoặc thư mục nào như vậy."
  • Trên Ubuntu 13.04, gcc 4.7.3 ngoại lệ cho biết "basic_ios :: clear" (cảm ơn arne )

Nếu e.what()không hiệu quả với bạn (tôi không biết nó sẽ cho bạn biết gì về lỗi, vì lỗi đó không được chuẩn hóa), hãy thử sử dụng std::make_error_condition(chỉ 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";
}

Cảm ơn. Tôi đã không kiểm tra điều này vì strerror(errno)được đăng trong các nhận xét hoạt động và rất đơn giản để sử dụng. Tôi nghĩ rằng điều đó e.whatsẽ hiệu quả, vì nó errnohoạt động.
Alex F

Sau đó, hãy xem các chú thích về đa luồng trong câu trả lời của Matthieus - tôi đoán đó e.what()sẽ là những gì strerrortrả về, theo cách an toàn. Cả hai có thể sẽ phụ thuộc vào nền tảng.
Arne Mertz

1
@AlexFarber: Tôi nghĩ câu trả lời của Arne hay hơn câu trả lời của tôi. Giải pháp của tôi không phải là C ++ - cách giải quyết vấn đề của bạn. Tuy nhiên, tôi không tìm thấy thông tin chính thức về cách thư viện C ++ ánh xạ lỗi hệ thống gọi đến exception.what(). Có thể là một cơ hội tốt để bổ nhào vào libstdc mã nguồn ++ :-)
Matthieu Rouget

Tôi đã thử điều này: Đã cố gắng mở một tệp không có và đọc thông báo ngoại lệ basic_ios::clear, không có gì khác. Điều này không thực sự hữu ích. Đó là lý do tại sao tôi không đăng;)
arne

@arne wich nền tảng, trình biên dịch, hệ điều hành?
Arne Mertz

22

Theo sau câu trả lời của @Arne Mertz, về C ++ 11 std::ios_base::failurekế thừa từ system_error(xem http://www.cplusplus.com/reference/ios/ios_base/failure/ ), có chứa cả mã lỗi và thông báo strerror(errno)sẽ trả về.

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;
}

Bản in này No such file or directory.nếu fileNamekhông tồn tại.


8
Đối với tôi trong MSVC 2015 mà chỉ in iostream stream error.
gỉ vào

2
Đối với tôi GCC 6.3 cũng được in iostream error. Bạn đã kiểm tra trình biên dịch này trên trình biên dịch nào? Có trình biên dịch nào thực sự cung cấp lý do người dùng có thể đọc được cho sự thất bại không?
Ruslan

2
Clang 6 trên libc ++ trên macOS : unspecified iostream_category error.
akim

Xcode 10.2.1 (Clang) / libc ++ (C ++ 17) trên MacOS 10.14.x: cũng "Lỗi iostream_category không xác định". strerror (errno) SEEMS là cách duy nhất để đạt được điều này. Tôi cho rằng tôi có thể bắt nó trước bằng cách hỏi std :: filesystem nếu path.exists () và kiểm tra std :: error_code mà nó trả về.
SMGreenfield

7

Bạn cũng có thể ném một std::system_errornhư được hiển thị trong mã thử nghiệm bên dưới. Phương pháp này dường như tạo ra đầu ra dễ đọc hơn 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;
}

Ví dụ đầu ra (Ubuntu w / clang):

$ ./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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.