2011-01-27 136 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; 
} 
相关问题