问题
Suppose I'm reading tokens from a std::istream
. What is a good way to know the number of newlines that have been read from the stream? This is for the purpose of error reporting in a parser. I don't want to use std::getline
to read input.
Here is a test case. I am looking for something functionally similar to GetLineNumber
, which in this case would return the line number of the last read token.
std::stringstream ss;
ss << "1 \n 2 \n 3 \n";
int x;
while (ss >> x) {
std::cout << "Line " << GetLineNumber(ss) << ": " << x << std::endl;
}
The output to this example should be:
Line 1: 1
Line 2: 2
Line 3: 3
回答1:
You can use a filtering streambuf, and keep count there:
class LineNumberStreambuf : public std::streambuf
{
std::streambuf* mySource;
std::istream* myOwner;
bool myIsAtStartOfLine;
int myLineNumber;
char myBuffer;
protected:
int underflow()
{
int ch = mySource->sbumpc();
if ( ch != EOF ) {
myBuffer = ch;
setg( &myBuffer, &myBuffer, &myBuffer + 1 );
if ( myIsAtStartOfLine ) {
++ myLineNumber;
}
myIsAtStartOfLine = myBuffer == '\n';
}
return ch;
}
public:
LineNumberStreambuf( std::streambuf* source )
: mySource( source )
, myOwner( nullptr )
, myIsAtStartOfLine( true )
, myLineNumber( 0 )
{
}
LineNumberStreambuf( std::istream& owner )
: mySource( owner.rdbuf() )
, myOwner( &owner )
, myIsAtStartOfLine( true )
, myLineNumber( 0 )
{
myOwner->rdbuf( this );
}
~LineNumberStreambuf()
{
if ( myOwner != nullptr ) {
myOwner.rdbuf( mySource );
}
}
int lineNumber() const
{
return myLineNumber;
}
};
Just insert this into your input:
LineNumberStreambuf ln( std::cin );
// ...
std::cerr << "Error (" << ln.lineNumber << "): ..." << std::endl;
Note that line numbers will only reflect the input which takes place through the streambuf.
来源:https://stackoverflow.com/questions/24859739/line-number-in-a-c-istream