1. 程式人生 > >【C++】STL常用容器總結之十二:string類

【C++】STL常用容器總結之十二:string類

13、string類

宣告

string類本不是STL的容器,但是它與STL容器有著很多相似的操作,因此,把string放在這裡一起進行介紹。
之所以拋棄char*的字串而選用C++標準程式庫中的string類,是因為他和前者比較起來,不必擔心記憶體是否足夠、字串長度等等,而且作為一個類出現,他整合的操作函式足以完成我們大多數情況下的需要。我們儘可以把它看成是C++的基本資料型別。
首先,為了在我們的程式中使用string型別,我們必須包含標頭檔案。如下:

#include  <string>  // 注意這裡不是string.h,string.h是C字串標頭檔案

1、宣告一個C++字串

宣告一個字串變數很簡單: string str;
這樣我們就聲明瞭一個字串變數,但既然是一個類,就有建構函式和解構函式。上面的宣告沒有傳入引數,所以就直接使用了string的預設的建構函式,這個函式所作的就是把str初始化為一個空字串。string類的建構函式和解構函式如下:

1)  string s;  // 生成一個空字串s 
2)  string s(str) ; // 拷貝建構函式生成str的複製品 
3)  string s(str, stridx);  // 將字串str內"始於位置stridx"的部分當作字串的初值 
4)  string s(str, stridx, strlen) ; // 將字串str內"始於stridx且長度頂多strlen"的部分作為字串的初值 
5) string s(cstr) ; // 將C字串(以NULL結束)作為s的初值 6) string s(chars, chars_len) ; // 將C字串前chars_len個字元作為字串s的初值。 7) string s(num, ‘c’) ; // 生成一個字串,包含num個c字元 8) string s(“value”); string s=“value”; // 將s初始化為一個字串字面值副本 9) string s(begin, end); // 以區間begin/end(不包含end)內的字元作為字串s的初值 10) s.~string(); //銷燬所有字元,釋放記憶體

2、string與C字元陣列的比較

string串要取得其中某一個字元,和傳統的C字串一樣,可以用s[i]的方式取得。比較不一樣的是如果s有三個字元,傳統C的字串的s[3]是’\0’字元,但是C++的string則是隻到s[2]這個字元而已。

1、C風格字串

  • 用”“括起來的字串常量,C++中的字串常量由編譯器在末尾新增一個空字元;
  • 末尾添加了‘\0’的字元陣列,C風格字串的末尾必須有一個’\0’。

2、C字元陣列及其與string串的區別

  • char ch[ ]={‘C’, ‘+’, ‘+’}; //末尾無NULL
  • char ch[ ]={‘C’, ‘+’, ‘+’, ‘\0’}; //末尾顯式新增NULL
  • char ch[ ]=”C++”; //末尾自動新增NULL字元 若[ ]內數字大於實際字元數,將實際字元存入陣列,其餘位置全部為’\0’。
    這裡寫圖片描述

3、string物件的操作

string s;
1)  s.empty();  // s為空串 返回true
2)  s.size();  // 返回s中字元個數 型別應為:string::size_type
3)  s[n];  //0開始相當於下標訪問
4)  s1+s2;  // 把s1和s2連線成新串 返回新串 
5)  s1=s2;  // 把s1替換為s2的副本
6)  v1==v2;  // 比較,相等返回true
7)  `!=, <, <=, >, >=`  慣有操作 任何一個大寫字母都小於任意的小寫字母

當進行string物件和字串字面值混合連線操作時,+操作符的左右運算元必須至少有一個是string型別的:

string s1(“hello”);
string s3=s1+”world”;  //合法操作
string s4=”hello”+”world”;  //非法操作:兩個字串字面值相加

4、字串操作函式

1、string類函式

1) =, s.assign() // 賦以新值
2) swap() // 交換兩個字串的內容
3) +=, s.append(), s.push_back() // 在尾部新增字元
4) s.insert() // 插入字元
5) s.erase() // 刪除字元
6) s.clear() // 刪除全部字元
7) s.replace() // 替換字元
8) + // 串聯字串
9) ==,!=,<,<=,>,>=,compare() // 比較字串
10) size(),length() // 返回字元數量
11) max_size() // 返回字元的可能最大個數
12) s.empty() // 判斷字串是否為空
13) s.capacity() // 返回重新分配之前的字元容量
14) reserve() // 保留一定量記憶體以容納一定數量的字元
15) [ ], at() // 存取單一字元
16) >>,getline() // 從stream讀取某值
17) << // 將謀值寫入stream
18) copy() // 將某值賦值為一個C_string
19) c_str() // 返回一個指向正規C字串(C_string)的指標 內容與本string串相同 有’\0’
20) data() // 將內容以字元陣列形式返回 無’\0’
21) s.substr() // 返回某個子字串
22) begin() end() // 提供類似STL的迭代器支援
23) rbegin() rend() // 逆向迭代器
24) get_allocator() // 返回配置器

