2011-01-27 30 views
10

ifstreamで開いたファイルの現在の行の行番号を取得する最も良い方法は何ですか?だから私はデータを読み込んでいるので、データが仕様と一致しないと後で表示できるように、行番号を格納する必要があります。C++のファイルから行番号を取得する方法は?

答えて

9

ifstreamの観点からは、行番号はありません。ファイルを1行ずつ読み込むと、それを自分で追跡するだけです。

+0

注:1行ずつ( 'std :: getline')読み込むか、単に渡す' \ n'文字の数を数えることができます。 –

+0

@Matthieu:これは、それぞれの '\ n'があなたのプラットフォーム上の改行を意味すると仮定しています。 – rubenvb

+0

@rubenvb:テキストモードでは '\ n'は改行を表し、' iostream'はロケールを使ってそれを作る(両方の読み込み書き込み)。バイナリモードで読み込んだ場合、変換は行われません。 –

4

std::getlineを使用して、各行を1つずつ読み込みます。読み込んだ行数を示す整数を0に初期化し、std::getlineを呼び出して成功するたびにインクリメントします。

8

あなたはは、あなたがstd::streambufから派生したクラスを使用することができ、かつこれは、現在の行番号を追跡し、std::getlineに自分自身を制限しない場合:ここで

class CountingStreamBuffer : public std::streambuf { /* see below */ }; 

// open file 
std::ifstream file("somefile.txt"); 

// "pipe" through counting stream buffer 
CountingStreamBuffer cntstreambuf(file.rdbuf()); 
std::istream is(&cntstreambuf); 

// sample usage 
is >> x >> y >> z; 
cout << "At line " << cntstreambuf.lineNumber(); 
std::getline(is, str); 
cout << "At line " << cntstreambuf.lineNumber(); 

ですCountingStreamBufferのサンプル実装:

#include <streambuf> 

class CountingStreamBuffer : public std::streambuf 
{ 
public: 
    // constructor 
    CountingStreamBuffer(std::streambuf* sbuf) : 
     streamBuf_(sbuf), 
     lineNumber_(1), 
     lastLineNumber_(1), 
     column_(0), 
     prevColumn_(static_cast<unsigned int>(-1)), 
     filePos_(0) 
    { 
    } 

    // Get current line number 
    unsigned int  lineNumber() const { return lineNumber_; } 

    // Get line number of previously read character 
    unsigned int  prevLineNumber() const { return lastLineNumber_; } 

    // Get current column 
    unsigned int  column() const { return column_; } 

    // Get file position 
    std::streamsize  filepos() const { return filePos_; } 

protected: 
    CountingStreamBuffer(const CountingStreamBuffer&); 
    CountingStreamBuffer& operator=(const CountingStreamBuffer&); 

    // extract next character from stream w/o advancing read pos 
    std::streambuf::int_type underflow() 
    { 
     return streamBuf_->sgetc(); 
    } 

    // extract next character from stream 
    std::streambuf::int_type uflow() 
    { 
     int_type rc = streamBuf_->sbumpc(); 

     lastLineNumber_ = lineNumber_; 
     if (traits_type::eq_int_type(rc, traits_type::to_int_type('\n'))) 
     { 
      ++lineNumber_; 
      prevColumn_ = column_ + 1; 
      column_ = static_cast<unsigned int>(-1); 
     } 

     ++column_; 
     ++filePos_; 
     return rc; 
    } 

    // put back last character 
    std::streambuf::int_type pbackfail(std::streambuf::int_type c) 
    { 
     if (traits_type::eq_int_type(c, traits_type::to_int_type('\n'))) 
     { 
      --lineNumber_; 
      lastLineNumber_ = lineNumber_; 
      column_ = prevColumn_; 
      prevColumn_ = 0; 
     } 

     --column_; 
     --filePos_; 

     if (c != traits_type::eof()) 
      return streamBuf_->sputbackc(traits_type::to_char_type(c)); 
     else 
      return streamBuf_->sungetc(); 
    } 

