วิธีรับข้อความแสดงข้อผิดพลาดเมื่อ ifstream เปิดล้มเหลว


101
ifstream f;
f.open(fileName);

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

จะรับข้อความแสดงข้อผิดพลาดเป็นสตริงได้อย่างไร?


3
อาจซ้ำกันได้ของC ++ ifstream Error Checking
Matthieu Rouget


3
@ Alex Farber: แน่นอน cerr << "Error code: " << strerror(errno); // Get some info as to whyดูเหมือนจะเกี่ยวข้องกับคำถาม
Matthieu Rouget

@MatthieuRouget: ตรวจสอบความซ้ำซ้อนที่ฉันโพสต์ไว้ - ดูเหมือนว่านี่จะเป็นพฤติกรรมที่ไม่ได้มาตรฐานที่ใช้งานโดย gcc เท่านั้น
arne

1
@MatthieuRouget: ได้strerror(errno)ผล โพสต์นี้เป็นคำตอบฉันจะยอมรับมัน
Alex F

คำตอบ:


75

ทุกการเรียกใช้ระบบที่อัปเดตerrnoค่าไม่สำเร็จ

ดังนั้นคุณสามารถมีข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่เกิดขึ้นเมื่อการifstreamเปิดล้มเหลวโดยใช้สิ่งต่างๆเช่น:

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

อย่างไรก็ตามเนื่องจากการเรียกระบบทุกครั้งจะอัปเดตerrnoค่าส่วนกลางคุณอาจมีปัญหาในแอปพลิเคชันมัลติเธรดหากการเรียกระบบอื่นทริกเกอร์ข้อผิดพลาดระหว่างการดำเนินการf.openและการใช้errno.

บนระบบด้วยมาตรฐาน POSIX:

errno เป็นเธรดท้องถิ่น การตั้งค่าในเธรดเดียวจะไม่มีผลกับค่าในเธรดอื่น


แก้ไข (ขอบคุณ Arne Mertz และคนอื่น ๆ ในความคิดเห็น):

e.what() ดูเหมือนว่าในตอนแรกจะเป็น C ++ มากขึ้น - วิธีที่ถูกต้องตามหลักการในการใช้สิ่งนี้อย่างไรก็ตามสตริงที่ส่งคืนโดยฟังก์ชันนี้ขึ้นอยู่กับการใช้งานและ (อย่างน้อยใน libstdc ++ ของ G ++) สตริงนี้ไม่มีข้อมูลที่เป็นประโยชน์เกี่ยวกับสาเหตุที่อยู่เบื้องหลังข้อผิดพลาด ...


1
e.what()ดูเหมือนจะไม่ให้ข้อมูลมากนักโปรดดูการอัปเดตคำตอบของฉัน
Arne Mertz

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

30

คุณสามารถลองปล่อยให้สตรีมมีข้อยกเว้นเมื่อเกิดความล้มเหลว:

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

จากนั้นดูคำอธิบายประกอบเกี่ยวกับมัลติเธรดในคำตอบของ Matthieus - ฉันเดาว่านั่นe.what()จะเป็นสิ่งที่strerrorส่งคืนในวิธีที่ปลอดภัย ทั้งสองอาจขึ้นอยู่กับแพลตฟอร์ม
Arne Mertz

1
@AlexFarber: ฉันคิดว่าคำตอบของ Arne ดีกว่าของฉัน วิธีแก้ปัญหาของฉันไม่ใช่C ++ - วิธีแก้ปัญหาของคุณ แต่ฉันไม่พบข้อมูลอย่างเป็นทางการเกี่ยวกับวิธีไลบรารี c ++ exception.what()แผนที่ข้อผิดพลาดของระบบโทรไป อาจเป็นโอกาสที่ดีในการดำดิ่งสู่ซอร์สโค้ด libstdc ++ :-)
Matthieu Rouget

ฉันลองแล้ว: พยายามเปิดไฟล์ที่ไม่มีอยู่และอ่านข้อความยกเว้นbasic_ios::clearไม่มีอะไรอื่น สิ่งนี้ไม่เป็นประโยชน์จริงๆ นั่นเป็นเหตุผลที่ฉันไม่โพสต์;)
arne

@arne wich แพลตฟอร์มคอมไพเลอร์ระบบปฏิบัติการ?
Arne Mertz

23

ตามด้วยคำตอบของ @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ไม่มีอยู่


9
สำหรับผมใน MSVC 2015 iostream stream errorที่เพิ่งพิมพ์
rustyx

2
สำหรับผม GCC 6.3 iostream errorยังพิมพ์ คุณทดสอบสิ่งนี้กับคอมไพเลอร์ใด คอมไพเลอร์ใด ๆ ให้เหตุผลที่ผู้ใช้สามารถอ่านได้สำหรับความล้มเหลวหรือไม่?
Ruslan

2
เสียงดังกราว 6 libc ++ บน unspecified iostream_category errorMacOS:
akim

Xcode 10.2.1 (เสียงดัง) / libc ++ (C ++ 17) บน MacOS 10.14.x: นอกจากนี้ยังมี "ข้อผิดพลาด iostream_category ที่ไม่ระบุ" strerror (errno) ดูเหมือนจะเป็นวิธีเดียวที่จะทำให้ได้สิทธิ์นี้ ฉันคิดว่าฉันสามารถจับได้ก่อนโดยถาม std :: filesystem ว่า path.exists () หรือไม่และตรวจสอบ std :: error_code ที่ส่งคืน
SMGreenfield

ในโปรแกรมตัวอย่างเช่นคำสั่งf.open(fileName)พ่นยกเว้นประเภทซึ่งได้มาจากstd::ios_base::failure std::system_errorข้อยกเว้นถูกจับโดยบล็อกการจับ ภายในจับบล็อกe.code()จะเรียกซึ่งจะส่งกลับวัตถุของการพิมพ์std::ios_base::failure::code() std::error_codeรหัสข้อผิดพลาดที่กำหนดโดยระดับstd::error_codeที่มีขึ้นอยู่กับแพลตฟอร์ม --ie, e.code().message()และe.code().value()ผลตอบแทนทั้งค่าขึ้นอยู่กับแพลตฟอร์ม
Jim Fischer

9

คุณยังสามารถโยนได้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 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.