C++ 从istream读取格式化输入
下面的问题已从实际需求中简化 考虑以下计划:C++ 从istream读取格式化输入,c++,C++,下面的问题已从实际需求中简化 考虑以下计划: #include <iostream> #include <iterator> #include <string> #include <set> #include <algorithm> using namespace std; typedef string T; // to simplify, always consider T as string template<typen
#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;
}
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
typedef字符串T;//为了简化,总是把T当作字符串。
模板
void do_something(常量输入迭代器&first,常量输入迭代器&last){
常量ostream_迭代器os(cout,“\n”);
常量集合词(第一个、最后一个);
复制(words.begin()、words.end()、os);
}
int main(int argc,字符**argv){
常量istream_迭代器为(cin),eof;
做某事(是,eof);
返回0;
}
该程序从istream
(cin
)中提取所有单词,并对它们进行处理。默认情况下,每个单词都用空格分隔。格式化提取背后的逻辑在istream\u迭代器中
我现在需要做的是将两个迭代器传递到do_something()
以便提取的单词将由标点字符而不是空格分隔(空格将被视为“正常”字符)。你会怎样用“干净的C++方式”(即,最小的努力)? 尽管它不是先验的显而易见的,但是有一个相对简单的方式来改变流被认为是空白的内容。这样做的方法是:<代码> IMBUEE()>代码>流,使用<代码> STD::LoalAs//Cord>对象,其<代码> STD::cType < /Cord>刻面被替换为将所需字符视为空白。code>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) {}
};
请注意,这实际上只将、
和\n
视为空格字符。这也意味着不会跳过其他字符作为空白。。。当然,多个逗号字符的序列被认为只是一个分隔符,而不可能创建一组空字符串。还要注意,上面的std::ctype
facet删除了所有其他字符分类。如果要解析字符串以外的其他对象,则可能需要保留其他字符分类,并仅对空格进行更改。以下是一种方法:
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];
};
模板
结构commactype_base{
commactype_base():表{
std::transform(std::ctype::classic_table(),
std::ctype::classic_table()+std::ctype::table_size,
这个->表,
[](std::ctype_base::mask m)->std::ctype_base::mask{
返回m&~(std::ctype_base::space);
});
这->表u[static_cast(S0)]|=std::ctype_base::space;
这->表u[static_cast(S1)]|=std::ctype_base::space;
}
std::ctype::mask table_[std::ctype::table_size];
};
遗憾的是,这与我在系统上使用的gcc版本(显然是
std::ctype::classic_table()相冲突)
生成一个空指针。使用当前版本的clang编译此代码不起作用,因为clang不支持lambda。有两个警告,上面的代码应该是正确的,不过…我不知道表的其余部分在哪里填充了默认值……这会不会破坏所有非空格字符类型?@Ben Voigt:我喜欢我的代码可能是正确的,但幸运的是:微妙的关键是:table_uz()
用默认的类型掩码填充表格?很好,但是通常的空白字符仍然被标记为空格,因为您没有更改它们。等等,我不确定。ctype::mask
是char
的类型定义,所以所有值都初始化为零。这清除了“spaciness”通常的空白字符,但它也打破了所有其他ctype类别。没有字符会被测试为上限
,下限
,数字
,xdigit
,等等。默认掩码为空。我创建了一个空掩码表,并将表中的两个条目设置为空格。使用这个特殊的std::ctype
facet对于改变空间的含义以外的其他东西来说肯定不会太成功,但我并没有打算这么做。
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];
};