C++ 파일 읽을 때 exception 처리에 관한 질문입니다.

aeronova의 이미지

안녕하세요,
C++에서 파일을 읽으려고 할 때 파일이 없거나 하면 예외 처리를 하려고 다음과 같은 예제에서 테스트 한 후 원래 제가 사용하려던 class에 옮겨 심었습니다. 다음 예제는 입력 파일이 없는 경우 예외를 발생시키는 식으로 잘 작동하더군요.
예제는 여기(http://www.cplusplus.com/ref/iostream/ios/exceptions.html)를 참고로 했습니다.

#include <fstream>
#include <iostream>
#include <stdexcept>
 
void f(const char* filename) {
    std::ifstream file;
    file.exceptions(std::ifstream::eofbit|std::ifstream::failbit|std::ifstream::badbit);
 
    try {
        file.open(filename);
        if (file.is_open())
	    std::cout << "file is open." << std::endl;
    } catch (const std::ifstream::failure& e) {
        // handle exception
        std::cerr << "exception in reading a file" << std::endl;
        std::cerr << "exception: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        // handle exception
        std::cerr << "exception: " << e.what() << std::endl;
    } catch (...) {
        // handle exception
        std::cerr << "any exception!" << std::endl;
    }
}
 
int main() {
    //f("all.dat");
    f("bogus.txt");
    return 0;
}

근데 문제는 아래와 같이 제 class에 옮겨 심었을 경우 입력 파일의 유무에 관계없이 무조건 예외가 발생합니다.ㅜㅜ

void RawDataProcessor::_readRawData(const char* filename) {
    // might call rawData constructor several times
    // since its size is unknown.
 
    // open a file
    _inFile.exceptions(std::ifstream::eofbit|std::ifstream::failbit|std::ifstream::badbit);
    // see: <a href="http://www.cplusplus.com/ref/iostream/ios/exceptions.html
" rel="nofollow">http://www.cplusplus.com/ref/iostream/ios/exceptions.html
</a>    // badbit : critical error in stream buffer
    // eofbit : End-Of-File reached while extracting
    // failbit: failure extracting from stream
    try {
        _inFile.open(filename, std::ios::in);
        if (_inFile.is_open()) {
            // length of row
            std::string line;
            while (getline(_inFile,line)) {
                if (line.find('#')!=std::string::npos || line.empty()) {
                    //std::clog << "found : " << line << endl;
                    continue;
                }            
                //std::clog << line << endl;
                // I cannot understand the below statement :(, but it works
                std::istringstream iss(line);
                _rawData.push_back(Row(std::istream_iterator<double>(iss),
                                       std::istream_iterator<double>()));	
            }// end of while
        }// end of if
        _inFile.close();
     } catch (const std::ifstream::failure& e) {
        const char* msg = "exception raised in reading a raw data file : "; 
        std::cerr << msg << filename << std::endl;
        //abort();
        exit(EXIT_FAILURE);
    }
}

음... try catch를 사용한 방식이 동일한데 이상합니다. 아무래도 제가 짚어내지 못하는 부분 때문에 입력 파일 유무에 관계없이 무조건 예외가 발생하는 듯 합니다. 사실 예제를 가지고 입력 파일이 있어도 예외가 발생하는 -- 제가 지금 겪는 것과 같은 상황을 만들어 보려고 이리 저리 해 보았지만 그런 이상한(?) 경우가 생기질 않더군요. :(

잘 아시는 분들이 한번 보시고 왜 입력 파일이 있어도 예외가 발생하는지 알려주시면 감사하겠습니다.

익명사용자의 이미지

'std::ifstream::eofbit'를 배고

밑의 while문은

while ( !_inFile.eof() )
{
_inFile.getline( ch, 1000 );
}// end of while

처럼 별도로 eof를 처리하는 게 좋겠다는 생각이 듭니다.

aeronova의 이미지

감사합니다.
답글 보고 하나하나 comment로 처리하면서 해 보니까 제가 사용한 while 문이 문제더군요.
while문을 조언해 주신 것 처럼 처리하고 eofbit을 빼니까 생각했던것처럼 작동합니다. :)

It's better to burn out than to fade away. -- Kurt Cobain.

doldori의 이미지

아마 생각했던 것처럼 동작하지는 않을 걸요. :-)
마지막에 똑같은 데이터가 두 번 들어가지 않습니까?

while 문에는 문제가 없습니다. eofbit이 있느냐 없느냐도 관계없고요.
문제는 정상적인 상황까지 예외 상황으로 처리했다는 것에 있습니다.
while 문의 내용은 파일을 "끝까지" 또는 잘못된 입력이 들어올 때까지
읽는 것입니다. 잘못된 입력이 없다면 당연히 언젠가는 EOF를 만나게 되고
eofbit이 켜지겠지요.
참고로 파일 끝에 도달하여 입력이 실패하면 eofbit과 failbit이 모두 켜집니다. 따라서

_inFile.exceptions(std::ifstream::failbit|std::ifstream::badbit);
_inFile.exceptions(std::ifstream::eofbit|std::ifstream::failbit|std::ifstream::badbit);

지금의 경우 이 둘은 차이가 없습니다.

해결책은 예외 핸들러에서 입력에 실패한 이유가 EOF 때문이라면
정상적인 상황으로 처리하는 것입니다.

