2011-01-27 52 views
10

在我用ifstream打開的文件中獲取當前行的行號的最佳方法是什麼?因此,我正在讀取數據,我需要存儲它所在的行號,以便以後可以在數據與規格不匹配時顯示。如何從C++文件中獲取行號?

回答

9

從ifstream的觀點來看,沒有行號。如果你逐行讀入文件,那麼你只需要自己跟蹤它。

+0

注意:您可以逐行閱讀(`std :: getline`),或者只是計算經過的\ n字符的數量。 – 2011-01-27 09:07:44

4

使用std::getline逐一讀取每一行。保留一個整數,指出已閱讀的行數:將其初始化爲零,並且每次調用std::getline併成功時,將其遞增。

8

如果想限制自己std::getline,那麼你可以使用從std::streambuf派生類,並跟蹤當前的行號:

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; 
} 
相關問題