2、函式說明

1、s.assign();

s.assign(str); // 不說 
s.assign(str,1,3); // 如果str是"iamangel" 就是把"ama"賦給字串 
s.assign(str,2,string::npos); // 把字串str從索引值2開始到結尾賦給s 
s.assign("gaint"); // 不說 
s.assign("nico",5); // 把’n’ ‘I’ ‘c’ ‘o’ ‘\0’賦給字串 
s.assign(5,'x'); // 把五個x賦給字串

2、大小和容量函式

一個C++字串存在三種大小:
1) 現有的字元數,函式是s.size()和s.length(),他們等效。s.empty()用來檢查字串是否為空。
2) max_size(); 這個大小是指當前C++字串最多能包含的字元數,很可能和機器本身的限制或者字串所在位置連續記憶體的大小有關係。
3) capacity()重新分配記憶體之前string所能包含的最大字元數。
這裡另一個需要指出的是reserve()函式,這個函式為string重新分配記憶體。重新分配的大小由其引數決定,預設引數為0,這時候會對string進行非強制性縮減。

3、元素存取

我們可以使用下標操作符[]和函式at()對元素包含的字元進行訪問。但是應該注意的是操作符[]並不檢查索引是否有效(有效索引0~str.length()),如果索引失效,會引起未定義的行為。而at()會檢查,如果使用at()的時候索引無效,會丟擲out_of_range異常。
有一個例外不得不說,const string a;的操作符[]對索引值是a.length()仍然有效,其返回值是’\0’。其他的各種情況,a.length()索引都是無效的。

4、比較函式

C ++字串支援常見的比較操作符(>,>=,<,<=,==,!=),甚至支援string與C-string的比較(如 str<”hello”)。在使用>,>=,<,<=這些操作符的時候是根據”當前字元特性”將字元按字典順序進行逐一的比較。字典排序靠前的字元小,比較的順序是從前向後比較,遇到不相等的字元就按這個位置上的兩個字元的比較結果確定兩個字串的大小。
另一個功能強大的比較函式是成員函式compare()。他支援多引數處理,支援用索引值和長度定位子串來進行比較。他返回一個整數來表示比較結果,返回值意義如下:0-相等 、>0-大於、<0-小於。

5、插入字元

也許你需要在string中間的某個位置插入字串,這時候你可以用insert()函式,這個函式需要你指定一個安插位置的索引,被插入的字串將放在這個索引的後面。
s.insert(0,”my name”);
s.insert(1,str);
這種形式的insert()函式不支援傳入單個字元,這時的單個字元必須寫成字串形式。為了插入單個字元,insert()函式提供了兩個對插入單個字元操作的過載函式:
insert(size_type index, size_type num, chart c)和insert(iterator pos, size_type num, chart c)。
其中size_type是無符號整數,iterator是char*,所以,你這麼呼叫insert函式是不行的:
insert(0, 1, ‘j’);這時候第一個引數將轉換成哪一個呢?
所以你必須這麼寫:insert((string::size_type)0, 1, ‘j’)!
第二種形式指出了使用迭代器安插字元的形式。

6、提取子串s.substr()

s.substr(); // 返回s的全部內容 
s.substr(11); // 從索引11往後的子串 
s.substr(5,6); // 從索引5開始6個字元

5、字串流stringstream操作

Iostream標準庫支援記憶體中的輸入輸出,只要將流與儲存在程式記憶體中的string物件捆綁起來即可。此時,可使用iostream輸入和輸出操作符讀寫這個stream物件。使用stringstream,我們必須包含標頭檔案#include。

1、string s

1)  >>操作符 // 用於從istream物件中讀入輸入
2)  is >> s;  // 從輸入流is中讀取一個以空白字元分割的字串,寫入s
3)  <<操作符 // 用於把輸出寫到ostream物件中
4)  os << s; // 將s寫到輸出流os中
5)  getline(is, s);  // 從輸入流is中讀取一行字元,寫入s,直到遇到分行符或到了檔案尾
6)  istream // 輸入流 提供輸入操作
7)  ostream // 輸出流 提供輸出操作

2、stringstream特定的操作

1)  stringstream strm; // 建立自由的stringstream物件
2)  stringstream strm(s); // 建立儲存s的副本的stringstream物件,s是stringstream型別
3)  strm.str(); // 返回strm中儲存的string型別物件
4)  strm.str(s); // 將string型別的s複製給strm 返回void

