从istream读取格式化的输入 [英] Reading formatted input from an istream

查看:260
本文介绍了从istream读取格式化的输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下问题已从实际需求中简化。



请考虑以下程序:

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

using namespace std;

typedef string T; //简化,总是将T看作字符串

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>字(第一,最后);
copy(words.begin(),words.end(),os);
}

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

程序从 istream cin ),并对它们执行操作。默认情况下,每个单词由空格分隔。格式化提取背后的逻辑在 istream_iterator 中。



我现在需要做的是传递到 do_something()两个迭代器,以便提取的单词将以标点符号而不是空格分隔(空格将被视为正常字符)。你将如何以干净的C ++方式(即以最小的努力)来做?

解决方案

显然,有一种相对简单的方法来改变流被认为是空格的情况。这样做的方式是 imbue()具有 std :: locale 对象的流,其 std :: ctype< char> facet被替换以将所需字符视为空格。 imbue() locale ctype - huh? ? OK,好吧,这些不一定是你每天使用的东西,所以这里是一个快速示例,设置 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> code>实际上可以使用一个或两个任意的 char 作为空格(一个适当的C ++ 2011版本可能允许任意数量的参数; ,真的不必是模板参数)。无论如何,只要在你的 main()函数的开头放下一行,你就全部设置了:

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

注意,这真的只考虑 \\\
作为空格字符。这也意味着没有其他字符作为空格被跳过。 ...当然,多个逗号字符的序列被认为只是一个分隔符,而不是可能创建一堆空字符串。另请注意,上述 std :: ctype< char> 构面会删除所有其他字符分类。如果你想解析其他对象而不仅仅是字符串,你可能想要保留另一个字符分类,只改变空格。这是可以做到的一种方式:

 模板< 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) :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版本崩溃了(显然是<$ c




$ b $ p $ c> std :: ctype< char> :: classic_table()
产生一个空指针,用clang的当前版本编译这个不起作用,因为clang不支持lambda。上面的代码应该是正确的,虽然...


The problem below has been simplified from real requirements.

Consider the following program:

#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;
}

The program extracts all the words from an istream (cin) and does something with them. Each word is seperated by a white space by default. The logic behind the formatted extraction is inside the istream_iterator.

What I need to do now is to pass to do_something() two iterators so that the extracted words will be separated by a punctuation character instead of a white space (white spaces will be considered as "normal" characters). How would you do that in a "clean C++ way" (that is, with the minimum effort)?

解决方案

Although it isn't a priori obvious there is a relatively simple way to change what a stream considers to be whitespace. The way to do it is to imbue() the stream with a std::locale object whose std::ctype<char> facet is replaced to consider the desired characters as whitespace. imbue(), locale, ctype - huh?!? OK, well, these aren't necessarily the things you use day to day so here is a quick example which set's up std::cin to use comma and newline characters as spaced:

#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) {}
};

Actually, this particular implementation of std::ctype<char> can actually be used to use one or two arbitrary chars as spaces (a proper C++2011 version would probably allow an arbitrary number of arguments; also, the don't really have to be template argumentss). Anyway, with this in place, just drop the following line at the beginning of your main() function and you are all set:

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

Note that this really only considers , and \n as space characters. This also means that no other characters are skipped as whitespace. ... and, of course, a sequence of multiple comma characters is considered to be just one separator rather than possibly creating a bunch of empty strings. Also note that the above std::ctype<char> facet removes all other character classification. If you want to parse other objects than just strings you might want to retain the other character classification and only change that for spaces. Here is a way this could be done:

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];
};

Sadly, this crashes with the version of gcc I have on my system (apparently the std::ctype<char>::classic_table() yields a null pointer. Compiling this with a current version of clang doesn't work because clang doesn't support lambda. With the two caveats the above code should be correct, though...

这篇关于从istream读取格式化的输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