天天看點

stl容器使用中的經驗(八)對于逐個字元的輸入請考慮使用 istreambuf_iterator

先來看個例子:

ifstream inputFile("ints.dat");
list<int> data(istream_iterator<int>(inputFile), istream_iterator<int>());
           

上面的這兩行代碼,貌似是将兩個 isteam_iterator 疊代器傳給list容器的構造函數,進而将檔案中的資料拷貝到容器中。

但是,結果真的如我們所料嗎?首先我們看下下面的代碼。

int f(double d);
int f(double (d));
int f(double);
           

這三行代碼效果是相同的,都是定義了一個函數,函數的傳回值的int類型,參數是double類型。

下面的這行代碼也是定義了一個函數,函數 g 以指向函數的指針為參數

(*pf)()

是一個指向一個不帶任何參數的函數的指針,傳回值為double類型,傳回值做了 函數 g 的參數。

那麼下面的這個函數申明和上面表達了同樣的含義。

如果我們将參數名稱省略。

那麼這個函數和上面的

int f(double (d));

有什麼差別呢?

我們可以看到,函數 f 的括号是在實參的兩邊,括号存在和不存在效果是一樣的,但是函數 g 則不同,存在一個空的括号,根據推導,這個空的括号是有具體意義的,是存在一個函數指針參數的。

接下來,我們看下最開始的問題。

ifstream inputFile("ints.dat");
list<int> data(istream_iterator<int>(inputFile), istream_iterator<int>());
           

那麼我們就能夠很明顯的看出,第二行代碼是申明了一個名稱為

data

的函數。函數傳回值類型為

list<int>

,函數有兩個參數。

  1. 第一個參數名稱為 inputFile,類型為

    istream_iterator<int>

  2. 第二個參數沒有名稱。類型是指向不帶參數的函數的指針,該函數的傳回值是

    istream_iterator<int>

是以,上面的代碼不會達到我們逾期的效果。

就跟下面的例子一樣,是我們經常犯的錯誤,下面第二行代碼并沒有定義一個Widget的對象,而是申明了一個,沒有參數,傳回值類型為Widget,名稱為w的函數。

class Widget{...};

Widget w();
           

按照下面的方式解決上述的問題。讓代碼不要出現二義性。

ifstream inputFile("ints.dat");
istream_iterator<int> begin(inputFile);
istream_iterator<int> end;
list<int> data(begin, end);
           

我們再來看下下面的例子:

#include<iostream>
#include <fstream>
#include <ctime>
#include <iterator>
#include <cassert>

using namespace std;

int main()
{
	ifstream inputData("intdata.dat");
	assert(inputData.is_open());
	
   inputData.unsetf(ios::skipws);
 
	time_t t1 = time(NULL);
	string data((istream_iterator<char>(inputData)),  istream_iterator<char>());
	time_t t2 = time(NULL);

	cout << t1 << " " << t2 << endl; //1634635857 1634635866
	
	time_t t3 = time(NULL);
	string data2((istreambuf_iterator<char>(inputData)),  istreambuf_iterator<char>());
	time_t t4 = time(NULL);
	
	cout << t3 << " " << t4 << endl;   //1634635923 1634635923

	return 0;
}
           

上面的例子有兩種從檔案讀取字元并建構了一個

string

的對象,前一種使用了

istream_iterator

後一種使用的是

istreambuf_iterator

,兩者的差別也是顯而易見的。

  1. istream_iterator

    istreambuf_iterator

    耗時更久
  2. istream_iterator

    預設是忽略字元中的空格的
  3. 需要設定

    inputData.unsetf(ios::skipws);

是以,如果需要從輸入流中逐個讀取字元,

istreambuf_iterator

是個不錯的選擇,就不必要進行格式化輸入,因為

istream_iterator

在調用其

operator <<

時其實是進行了格式化的輸入。但

istreambuf_iterator

從一個輸入流中

stream s

讀取下一個字元是通過

s.rdbuf()->sgetc()

完成的。 如果追求輸入的效率,那麼選擇

istreambuf_iterator

同樣的,輸出的時候也可以選擇

ostreambuf_iterator

繼續閱讀