Альтернатива std :: getline, когда вводные окончания строки смешаны - PullRequest
7 голосов
/ 14 июля 2011

Я пытаюсь прочитать строки из std::istream, но вход может содержать '\r' и / или '\n', поэтому std::getline бесполезен.

Извините, что кричите, но это, кажется, требует акцента ...

Вход может содержать либо тип новой строки , либо оба .

Есть листандартный способ сделать это?В данный момент я пытаюсь

char c;
while (in >> c && '\n' != c && '\r' != c)
    out .push_back (c);

... но это пропускает пробелы.D'о!std::noskipws - больше требуется тряска, и теперь он теряет сознание.

Наверняка должен быть лучший способ?!?

Ответы [ 2 ]

4 голосов
/ 14 июля 2011

ОК, вот один из способов сделать это. В основном я сделал реализацию 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'.

3 голосов
/ 14 июля 2011

Обычный способ прочитать строку - std::getline.

Редактировать: Если ваша реализация std::getline не работает, вы можете написать что-то похожее на ваше, что-то вроде этого:

std::istream &getline(std::istream &is, std::string &s) { 
    char ch;

    s.clear();

    while (is.get(ch) && ch != '\n' && ch != '\r')
        s += ch;
    return is;
}

Я должен добавить, что технически это, вероятно, не является причиной нарушения std::getline, поскольку прерывается реализация базового потока - это поток, который нужно преобразовать из любых символов, обозначающих конец строки для платформа, в символ новой строки. Независимо от того, какие именно части сломаны, однако, если ваша реализация сломана, это может быть в состоянии восполнить это (с другой стороны, если ваша реализация сломана достаточно сильно, трудно быть уверенным, что это также будет работать).

...