Изменение разделителя для cin (C++)
Я перенаправил "Cin" для чтения из потока файлов cin.rdbug(inF.rdbug())
Когда я использую оператор извлечения, он читает, пока не достигнет пробела.
Можно ли использовать другой разделитель? Я прошел через API в cplusplus.com, но ничего не нашел.
3 ответа
Можно изменить разделитель между словами для 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";
}
}
Для строк вы можете использовать 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))));
Вы также можете переключаться между ctype
s и поведение изменится в середине потока:
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) {}
};
Ура!