iostream使用技巧

流操作
  1. 简单常识——关于 stream
  2. 一次把整个文件读入一个 string
  3. open 一个文件的那些选项
  4. 用我想要的分隔符来解析一个字符串,以及从流中读取数
  5. 把原本输出到屏幕的东西输出到文件,不用到处去把 cout 改成 fs
  6. 关于 istream_iterator 和 ostream_iterator
  7. 其它算法配合 iterator
  8. Manipulator

简单常识——关于 stream
从文件中读入一行

简单,这样就行了:

ifstream ifs("input.txt");
char buf[1000];

ifs.getline(buf, sizeof buf);

string input(buf);

当然,这样没有错,但是包含不必要的繁琐和拷贝,况且,如果一行超过1000个字符,就必须用一个循环和更麻烦的缓冲管理。下面这样岂不是更简单?

string input;
input.reserve(1000);
ifstream ifs("input.txt");
getline(ifs, input);

不仅简单,而且安全,因为全局函数 getline 会帮你处理缓冲区用完之类的麻烦,如果你不希望空间分配发生的太频繁,只需要多 reserve 一点空间。

这就是“简单常识”的含义,很多东西已经在那里,只是我一直没去用。

---------------------------------------------------------------------------

一次把整个文件读入一个 string

我希望你的答案不要是这样:

string input;
while( !ifs.eof() )
{
    string line;
    getline(ifs, line);
    input.append(line).append(1, ' ');
}

当然了,没有错,它能工作,但是下面的办法是不是更加符合 C++ 的精神呢?

string input(
    istreambuf_iterator<char>(instream.rdbuf()),
    istreambuf_iterator<char>()
);

同样,事先分配空间对于性能可能有潜在的好处:

string input;
input.reserve(10000);
input.assign(
    istreambuf_iterator<char>(ifs.rdbuf()),
    istreambuf_iterator<char>()
);

很简单,不是么?但是这些却是我们经常忽略的事实。
补充一下,这样干是有问题的:

    string input;
    input.assign(
        istream_iterator<char>(ifs),
        istream_iterator<char>()
    );

因为它会忽略所有的分隔符,你会得到一个纯“字符”的字符串。最后,如果你只是想把一个文件的内容读到另一个流,那没有比这更快的了:

    fstream fs("temp.txt");
    cout << fs.rdbuf();

因此,如果你要手工 copy 文件,这是最好的(如果不用操作系统的 API):

   ifstream ifs("in.txt");
   ofstream ofs("out.txt");
   ofs << in.rdbuf();

-------------------------------------------------------------------------

open 一个文件的那些选项

ios::in     Open file for reading
ios::out    Open file for writing
ios::ate    Initial position: end of file
ios::app    Every output is appended at the end of file
ios::trunc  If the file already existed it is erased
ios::binary Binary mode

-------------------------------------------------------------------------

还有 ios 的那些 flag

flag effect if set
ios_base::boolalpha input/output bool objects as alphabetic names (true, false).
ios_base::dec input/output integer in decimal base format.
ios_base::fixed output floating point values in fixed-point notation.
ios_base::hex input/output integer in hexadecimal base format.
ios_base::internal the output is filled at an internal point enlarging the output up to the field width.
ios_base::left the output is filled at the end enlarging the output up to the field width.
ios_base::oct input/output integer in octal base format.
ios_base::right the output is filled at the beginning enlarging the output up to the field width.
ios_base::scientific output floating-point values in scientific notation.
ios_base::showbase output integer values preceded by the numeric base.
ios_base::showpoint output floating-point values including always the decimal point.
ios_base::showpos output non-negative numeric preceded by a plus sign (+).
ios_base::skipws skip leading whitespaces on certain input operations.
ios_base::unitbuf flush output after each inserting operation.
ios_base::uppercase output uppercase letters replacing certain lowercase letters.

There are also defined three other constants that can be used as masks:

constant value
ios_base::adjustfield left | right | internal
ios_base::basefield dec | oct | hex
ios_base::floatfield scientific | fixed

--------------------------------------------------------------------------

用我想要的分隔符来解析一个字符串,以及从流中读取数据