catch (const std::ifstream::failure& e) {
    if (!_inFile.eof())
    {
        const char* msg = "exception raised in reading a raw data file : "; 
        std::cerr << msg << filename << std::endl;
        //abort();
        exit(EXIT_FAILURE);
    }
}

aeronova의 이미지

답글 감사합니다. 원래 제가 원하는 것은 입력 파일에 문제가 있는 경우(입력 파일이 존재하지 않거나..) 예외 처리를 하려고 했는데, failbit, eofbit등은 그런 상황을 다루기엔 적합하지 않아 보입니다.
여기(http://www.greenend.org.uk/rjk/2000/10/iostream.html)를 보니 각각의 bit이 어떤 상황에서 발생하는지 설명되어 있네요. 파일의 존재 여부를 검사하는 것은 C++에서 기본적으로 제공되지 않는 듯 한데, 대강 검색해보니 boost가 무난한 듯 보입니다. 이걸 좀 파보던지 해야겠습니다.

p.s.
흠.. 근데 ifstream에서 file을 열때 그 file이 존재하지 않으면 어떤 일이 발생하나 궁금하네요.
예외가 발생할 것이라 생각했는데 (failbit같은 것)...

It's better to burn out than to fade away. -- Kurt Cobain.

doldori의 이미지

failbit이 켜지는 원인은 여러 가지가 있고 파일이 존재하지 않는 경우도 이에 해당합니다.

단순히 입력에 문제가 있는 경우에 대한 예외 처리라면 표준에서 제공하는
기능만으로 충분할 듯 싶습니다.
그게 아니라 문제가 생긴 이유(파일이 존재하지 않음, 읽기/쓰기 권한이 없음,
다른 프로세스에 의해 잠겨 있음 등)에 대한 구체적인 정보를 원한다면
이런 것은 어차피 표준의 범위 밖이니 환경 의존적인 방법을 써야겠지요.

thyoo의 이미지

심지어는 C이건 C++이건
seek, tell 조합 이외에는 file size를 알아낼 방법조차 없읍니다.

stat, fstat은 POSIX이고 대개의 C Library가 지원하지만
C 표준은 아닙니다.
___________________________________
Less is More (Robert Browning)

___________________________________
Less is More (Robert Browning)

aeronova의 이미지

에고.. 이상하게 거의 같은 코드를 사용하는 다른 곳에서는 말씀하신 것이 작동하지 않네요.ㅜㅜ
while도 고쳤고 getline도 고쳤는데, 여전히 파일이 있는데도 exception을 발생시킵니다.
아래가 그 문제의 코드입니다.

Matrix Regressor::_setParaData(const char* filename) {
    _inFile.exceptions(std::ifstream::failbit|std::ifstream::badbit);
    try {
    Matrix tempMatrix;
    printDebugMSG("processing",filename);
    _inFile.open(filename, std::ios::in);
    if (_inFile.is_open()) {
        //while (getline(_inFile,line)) {
        while (!_inFile.eof()) {
        /*
        std::string line;
        getline(_inFile,line);
        */
        char cstr[1000];
        _inFile.getline(cstr,1000);
/*
        if (line.find('#')!=std::string::npos || line.empty()) {
            //std::clog << "found : " << line << endl;
            continue;
        }            
        //std::clog << line << endl;
        // I cannot understand the below statement :(, but it works
        std::istringstream iss(line);
        tempMatrix.push_back(Row(std::istream_iterator<double>(iss),
                                 std::istream_iterator<double>()));	
*/
        }// end of while
    }// end of if
    _inFile.close();
    return tempMatrix;
    } catch (const std::ifstream::failure& e) {
        const char* msg = "exception raised in reading a file : "; 
        std::cerr << msg << filename << std::endl;
        std::cerr << e.what() << std::endl;
        //abort();
        exit(EXIT_FAILURE);
    }
}

while만 쓰고 while 안을 comment 처리했을 때 정상 작동 하는 것으로 보아 뭔가 파일을 읽는 getline 부분에서 잘못되지 않았나 싶습니다. 흠... 이유를 알 수 없으니 참 답답하네요.ㅜㅜ

It's better to burn out than to fade away. -- Kurt Cobain.

doldori의 이미지

위의 제 답글을 보시고 같은 방식으로 하면 되겠습니다.

while (!_inFile.eof()) { /* ... */ }

이것은 잘못된(그리고 많이들 잘못 알고 있는) 방법입니다.

그리고 코멘트에 stream iterator를 사용하는 것을 잘 모르겠다고 하셨군요. :-)
이를 풀어쓰면

std::istringstream iss(line);
std::istream_iterator<double> beg(iss);  // iterator pointing to the beginning of a stream
std::istream_iterator<double> end;       // iterator pointing to EOF
Row row(beg, end);   // a constructor of the form vector(iterator, iterator)
tempMatrix.push_back(row); 

aeronova의 이미지

while문은 원래 제가 사용한 것이 맞더군요. 이전에 제가 포럼에 질문하고 다루었던 건데(http://kldp.org/node/72257) 그새 까먹었었네요. 그리고 iterator 사용법 풀어주신 것 감사합니다. :)

It's better to burn out than to fade away. -- Kurt Cobain.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.