    // change position by offset, according to way and mode 
    virtual std::ios::pos_type seekoff(std::ios::off_type pos, 
            std::ios_base::seekdir dir, 
            std::ios_base::openmode mode) 
    { 
     if (dir == std::ios_base::beg 
     && pos == static_cast<std::ios::off_type>(0)) 
     { 
      lastLineNumber_ = 1; 
      lineNumber_ = 1; 
      column_ = 0; 
      prevColumn_ = static_cast<unsigned int>(-1); 
      filePos_ = 0; 

      return streamBuf_->pubseekoff(pos, dir, mode); 
     } 
     else 
      return std::streambuf::seekoff(pos, dir, mode); 
    } 

    // change to specified position, according to mode 
    virtual std::ios::pos_type seekpos(std::ios::pos_type pos, 
            std::ios_base::openmode mode) 
    { 
     if (pos == static_cast<std::ios::pos_type>(0)) 
     { 
      lastLineNumber_ = 1; 
      lineNumber_ = 1; 
      column_ = 0; 
      prevColumn_ = static_cast<unsigned int>(-1); 
      filePos_ = 0; 

      return streamBuf_->pubseekpos(pos, mode); 
     } 
     else 
      return std::streambuf::seekpos(pos, mode); 
    } 


private: 
    std::streambuf*  streamBuf_;  // hosted streambuffer 
    unsigned int  lineNumber_; // current line number 
    unsigned int  lastLineNumber_;// line number of last read character 
    unsigned int  column_;  // current column 
    unsigned int  prevColumn_; // previous column 
    std::streamsize  filePos_;  // file position 
}; 
0

非効率的だが死んだ簡単な方法ですストリームを指定すると、ストリームの始めから現在の位置までの改行文字が数えられます。複数のストリーム位置の行を知りたい場合は、その都度、ストリームの先頭から数え始めて何度も呼び出す必要があるため、非効率です。私が取り組んでいるコードでは、無効な入力に遭遇した場合にのみ行番号を知りたいと思っています。その場合、インポートは即座に中止されます。この関数は、非効率性が実際問題ではない場合にのみ呼び出されるため、ここで

は、これを行うための私のコードです:メソッドの

int getCurrentLine(std::istream& is) 
{ 
    int lineCount = 1; 
    is.clear();  // need to clear error bits otherwise tellg returns -1. 
    auto originalPos = is.tellg(); 
    if (originalPos < 0) 
     return -1; 
    is.seekg(0); 
    char c; 
    while ((is.tellg() < originalPos) && is.get(c)) 
    { 
     if (c == '\n') ++lineCount; 
    } 
    return lineCount; 
} 

一つの利点は何も変更はストリームが構築される場所で必要とされていないということです、あなたはちょうどあなたがそれを必要とする機能を呼び出す必要があります。以下は完全な例です:

#include <iostream> 
#include <sstream> 


int getCurrentLine(std::istream& is) 
{ 
    int lineCount = 1; 
    is.clear();  // need to clear error bits otherwise tellg returns -1. 
    auto originalPos = is.tellg(); 
    if (originalPos < 0) 
     return -1; 
    is.seekg(0); 
    char c; 
    while ((is.tellg() < originalPos) && is.get(c)) 
    { 
     if (c == '\n') ++lineCount; 
    } 
    return lineCount; 
} 

void ReadDataFromStream(std::istream& s) 
{ 
    double x, y, z; 
    while (!s.fail() && !s.eof()) 
    { 
     s >> x >> y >> z; 
     if (!s.fail()) 
      std::cout << x << "," << y << "," << z << "\n"; 
    } 

    if (s.fail()) 
     std::cout << "Error at line: " << getCurrentLine(s) << "\n"; 
    else 
     std::cout << "Read until line: " << getCurrentLine(s) << "\n"; 
} 

int main(int argc, char* argv[]) 
{ 
    std::stringstream s; 
    s << "0.0 0.0 0.0\n"; 
    s << "1.0 ??? 0.0\n"; 
    s << "0.0 1.0 0.0\n"; 
    ReadDataFromStream(s); 

    std::stringstream s2; 
    s2 << "0.0 0.0 0.0\n"; 
    s2 << "1.0 0.0 0.0\n"; 
    s2 << "0.0 1.0 0.0"; 
    ReadDataFromStream(s2); 

    return 0; 
} 
関連する問題