ОК, вот один из способов сделать это. В основном я сделал реализацию std::getline
, которая принимает предикат вместо символа. Это даст вам 2/3 пути:
template <class Ch, class Tr, class A, class Pred>
std::basic_istream<Ch, Tr> &getline(std::basic_istream<Ch, Tr> &is, std::basic_string<Ch, Tr, A>& str, Pred p) {
typename std::string::size_type nread = 0;
if(typename std::istream::sentry(is, true)) {
std::streambuf *sbuf = is.rdbuf();
str.clear();
while (nread < str.max_size()) {
int c1 = sbuf->sbumpc();
if (Tr::eq_int_type(c1, Tr::eof())) {
is.setstate(std::istream::eofbit);
break;
} else {
++nread;
const Ch ch = Tr::to_char_type(c1);
if (!p(ch)) {
str.push_back(ch);
} else {
break;
}
}
}
}
if (nread == 0 || nread >= str.max_size()) {
is.setstate(std::istream::failbit);
}
return is;
}
с функтором, подобным этому:
struct is_newline {
bool operator()(char ch) const {
return ch == '\n' || ch == '\r';
}
};
Теперь осталось только определить, закончили ли вы '\r'
или нет ... если вы это сделали, то, если следующий символ - '\n'
, просто используйте его и проигнорируйте.
РЕДАКТИРОВАТЬ : Итак, чтобы поместить все это в функциональное решение, вот пример:
#include <string>
#include <sstream>
#include <iostream>
namespace util {
struct is_newline {
bool operator()(char ch) {
ch_ = ch;
return ch_ == '\n' || ch_ == '\r';
}
char ch_;
};
template <class Ch, class Tr, class A, class Pred>
std::basic_istream<Ch, Tr> &getline(std::basic_istream<Ch, Tr> &is, std::basic_string<Ch, Tr, A>& str, Pred &p) {
typename std::string::size_type nread = 0;
if(typename std::istream::sentry(is, true)) {
std::streambuf *const sbuf = is.rdbuf();
str.clear();
while (nread < str.max_size()) {
int c1 = sbuf->sbumpc();
if (Tr::eq_int_type(c1, Tr::eof())) {
is.setstate(std::istream::eofbit);
break;
} else {
++nread;
const Ch ch = Tr::to_char_type(c1);
if (!p(ch)) {
str.push_back(ch);
} else {
break;
}
}
}
}
if (nread == 0 || nread >= str.max_size()) {
is.setstate(std::istream::failbit);
}
return is;
}
}
int main() {
std::stringstream ss("this\ris a\ntest\r\nyay");
std::string item;
util::is_newline is_newline;
while(util::getline(ss, item, is_newline)) {
if(is_newline.ch_ == '\r' && ss.peek() == '\n') {
ss.ignore(1);
}
std::cout << '[' << item << ']' << std::endl;
}
}
Я внес пару небольших изменений в свой исходный пример. Параметр Pred p
теперь является ссылкой, так что предикат может хранить некоторые данные (в частности, последний протестированный char
). И также я сделал предикат operator()
неконстантным, чтобы он мог хранить этот символ.
В основном у меня есть строка в std::stringstream
, в которой есть все 3 версии переносов строк. Я использую мой util::getline
, и если объект предиката говорит, что последний char
был '\r'
, тогда я peek()
вперед и игнорирую 1
символ, если он окажется '\n'
.