这曾经是一个需要不少麻烦的话题,由于其常用而显得尤其麻烦,但是其实 getline 可以做得不错:

    getline(cin, s, ';');   
    while ( s != "quit" )
    {
        cout << s << endl;
        getline(cin, s, ';');
    }

简单吧?不过注意,由于这个时候 getline 只把 ; 作为分隔符,所以你需要用 ;quit; 来结束输入,否则 getline 会把前后的空格和回车都读入 s ,当然,这个问题可以在代码里面解决。

同样,对于简单的字符串解析,我们是不大需要动用什么 Tokenizer 之类的东西了:

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main()
{
    string s("hello,world, this is a sentence; and a word, end.");
    stringstream ss(s);
   
    for ( ; ; )
    {
        string token;
        getline(ss, token, ',');
        if ( ss.fail() ) break;
       
        cout << token << endl;
    }
}

输出:

hello
world
 this is a sentence; and a word
 end.

很漂亮不是么?不过这么干的缺陷在于,只有一个字符可以作为分隔符。

--------------------------------------------------------------------------

把原本输出到屏幕的东西输出到文件,不用到处去把 cout 改成 fs

#include <iostream>
#include <fstream>
using namespace std;
int main()
{    
    ofstream outf("out.txt"); 
    streambuf *strm_buf=cout.rdbuf();    
    cout.rdbuf(outf.rdbuf()); 
    cout<<"write something to file"<<endl; 
    cout.rdbuf(strm_buf);   //recover 
    cout<<"display something on screen"<<endl;
    system("PAUSE");
    return 0;
}
 

输出到屏幕的是:

display something on screen

输出到文件的是:

write something to file

也就是说,只要改变 ostream 的 rdbuf ,就可以重定向了,但是这招对 fstream 和 stringstream 都没用。

--------------------------------------------------------------------------

关于 istream_iterator 和 ostream_iterator

经典的 ostream_iterator 例子,就是用 copy 来输出:

#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <vector>
#include <iterator>

using namespace std;

int main()
{  
    vector<int> vect;
    for ( int i = 1; i <= 9; ++i )
        vect.push_back(i);
       
    copy(vect.begin(), vect.end(),
        ostream_iterator<int>(cout, " ")
    );
    cout << endl;
   
    ostream_iterator<double> os_iter(cout, " ~ ");
    *os_iter = 1.0;
    os_iter++;
    *os_iter = 2.0;
    *os_iter = 3.0;
}

输出:

1 2 3 4 5 6 7 8 9
1 ~ 2 ~ 3 ~

很明显,ostream_iterator 的作用就是允许对 stream 做 iterator 的操作,从而让算法可以施加于 stream 之上,这也是 STL 的精华。与前面的“读取文件”相结合,我们得到了显示一个文件最方便的办法:

    copy(istreambuf_iterator<char>(ifs.rdbuf()),
         istreambuf_iterator<char>(),
         ostreambuf_iterator<char>(cout)
    );

同样,如果你用下面的语句,得到的会是没有分隔符的输出:

    copy(istream_iterator<char>(ifs),
         istream_iterator<char>(),
         ostream_iterator<char>(cout)
    );

那多半不是你要的结果。如果你硬是想用 istream_iterator 而不是 istreambuf_iterator 呢?还是有办法:

    copy(istream_iterator<char>(ifs >> noskipws),
         istream_iterator<char>(),
         ostream_iterator<char>(cout)
    );

但是这样不是推荐方法,它的效率比第一种低不少。
如果一个文件 temp.txt 的内容是下面这样,那么我的这个从文件中把数据读入 vector 的方法应该会让你印象深刻。

12345 234 567
89    10

程序:

#include <iostream>
#include <fstream>
#include <algorithm>
#include <vector>
#include <iterator>

using namespace std;

int main()
{  
    ifstream ifs("temp.txt");
   
    vector<int> vect;
    vect.assign(istream_iterator<int>(ifs),
        istream_iterator<int>()
    );


    copy(vect.begin(), vect.end(), ostream_iterator<int>(cout, " "));
}

输出:

