之前總結了C++的檔案輸出輸入流的相關知識,通過介紹底層的streambuf緩衝區,從而與stringstream流(字串流)聯絡了起來,本文就對此進行簡單的介紹。

首先介紹string。 string 是 C++ 提供的字串類,和 C 型別的字串相比,除了有不限長度的優點外,還有其他許多方便的功能,其可以看成類似STL裡vector陣列的一種容器,可以方便的進行資料的增刪改查,並可以進行排序、交換與遍歷。要使用 string, 必須包含標頭檔案string,幷包含std名稱空間:

#include <string>
using namespace std;

宣告一個字串變數為(本質上也可以理解為class string的一個物件,包含許多的成員函式):

string str;
//可以在宣告時進行初始化
string str = "Hello";

要輸出str的單個字元,可以與傳統C的字串一樣採用下標運算str[i]的形式,也可以採用成員函式(str.at(i))的形式,不同的是[]運算不會檢查索引i是否越界,而at()函式會檢查,使用無效時會丟擲out_of_range異常。

同時,string型別的字串是不以‘\0’結尾的,因此若str有三個字元,傳統C語言的字串的str[3]是字元‘\0’,但是string型別的只能到str[2],str[3]是沒有定義的,而str. at(3)會提示越界奔潰。

C++ string型別字串與C型別的字串的簡單對比如下:

功能 C++ string C字元陣列
定義字串 string str; char str[100];
單個字元輸出 str[i] / str.at(i) str[i]
字串長度 str.length() / str.size() strlen(str)
讀取一行 getline(cin,str) gets(str)
賦值 str = “Hello”; strcpy(str,“Hello”);
連線字串 str = str + “Hello” strcat(str,“Hello”);
比較字串 str == “Hello”; strcmp(str,“Hello”);

要使用strlen()、strcpy()函式需要包含C語言的字串操作函式標頭檔案:

#include <string.h>
using namespace std;
//上述兩行程式碼等價於下面一行程式碼
#include <cstring>

C++字串與C字串

C++ string類提供了c_str()、data()和copy()這三個成員函式用於將C++字串string轉換為C字串C_string,其

函式 功能
c_str() 返回一個以‘/0’結尾的字元陣列
data() 以字元陣列的形式返回字串內容,但並不新增’/0’
copy() 字串的內容複製或寫入既有的c_string或字元陣列內
string str = "Hello World!";
const char* p1 = str.c_str();
const char* p2 = str.data();
const char* p3=new char[10];
str.copy(p3,5,0);
//函式原型:copy(char *s, int n, int pos = 0)
//把當前串中以pos開始的n個字元拷貝到以s為起始位置的字元陣列中,返回實際拷貝的數目 

string還可以方便的改變字串的容量大小,通過呼叫成員函式resize()可以重設string的容量。

string str="Hello";
str.resize(3);

string可以很方便的查詢字串中的字元或者子串,其是通過成員函式find()和substr()實現的, find()函式是從str第3個位置查起,找到子串後,返回子串的位置;而substr函式從pos位置(子串開始的位置)開始,擷取5個字元,賦值給str2,也就是說,str2的內容將是ssdfs。

string str = "aaaaddddssdfsasdf";  
size_t pos = str.find("ssdf", 3);//注意pos的資料型別string::size_type
//如果沒找到,返回一個特殊的標誌npos
// 可以用if(pos != string::npos)則表示找到。
string str2 = str.substr(pos, 5);

二、stringstream

stringstream是 C++ 提供的一個字串流(stream),和之前學過的iostream、fstream有類似的操作方式,要使用stringstream,必須包含其標頭檔案:

#include <sstream>
using namespace std;
stringstream ss;

< sstream > 庫定義了三種類:istringstream、ostringstream和stringstream,分別用來進行流的輸入、輸出和輸入輸出操作。另外,每個類都有一個對應的寬字符集版本。一般情況下使用stringstream就足夠,因為字串要頻繁的涉及到輸入輸出。

< sstream > 使用string物件來代替字元陣列,這樣可以避免緩衝區溢位的危險。而且,傳入引數和目標物件的型別被自動推匯出來,即便使用了不正確的格式化符也沒有危險。

