Чтение отформатированного ввода из istream

Приведенная ниже проблема была упрощена от реальных требований.

Рассмотрим следующую программу:

#include <iostream>
#include <iterator>
#include <string>
#include <set>
#include <algorithm>

using namespace std;

typedef string T; // to simplify, always consider T as string

template<typename input_iterator>
void do_something(const input_iterator& first, const input_iterator& last) {
    const ostream_iterator<T> os(cout, "\n");
    const set<T> words(first, last);
    copy(words.begin(), words.end(), os);
}

int main(int argc, char** argv) {
    const istream_iterator<T> is(cin), eof;
    do_something(is, eof);
    return 0;
}

Программа извлекает все слова из istream (cin) и что-то с ними делает. Каждое слово по умолчанию отделяется пробелом. Логика форматированного извлечения находится внутри istream_iterator,

Что мне нужно сделать сейчас, это перейти к do_something() два итератора, чтобы извлеченные слова были разделены символом пунктуации вместо пробела (пробелы будут рассматриваться как "нормальные" символы). Как бы вы сделали это "чистым способом C++" (то есть с минимальными усилиями)?

1 ответ

Решение

Хотя это априори не очевидно, есть относительно простой способ изменить то, что поток считает пробелом. Способ сделать это состоит в том, чтобы imbue() поток с std::locale объект которого std::ctype<char> фасет заменяется, чтобы рассматривать желаемые символы как пробел. imbue(), locale, ctype - а? Хорошо, ну, это не обязательно то, что вы используете изо дня в день, так что вот краткий пример, который настраивает std::cin использовать запятые и символы новой строки как пробел:

#include <locale>
template <char S0, char S1>
struct commactype_base {
    commactype_base(): table_() {
        this->table_[static_cast<unsigned char>(S0)] = std::ctype_base::space;
        this->table_[static_cast<unsigned char>(S1)] = std::ctype_base::space;
    }
    std::ctype<char>::mask table_[std::ctype<char>::table_size];
};
template <char S0, char S1 = S0>
struct ctype:
    commactype_base<S0, S1>,
    std::ctype<char>
{
    ctype(): std::ctype<char>(this->table_, false) {}
};

На самом деле, это конкретная реализация std::ctype<char> на самом деле может быть использован для использования одного или двух произвольных chars как пробелы (правильная версия C++2011, вероятно, допускает произвольное количество аргументов; кроме того, они не обязательно должны быть аргументами шаблона). В любом случае, с этим на месте, просто опустите следующую строку в начале вашего main() Функция и все готово:

std::cin.imbue(std::locale(std::locale(), new ::ctype<',', '\n'>));

Обратите внимание, что это действительно только учитывает , а также \n как космические символы. Это также означает, что никакие другие символы не пропускаются в качестве пробела.... и, конечно, последовательность из нескольких символов запятой считается одним разделителем, а не может создавать кучу пустых строк. Также обратите внимание, что выше std::ctype<char> Фасет удаляет все другие классификации символов. Если вы хотите проанализировать другие объекты, а не только строки, вы можете сохранить другую классификацию символов и изменить ее только для пробелов. Вот как это можно сделать:

template <char S0, char S1>
struct commactype_base {
    commactype_base(): table_() {
        std::transform(std::ctype<char>::classic_table(),
                       std::ctype<char>::classic_table() + std::ctype<char>::table_size,
                       this->table_, 
                       [](std::ctype_base::mask m) -> std::ctype_base::mask {
                           return m & ~(std::ctype_base::space);
                       });
        this->table_[static_cast<unsigned char>(S0)] |= std::ctype_base::space;
        this->table_[static_cast<unsigned char>(S1)] |= std::ctype_base::space;
    }
    std::ctype<char>::mask table_[std::ctype<char>::table_size];
};

К сожалению, это происходит с версией gcc, установленной в моей системе (по-видимому, std::ctype<char>::classic_table() дает нулевой указатель. Компиляция этого с текущей версией clang не работает, потому что clang не поддерживает лямбду. С двумя оговорками приведенный выше код должен быть правильным, хотя...

Другие вопросы по тегам