12345 234 567 89 10

很酷不是么?判断文件结束、移动文件指针之类的苦工都有 istream_iterator 代劳了。

-----------------------------------------------------------------------

其它算法配合 iterator

计算文件行数:

    int line_count =
        count(istreambuf_iterator<char>(ifs.rdbuf()),
              istreambuf_iterator<char>(),
              ' ');       

当然确切地说,这是在计算文件中回车符的数量,同理,你也可以计算文件中任何字符的数量,或者某个 token 的数量:

    int token_count =
        count(istream_iterator<string>(ifs),
              istream_iterator<string>(),
              "#include");       

注意上面计算的是 “#include” 作为一个 token 的数量,如果它和其他的字符连起来,是不算数的。

------------------------------------------------------------------------
Manipulator

Manipulator 是什么?简单的说,就是一个接受一个 stream 作为参数,并且返回一个 stream 的函数,比如上面的 unskipws ,它的定义是这样的:

  inline ios_base&
  noskipws(ios_base& __base)
  {
    __base.unsetf(ios_base::skipws);
    return __base;
  }

这里它用了更通用的 ios_base 。知道了这一点,你大概不会对自己写一个 manipulator 有什么恐惧感了,下面这个无聊的 manipulator 会忽略 stream 遇到第一个分号之前所有的输入(包括那个分号):

template <class charT, class traits>
inline std::basic_istream<charT, traits>&
ignoreToSemicolon (std::basic_istream<charT, traits>& s)
{
    s.ignore(std::numeric_limits<int>::max(), s.widen(';'));
    return s;
}

不过注意,它不会忽略以后的分号,因为 ignore 只执行了一次。更通用一点,manipulator 也可以接受参数的,下面这个就是 ignoreToSemicolon 的通用版本,它接受一个参数, stream 会忽略遇到第一个该参数之前的所有输入,写起来稍微麻烦一点:

struct IgnoreTo {
    char ignoreTo;
    IgnoreTo(char c) : ignoreTo(c)
    {}
};
   
std::istream& operator >> (std::istream& s, const IgnoreTo& manip)
{
    s.ignore(std::numeric_limits<int>::max(), s.widen(manip.ignoreTo));
    return s;
}

但是用法差不多:

    copy(istream_iterator<char>(ifs >> noskipws >> IgnoreTo(';')),
         istream_iterator<char>(),
         ostream_iterator<char>(cout)
    );

其效果跟 IgnoreToSemicolon 一样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iostream是C++标准库中的一个头文件,它包含了输入输出流(I/O streams)的相关类和函数。这些类和函数用于处理程序中的输入和输出操作。 使用iostream库,你可以创建和管理输入和输出流对象,这些对象可以与各种输入和输出设备(如键盘、屏幕、文件等)进行交互。流对象通常与标准库中的一些函数和对象一起使用,例如cin(用于从标准输入读取数据)和cout(用于向标准输出写入数据)。 以下是使用iostream库的一些基本步骤: 1. 包含iostream头文件:在程序中包含iostream头文件,以便可以使用流类和函数。 ```cpp #include <iostream> ``` 2. 创建流对象:使用std::cout、std::cin等流对象,可以将数据写入标准输出或从标准输入读取数据。 ```cpp std::cout << "Hello, world!" << std::endl; // 写入数据到标准输出 int number; std::cin >> number; // 从标准输入读取数据 ``` 3. 控制流操作:可以使用流控制函数(如std::getline、std::ignore等)来控制流的读取和写入操作。 ```cpp std::string line; std::getline(std::cin, line); // 从标准输入读取一行数据 ``` 4. 处理格式化输出:可以使用流插入运算符(<<)和格式化输出函数(如std::setw、std::setprecision等)来控制输出的格式。 ```cpp double value = 3.14; std::cout << "Value: " << std::fixed << std::setprecision(2) << value << std::endl; // 格式化输出浮点数 ``` 除了标准库中的流类和函数之外,iostream库还提供了其他一些功能,例如文件输入输出、字符串操作等。你可以通过阅读相关文档或参考教程来深入了解更多关于iostream库的使用方法和技巧

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值