Изменение разделителя для cin (C++)

Я перенаправил "Cin" для чтения из потока файлов cin.rdbug(inF.rdbug())Когда я использую оператор извлечения, он читает, пока не достигнет пробела.

Можно ли использовать другой разделитель? Я прошел через API в cplusplus.com, но ничего не нашел.

3 ответа

Можно изменить разделитель между словами для cin или любой другой std::istream, с помощью std::ios_base::imbue добавить кастом ctypefacet,

Если вы читаете файл в стиле /etc/passwd, следующая программа будет читать каждый : -ограниченное слово отдельно.

#include <locale>
#include <iostream>


struct colon_is_space : std::ctype<char> {
  colon_is_space() : std::ctype<char>(get_table()) {}
  static mask const* get_table()
  {
    static mask rc[table_size];
    rc[':'] = std::ctype_base::space;
    rc['\n'] = std::ctype_base::space;
    return &rc[0];
  }
};

int main() {
  using std::string;
  using std::cin;
  using std::locale;

  cin.imbue(locale(cin.getloc(), new colon_is_space));

  string word;
  while(cin >> word) {
    std::cout << word << "\n";
  }
}

Для строк вы можете использовать std::getline перегрузки для чтения с использованием другого разделителя.

Для извлечения чисел разделитель на самом деле не "пробел", а любой недопустимый символ в числе.

Это улучшение Robᵩ, потому что это правильный ответ (и я разочарован, что он не был принят).

Что вам нужно сделать, это изменить массив, который ctype смотрит, чтобы решить, что такое разделитель.

В простейшем случае вы можете создать свой собственный:

const ctype<char>::mask foo[ctype<char>::table_size] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ctype_base::space};

На моей машине '\n' 10. Я установил для этого элемента массива значение разделителя: ctype_base::space, ctype инициализируется с foo будет ограничиваться только '\n' не ' ' или же '\t',

Теперь это проблема, потому что массив передан в ctype определяет больше, чем просто разделитель, он также определяет буквы, числа, символы и некоторые другие ненужные вещи, необходимые для потоковой передачи. (Ответ Ben Voigt касается этого.) Так что мы действительно хотим изменить mask, а не создавать с нуля.

Это может быть достигнуто следующим образом:

const auto temp = ctype<char>::classic_table();
vector<ctype<char>::mask> bar(temp, temp + ctype<char>::table_size);

bar[' '] ^= ctype_base::space;
bar['\t'] &= ~(ctype_base::space | ctype_base::cntrl);
bar[':'] |= ctype_base::space;

ctype инициализируется с bar будет ограничивать на '\n' а также ':' но нет ' ' или же '\t',

Вы идете о настройке cinили любой другой istream, чтобы использовать ваш обычай ctype как это:

cin.imbue(locale(cin.getloc(), new ctype<char>(data(bar))));

Вы также можете переключаться между ctypes и поведение изменится в середине потока:

cin.imbue(locale(cin.getloc(), new ctype<char>(foo)));

Если вам нужно вернуться к поведению по умолчанию, просто сделайте это:

cin.imbue(locale(cin.getloc(), new ctype<char>));

Живой пример

Это улучшение ответа Джона и примера cppreference.com. Таким образом, это следует той же предпосылке, что и оба, но объединяет их с параметризованными разделителями.

struct delimiter_ctype : std::ctype<char> {
    static const mask* make_table(std::string delims)
    {
        // make a copy of the "C" locale table
        static std::vector<mask> v(classic_table(), classic_table() + table_size);
        for(mask m : v){
            m &= ~space;
        }
        for(char d : delims){
            v[d] |= space;
        }
        return &v[0];
    }
    delimiter_ctype(std::string delims, ::size_t refs = 0) : ctype(make_table(delims), false, refs) {}
};

Ура!

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