I am struggling with the problem of reading input from file on a per-line basis, in a cross-platform way.
Different platforms use different sequences of characters to represent a new line/end of line.
std::getline
doesn't deal with these in a cross platform way.
What do I mean by this?
std::getline
changes its behavior depending on the platform on which an executable is compiled. On Windows platforms, it expects to seeCRLF
to denote line endings. On Linux, it expects justLF
.- It does not handle cases where a file contains a line ending which is not what the platform expects. For example a file created on a Windows machine is likely to have
CRLF
line endings. If that file is copied to a Linux machine without changing the line ending format thenstd::getline
"breaks".
It seemed to me that the easiest way to work around this would be to create a new function which wraps std::getline
. Something like this:
return_type GetLine(stream_type ifs, string_type s)
{
return_type ret = std::getline(ifs, s);
s.erase(std::remove(s.begin(), s.end(), '\r' ), s.end());
s.erase(std::remove(s.begin(), s.end(), '\n' ), s.end());
return ret;
}
However at this point I'm stuck. From some searching, although getline
returns a stream object (?) it also has an implicit cast-to-bool operator.
I could force return_type
to be bool
, but then this prevents my wrapper function from returning a stream object, if such a thing were to be required in future.
I also haven't been able to make sense of the STL templates in a sufficient enough way to determine what stream_type
and string_type
should be. I can force them to be std::ifstream
and std::string
, but I think this decision would also make the function less generic.
How should I proceed here?
CodePudding user response:
You should take the stream by reference because streams typically cannot be copied. Also the string should be passed by reference because you want to write to it.
To be generic you can use the same interface as std::getline
does. As you want to use specific delimiters, they need not be passed as arguments. If you make the function a template then it will work with any stream that also works for std::getline
:
#include <iostream>
#include <sstream>
#include <string>
template< class CharT, class Traits, class Allocator >
std::basic_istream<CharT,Traits>& my_getline(
std::basic_istream<CharT,Traits>& input,
std::basic_string<CharT,Traits,Allocator>& str)
{
return std::getline(input,str);
}
int main() {
std::istringstream s{"hello world"};
std::string foo;
my_getline(s,foo);
std::cout << foo;
}
However at this point I'm stuck. From some searching, although getline returns a stream object (?) it also has an implicit cast-to-bool operator.
It's not getline
that converts to bool
but the stream returned by getline
can be converted to bool
. Your line is almost correct, but it needs to be a reference (and you need not spell out the type explicitly):
auto& ret = std::getline(ifs, s);
// more code
return ret;
Note that I didn't address the actual issue of extracting characters until any of the delimiters is encountered (rather than only the platform specific newline that you already get with bare std::getline
).