изменение разделителя для cin (c ++) - PullRequest
42 голосов
/ 05 сентября 2011

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

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

Ответы [ 4 ]

42 голосов
/ 05 сентября 2011

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

Если вы читаете файл в стиле / 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";
  }
}
20 голосов
/ 05 сентября 2011

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

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

15 голосов
/ 28 января 2015

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

Вам нужно изменить массив, на который 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 * ответ 1022 * касается этого.) Поэтому мы действительно хотим изменить a 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;

A ctype, инициализированный bar, будет разделять на '\n' и ':', но не ' ' или '\t'.

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

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

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

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

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

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

Живой пример

0 голосов
/ 21 марта 2019

Это улучшение ответа Джона и примера из 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) {}
};

ура!

...