C++ 如何从C++中的文件中获取行号?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4813129/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:37:27  来源:igfitidea点击:

How to get the line number from a file in C++?

c++linefstream

提问by Mike

What would be the best way to get the line number of the current line in a file that I have opened with a ifstream? So I am reading in the data and I need to store the line number that it is on so that I can display it later if the data doesn't match the specifications.

在我用 . 打开的文件中获取当前行的行号的最佳方法是ifstream什么?所以我正在读入数据,我需要存储它所在的行号,以便在数据与规范不匹配时可以显示它。

回答by Daniel Gehriger

If you don'twant to limit yourself to std::getline, then you could use class derived from std::streambuf, and which keeps track of the current line number:

如果你希望限制自己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();


Here is a sample implementation of CountingStreamBuffer:

这是一个示例实现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
};

回答by Keith

From an ifstream point of view there is no line number. If you read in the file line by line, then you just have to keep track of it yourself.

从 ifstream 的角度来看,没有行号。如果您逐行读取文件,那么您只需要自己跟踪它。

回答by James McNellis

Use std::getlineto read each line in one by one. Keep an integer indicating the number of lines you have read: initialize it to zero and each time you call std::getlineand it succeeds, increment it.

用于std::getline逐行读取。保留一个整数,表示您已阅读的行数:将其初始化为零,每次调用std::getline并成功时,将其递增。

回答by opetroch

An inefficient but dead simple way is to have a function that given a stream, it counts the new line characters from the beginning of the stream to the current position.

一种低效但非常简单的方法是使用一个函数来给定一个流,它计算从流的开头到当前位置的换行符。

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;
}

In some code I am working on, I am only interested to know the line number if invalid input is encountered, in which case import is aborted immediately. Since the function is called only once the inefficiency is not really a problem.

在我正在处理的一些代码中,如果遇到无效输入,我只想知道行号,在这种情况下,导入会立即中止。由于该函数仅被调用一次,因此效率低下并不是真正的问题。

The following is a full example:

下面是一个完整的例子:

#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;
}