3、string到int的轉換

stringstream通常是用來做資料轉換的,如果你打算在多次轉換中使用同一個stringstream物件,記住在每次轉換前要使用clear()方法。在多次轉換中重複使用同一個stringstream(而不是每次都建立一個新的物件)物件最大的好處在於效率。stringstream物件的構造和解構函式通常是非常耗費CPU時間的。
string到int的轉換(與其他型別間的轉換一樣大同小異):

string result=”10000”;  
int n=0;
stream<<result;
stream>>n;  // n等於10000

6、C字串、string串、stringstream之間的關係

首先必須瞭解,string可以被看成是以字元為元素的一種容器。字元構成序列(字串)。有時候在字元序列中進行遍歷,標準的string類提供了STL容器介面。具有一些成員函式比如begin()、end(),迭代器可以根據他們進行定位。注意,與char*不同的是,string不一定以NULL(‘\0’)結束。string長度可以根據length()得到,string可以根據下標訪問。所以,不能將string直接賦值給char*。

1、string轉換成const char *

如果要將字面值string直接轉換成const char *型別。string有2個函式可以運用:一個是.c_str(),一個是data成員函式。
c_str()函式返回一個指向正規C字串的指標,內容與本string串相同。這是為了與C語言相容,在C語言中沒有string型別,故必須通過string類物件的成員函式c_str()把string 物件轉換成C中的字串樣式。注意:一定要使用strcpy()函式等來操作方法c_str()返回的指標

string str = "Hello World";
const char *ch1 = str.c_str();
const char *ch2 = str.data();

此時,ch1與ch2的內容將都是”Hello World”。但是隻能轉換成const char*,如果去掉const編譯不能通過。

2、string轉換成char *

C++提供的由C++字串得到對應的C_string的方法是使用data()、c_str()和copy(),其中
1) data()以字元陣列的形式返回字串內容,但並不新增’\0’。
2) c_str()返回一個以’\0’結尾的字元陣列,返回值是const char*。
3) copy()則把字串的內容複製或寫入既有的c_string或字元陣列內。
C++字串並不以’\0’結尾。我的建議是在程式中能使用C++字串就使用,除非萬不得已不選用c_string。
如果要轉換成char*,可以用string的一個成員函式strcpy實現。

string str = "Hello World";
int len = str.length();
char *data = new char[len+1];  //這裡+1還是不+1需要注意
strcpy(data, str.c_str());  // const char *data = new char[len+1];  strcpy(data, str);

此時,data中的內容為”Hello World”使用c_str()要麼str賦給一個const指標,要麼用strcpy()複製。

3、char *轉換成string

string型別能夠自動將C風格的字串轉換成string物件:

string str; 
const char *pc = "Hello World"; 
str = pc;
printf(“%s\n”, str);  //此處出現錯誤的輸出
cout<<str<<endl;

不過這個是會出現問題的。有一種情況我要說明一下。當我們定義了一個string型別之後,用printf(“%s”,str);輸出是會出問題的。這是因為“%s”要求後面的物件的首地址。但是string不是這樣的一個型別。所以肯定出錯。
用cout輸出是沒有問題的,若一定要printf輸出。那麼可以這樣:

printf("%s",str.c_str());

4、char[ ] 轉換成string

這個與char*的情況相同,也可以直接賦值,但是也會出現上面的問題,需要同樣的處理。
- 字元陣列轉化成string型別:

char ch [] = "ABCDEFG";
string str(ch); //也可string str = ch;

或者

char ch [] = "ABCDEFG";
string str;
str = ch; //在原有基礎上新增可以用str += ch;

5、string轉換成char[ ]

string物件轉換成C風格的字串:

const char *str = s.c_str();

這是因為為了防止字元陣列被程式直接處理c_str()返回了一個指向常量陣列的指標。
由於我們知道string的長度可以根據length()函式得到,又可以根據下標直接訪問,所以用一個迴圈就可以賦值了,這樣的轉換不可以直接賦值。

string str = "Hello World";
int len=str.length();
char ch[255]={};
for( int i=0;i<str.length();i++)
ch[i] = str[i];
ch[len+1] = '\0';
printf("%s\n", ch);
cout<<ch<<endl;

6、stringstream與string間的繫結

stringstream strm;
string s;
strm<<s;  // 將s寫入到strm
strm>>s;  // 從strm讀取串寫入s
strm.str(); // 返回strm中儲存的string型別物件
strm.str(s); // 將string型別的s複製給strm 返回void
char* cstr;  // 將C字元陣列轉換成流
string str(cstr);
stringstream ss(str);