與檔案流fstream類似,通過插入器(<<)和析取器(>>)這兩個運算子可以直接對stringstream上的資料輸入輸出,而將stringstream中的全部資料輸出則是使用成員函式str(),其有兩種形式: 1、void str() //無參形式,用於將stringstream流中的資料以string字串的形式輸出 2、void str (const string& s)//以字串為引數,用以覆蓋stringstream流中的資料 特別需要注意的是:

// 字串流清零,將流中的資料全部清除
ss.str(""); 

示例程式碼:

#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
ss << "hello ";
ss << "world!";

std::cout << ss.str() << std::endl;
// 對stringstream而言,operator<< 是一直往字串流中寫字元
// 而不是覆蓋之前輸入的字元,這一點不同於str()成員函式方法,str()函式建立的是一個臨時的string物件
//這個string物件在函式str()語句結束就會被銷燬,因而一般使用時應先宣告一個string物件s,將str()賦值給s
//const string s=ss.str();//這樣會有一個string拷貝的過程
/*又或者是const string& s=ss.str();這樣就是用s直接引用了ss.str()這個臨時變數,就“延長了臨時變數ss.str()的
生命週期”,使得ss.str()生命結束時刻和s一樣*/
return 0;
}

ss.clear()成員函式

同文件流fstream中的clear()函式類似,通過clear()成員函式可以清除流的錯誤狀態,主要用在stringstream重複使用時或者多個stringstream物件構造時清空,不然之前的緩衝就停留在輸入輸出流中。

ss.setstate(std::ios::eofbit);//設定流的狀態標誌位
std::cout << ss.rdstate() << std::endl;//獲取當前流的狀態標誌位
// 結果為1
ss.clear();
std::cout << ss.rdstate() << std::endl;
// 結果為0

在對同一個stringstream物件重複賦值,就需要先對流使用clear()函式清空流的狀態,此時流佔用的記憶體沒有改變,會一直增加(stringstream不主動釋放記憶體),若想改變記憶體(一般是清除記憶體,減少記憶體消耗),需要再配合使用str("")清空stringstream的快取。

stringstream stream;
    int a,b;
    ss<<"80";//向流輸出資料(寫入)
    ss>>a;//從流輸入資料到a
    cout<<"Size of ss = "<<ss.str().length()<<endl;//ss.str()返回一個string物件,再呼叫其成員函式length()
    ss.clear();//清空流
    ss.str("");//清空流快取
    cout<<"Size of ss = "<<ss.str().length()<<endl;
    ss<<"90";//重新賦值
    ss>>b;
    cout<<"Size of ss = "<<ss.str().length()<<endl;  

執行結果:

Size of ss = 2
Size of ss = 0
Size of ss = 2
80
90

stringstream與fstream

通過過載的<<和>>運算子可以將檔案流中的資料輸出到C++字串中,它們之間的媒介是緩衝區streambuf,可由流的成員函式rdbuf()讀取。

#include <iostream>
#include <sstream>//stringstream流的標頭檔案
#include <fstream>
using namespace std;
int main()
{
        string str;
        ifstream in;
        in.open("Hello.txt");
        //讀取檔案的緩衝內容到資料流中
        stringstream ss;
        ss << in.rdbuf();
        in.close();//關閉檔案
        str = ss.str();//將stringstream流中的資料賦值給string型別字串
        const char* p = str.c_str();//將字串內容轉化為C_string型別
        return 0;
}

stringstream通常是用來做資料轉換的,用於字串與其他變數型別的轉換,相比c庫的轉換,它更加安全,自動和直接。

#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
    string str1("How are you? 123 1 4.368");
    stringstream ss(str1);//建構函式初始化
    cout<<ss.str1()<<endl;
    string str2;
    for(int i=0;i<3;i++)
    {
    	ss>>str2;
    	cout<<str2<<" ";
	}
	int a;
	ss>>a;
	cout<<a<<endl;
	bool b;
	ss>>b;
	cout<<b<<endl;
	float c;
	ss>>c;
	cout<<c<<endl;	
	ss.clear();
	ss.str1("I am fine!");//重新賦值
	while(ss>>str2)//不斷讀取
	{
		cout<<str2<<" ";
	}		
}

執行結果:

How are you? 123 1 4.368
How are you?
123
1
4.368

由上面的程式碼可知,從stringstream流中的資料輸入字串到一個變數裡,是以遇到空格跳到下一個字串的這樣的形式連續讀取的。