Как получить номер строки из файла на С++?

Каким будет лучший способ получить номер строки текущей строки в файле, который я открыл с помощью ifstream? Поэтому я читаю данные, и мне нужно сохранить номер строки, на которой он находится, чтобы я мог отображать его позже, если данные не соответствуют спецификациям.

Ответ 1

С точки зрения потока нет номера строки. Если вы читаете в строке строки за строкой, вам просто нужно следить за ней самостоятельно.

Ответ 2

Если вы не хотите ограничить себя 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
};

Ответ 3

Используйте std::getline для чтения каждой строки по одному. Сохраните целое число, указывающее количество прочитанных строк: инициализируйте его до нуля и каждый раз, когда вы вызываете std::getline, и он преуспевает, увеличивайте его.

Ответ 4

Неэффективный, но мертвый простой способ состоит в том, чтобы иметь функцию, которая задает поток, который подсчитывает новые строковые символы от начала потока до текущей позиции. Это неэффективно, потому что если вы хотите знать строку для нескольких позиций потока, вы должны называть ее несколько раз, начиная отсчет с начала потока каждый раз. В некотором коде, над которым я работаю, мне интересно узнать номер строки, если встречается недопустимый ввод, в этом случае импорт немедленно прерывается. Поскольку функция вызывается только однажды, неэффективность не является проблемой.

Вот мой код для этого:

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