1. 程式人生 > >String型別詳解

String型別詳解

一、定義和初始化string物件

這裡寫圖片描述

直接初始化和拷貝初始化: 
如果使用等號(=)初始化一個變數,實際上執行的是拷貝初始化,編譯器把把等號右側的初始值拷貝到新建立的物件中去。與之相反,如果不等於等號,則執行的是直接初始化。

string s5 = "hiya";         //拷貝初始化  
string s6("hiya");          //直接初始化  
string s7(10, 'c');         //直接初始化,s7的內容是cccccccccc 
  • 1
  • 2
  • 3

、string物件上的操作

這裡寫圖片描述

1. 讀寫string物件:

#include<iostream>
#include <string.h>

using namespace std;

int main()  
{  
    string s;               // 空字串  
    cin >> s;               // 將string物件讀入s,遇到空白停止  
    cout << s << endl;      // 輸出s  
    return 0;  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在執行讀取操作時,string物件會自動忽略開頭的空白(即空格符、換行符、製表符等)並從第一個真正的字元開始讀起,直到遇見下一處空白為止。

如上所述,如果程式的輸入是” Hello World! “(注意開頭和結尾處的空格),則輸出將是”Hello”,輸出結果中沒有任何空格。

2. 使用getline讀取一整行

如果希望能在最終得到的字串中保留輸入時的空白符,這時應該用getline函式代替原來的>>運算子。getline函式的引數是一個輸入流和一個string物件,函式從給定的輸入流中讀入內容,直到遇到換行符為止(注意換行符也被讀進來了),然後把所讀的內容存入到那個string物件中去(注意不存換行符)。getline只要一遇到換行符就結束讀取操作並返回結果,哪怕輸入的一開始就是換行符也是如此。如果輸入真的一開始就是換行符,那麼所得的結果是個空string。

和輸入運算子一樣,getline也會返回它的流引數。因此既然輸入運算子能作為判斷的條件,我們也能用getline的結果作為條件。例如,可以通過改寫之前的程式讓它一次輸出一整行,而不再是每行輸出一個詞了:

#incude<iostream>
#include<string.h>

using namespace std;

int main()  
{  
    string word;  
    while (cin >> word)             // 反覆讀取,直至到達檔案末尾  
        cout << word << endl;   // 逐個輸出單詞,每個單詞後面緊跟一個換行  
    return 0;  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

因為line中不包含換行符,所以我們手動地加上換行操作符。和往常一樣,使用endl結束當前行並重新整理顯示緩衝區。

觸發getline函式返回的那個換行符實際上被丟棄掉了,得到的string物件中並不包含該換行符。

3. string::size_type型別

string類及其他大多數標準庫型別都定義了幾種配套的型別。這些配套型別體現了標準庫型別與機器無關的特性,型別size_type即是其中的一種。在具體使用的時候,通過作用域操作符來表明名字size_type是在類string中定義的。儘管我們不太清楚string::size_type型別的細節,但有一點是肯定的:它是一個無符號型別的值而且能足夠存放下任何string物件的大小。所有用於存放string類的size函式返回值的變數,都應該是string::size_type型別的。

4. 比較string物件

string類定義了幾種用於比較字串的運算子。這些比較運算子逐一比較string物件中的字元,並且對大小寫敏感,也就是說,在比較時同一個字母的大寫形式和小寫形式是不同的。

相等性運算子(==和!=)分別檢驗兩個string物件相等或不相等,string物件相等意味著它們的長度相同而且所包含的字元也全都相同。關係運算符<、<=、>、>=分別檢驗一個string物件是否小於、小於等於、大於、大於等於另外一個string物件。上述這些運算子都依照(大小寫敏感的)字典順序:

(1)如果兩個string物件的長度不同,而且較短string物件的每個字元都與較長string物件對應位置上的字元相同,就說較短string物件小於較長string物件。

(2)如果兩個string物件在某些對應的位置上不一致,則string物件比較的結果其實是string物件中第一對相異字元比較的結果。

下面是string物件比較的一個示例:

string st1(10, 'c'), st2;  // st1的內容是 cccccccccc,st2是一個空字串  
st1 = st2;      // 賦值:用st2的副本替換st1的內容,此時st1和st2都是空字串 
  • 1
  • 2

5. 兩個string物件相加

兩個string物件相加得到一個新的string物件,其內容是把左側的運算物件與右側的運算物件串接而成。也就是說,對string物件使用加法運算子(+)的結果是一個新的string物件,它所包含的字元由兩部分組成:前半部分是加號左側string物件所含的字元、後半部分是加號右側string物件所含的字元。另外,複合賦值運算子(+=)負責把右側string物件的內容追加到左側string物件的後面:

string s1 = "hello, ", s2 = "world\n";  
string s3 = s1 + s2;    // s3的內容是hello, world\n  
s1 += s2;               // 等價於s1s1 = s1 + s2
  • 1
  • 2
  • 3

因為標準庫允許把字元字面值和字串字面值轉換成string物件,所以在需要string物件的地方就可以使用這兩種字面值來替代。利用這一點將之前的程式改寫為如下形式:

string s1 = "hello", s2 = "world"; // 在s1和s2中都沒有標點符號  
string s3 = s1 + ", " + s2 + '\n';
  • 1
  • 2

當把string物件和字元字面值及字串字面值混在一條語句中使用時,必須確保每個加法運算子(+)的兩側的運算物件至少有一個是string:

string s4 = s1 + ", ";   // 正確:把一個string物件和一個字面值相加  
string s5 = "hello" + ", ";     // 錯誤:兩個運算物件都不是string  
string s6 = s1 + ", " + "world";  
    // 正確:每個加法運算子都有一個運算物件是string  
string s7 = "hello" + ", " + s2; // 錯誤:不能把字面值直接相加
  • 1
  • 2
  • 3
  • 4
  • 5

6. 處理string物件中的字元

這裡寫圖片描述

如果想對string物件中的每個字元做點兒什麼操作,目前最好的辦法是使用C++11新標準提供的一種語句:範圍for(range for)語句。這種語句遍歷給定序列中的每個元素並對序列中的每個值執行某種操作,其語法形式是:

for (declaration : expression)  
    statement 
  • 1
  • 2

其中,expression部分是一個物件,用於表示一個序列。declaration部分負責定義一個變數,該變數將被用於訪問序列中的基礎元素。每次迭代,declaration部分的變數會被初始化為expression部分的下一個元素值。

一個string物件表示一個字元的序列,因此string物件可以作為範圍for語句中的expression部分。舉一個簡單的例子,我們可以使用範圍for語句把string物件中的字元每行一個輸出出來:

string str("some string");  // 每行輸出str中的一個字元。 
for (auto c : str)          // 對於str中的每個字元  
     cout << c << endl;         // 輸出當前字元,後面緊跟一個換行符 
  • 1
  • 2
  • 3

for迴圈把變數c和str聯絡了起來,其中我們定義迴圈控制變數的方式與定義任意一個普通變數是一樣的。 
此例中,通過使用auto關鍵字讓編譯器來決定變數c的型別,這裡c的型別是char。每次迭代,str的下一個字元被拷貝給c,因此該迴圈可以讀作”對於字串str中的每個字元c,”執行某某操作。此例中的”某某操作”即輸出一個字元,然後換行。

使用範圍for語句改變字串中的字元

如果想要改變string物件中字元的值,必須把迴圈變數定義成引用型別。記住,所謂引用只是給定物件的一個別名,因此當使用引用作為迴圈控制變數時,這個變數實際上被依次繫結到了序列的每個元素上。使用這個引用,我們就能改變它繫結的字元。

新的例子不再是統計標點符號的個數了,假設我們想要把字串改寫為大寫字母的形式。為了做到這一點可以使用標準庫函式toupper,該函式接收一個字元,然後輸出其對應的大寫形式。這樣,為了把整個string物件轉換成大寫,只要對其中的每個字元呼叫toupper函式並將結果再賦給原字元就可以了:

string s("Hello World!!!");  
// 轉換成大寫形式。  
for (auto &c : s)       // 對於s中的每個字元(注意:c是引用)  
    c = toupper(c);   // c是一個引用,因此賦值語句將改變s中字元的值  
cout << s << endl; 
  • 1
  • 2
  • 3
  • 4
  • 5

7.只處理一部分字元

如果要處理string物件中的每一個字元,使用範圍for語句是個好主意。如果需要訪問的只是其中一個字元,或者訪問多個字元但遇到某個條件就要停下來。例如,同樣是將字元改為大寫形式,不過新的要求不再是對整個字串都這樣做,而僅僅把string物件中的第一個字母或第一個單詞大寫化。

要想訪問string物件中的單個字元有兩種方式:一種是使用下標,另外一種是使用迭代器。

下標運算子([ ])接收的輸入引數是string::size_type型別的值,這個引數表示要訪問的字元的位置;返回值是該位置上字元的引用。

string物件的下標從0計起。如果string物件s至少包含兩個字元,則s[0]是第1個字元、s[1]是第2個字元、s[s.size()-1]是最後一個字元。

string物件的下標必須大於等於0而小於s.size()。

一、定義和初始化string物件

這裡寫圖片描述

直接初始化和拷貝初始化: 
如果使用等號(=)初始化一個變數,實際上執行的是拷貝初始化,編譯器把把等號右側的初始值拷貝到新建立的物件中去。與之相反,如果不等於等號,則執行的是直接初始化。

string s5 = "hiya";         //拷貝初始化  
string s6("hiya");          //直接初始化  
string s7(10, 'c');         //直接初始化,s7的內容是cccccccccc 
  • 1
  • 2
  • 3

、string物件上的操作

這裡寫圖片描述

1. 讀寫string物件:

#include<iostream>
#include <string.h>

using namespace std;

int main()  
{  
    string s;               // 空字串  
    cin >> s;               // 將string物件讀入s,遇到空白停止  
    cout << s << endl;      // 輸出s  
    return 0;  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在執行讀取操作時,string物件會自動忽略開頭的空白(即空格符、換行符、製表符等)並從第一個真正的字元開始讀起,直到遇見下一處空白為止。

如上所述,如果程式的輸入是” Hello World! “(注意開頭和結尾處的空格),則輸出將是”Hello”,輸出結果中沒有任何空格。

2. 使用getline讀取一整行

如果希望能在最終得到的字串中保留輸入時的空白符,這時應該用getline函式代替原來的>>運算子。getline函式的引數是一個輸入流和一個string物件,函式從給定的輸入流中讀入內容,直到遇到換行符為止(注意換行符也被讀進來了),然後把所讀的內容存入到那個string物件中去(注意不存換行符)。getline只要一遇到換行符就結束讀取操作並返回結果,哪怕輸入的一開始就是換行符也是如此。如果輸入真的一開始就是換行符,那麼所得的結果是個空string。

和輸入運算子一樣,getline也會返回它的流引數。因此既然輸入運算子能作為判斷的條件,我們也能用getline的結果作為條件。例如,可以通過改寫之前的程式讓它一次輸出一整行,而不再是每行輸出一個詞了:

#incude<iostream>
#include<string.h>

using namespace std;

int main()  
{  
    string word;  
    while (cin >> word)             // 反覆讀取,直至到達檔案末尾  
        cout << word << endl;   // 逐個輸出單詞,每個單詞後面緊跟一個換行  
    return 0;  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

因為line中不包含換行符,所以我們手動地加上換行操作符。和往常一樣,使用endl結束當前行並重新整理顯示緩衝區。

觸發getline函式返回的那個換行符實際上被丟棄掉了,得到的string物件中並不包含該換行符。

3. string::size_type型別

string類及其他大多數標準庫型別都定義了幾種配套的型別。這些配套型別體現了標準庫型別與機器無關的特性,型別size_type即是其中的一種。在具體使用的時候,通過作用域操作符來表明名字size_type是在類string中定義的。儘管我們不太清楚string::size_type型別的細節,但有一點是肯定的:它是一個無符號型別的值而且能足夠存放下任何string物件的大小。所有用於存放string類的size函式返回值的變數,都應該是string::size_type型別的。

4. 比較string物件

string類定義了幾種用於比較字串的運算子。這些比較運算子逐一比較string物件中的字元,並且對大小寫敏感,也就是說,在比較時同一個字母的大寫形式和小寫形式是不同的。

相等性運算子(==和!=)分別檢驗兩個string物件相等或不相等,string物件相等意味著它們的長度相同而且所包含的字元也全都相同。關係運算符<、<=、>、>=分別檢驗一個string物件是否小於、小於等於、大於、大於等於另外一個string物件。上述這些運算子都依照(大小寫敏感的)字典順序:

(1)如果兩個string物件的長度不同,而且較短string物件的每個字元都與較長string物件對應位置上的字元相同,就說較短string物件小於較長string物件。

(2)如果兩個string物件在某些對應的位置上不一致,則string物件比較的結果其實是string物件中第一對相異字元比較的結果。

下面是string物件比較的一個示例:

string st1(10, 'c'), st2;  // st1的內容是 cccccccccc,st2是一個空字串  
st1 = st2;      // 賦值:用st2的副本替換st1的內容,此時st1和st2都是空字串 
  • 1
  • 2

5. 兩個string物件相加

兩個string物件相加得到一個新的string物件,其內容是把左側的運算物件與右側的運算物件串接而成。也就是說,對string物件使用加法運算子(+)的結果是一個新的string物件,它所包含的字元由兩部分組成:前半部分是加號左側string物件所含的字元、後半部分是加號右側string物件所含的字元。另外,複合賦值運算子(+=)負責把右側string物件的內容追加到左側string物件的後面:

string s1 = "hello, ", s2 = "world\n";  
string s3 = s1 + s2;    // s3的內容是hello, world\n  
s1 += s2;               // 等價於s1s1 = s1 + s2
  • 1
  • 2
  • 3

因為標準庫允許把字元字面值和字串字面值轉換成string物件,所以在需要string物件的地方就可以使用這兩種字面值來替代。利用這一點將之前的程式改寫為如下形式:

string s1 = "hello", s2 = "world"; // 在s1和s2中都沒有標點符號  
string s3 = s1 + ", " + s2 + '\n';
  • 1
  • 2

當把string物件和字元字面值及字串字面值混在一條語句中使用時,必須確保每個加法運算子(+)的兩側的運算物件至少有一個是string:

string s4 = s1 + ", ";   // 正確:把一個string物件和一個字面值相加  
string s5 = "hello" + ", ";     // 錯誤:兩個運算物件都不是string  
string s6 = s1 + ", " + "world";  
    // 正確:每個加法運算子都有一個運算物件是string  
string s7 = "hello" + ", " + s2; // 錯誤:不能把字面值直接相加
  • 1
  • 2
  • 3
  • 4
  • 5

6. 處理string物件中的字元

這裡寫圖片描述

如果想對string物件中的每個字元做點兒什麼操作,目前最好的辦法是使用C++11新標準提供的一種語句:範圍for(range for)語句。這種語句遍歷給定序列中的每個元素並對序列中的每個值執行某種操作,其語法形式是:

for (declaration : expression)  
    statement 
  • 1
  • 2

其中,expression部分是一個物件,用於表示一個序列。declaration部分負責定義一個變數,該變數將被用於訪問序列中的基礎元素。每次迭代,declaration部分的變數會被初始化為expression部分的下一個元素值。

一個string物件表示一個字元的序列,因此string物件可以作為範圍for語句中的expression部分。舉一個簡單的例子,我們可以使用範圍for語句把string物件中的字元每行一個輸出出來:

string str("some string");  // 每行輸出str中的一個字元。 
for (auto c : str)          // 對於str中的每個字元  
     cout << c << endl;         // 輸出當前字元,後面緊跟一個換行符 
  • 1
  • 2
  • 3

for迴圈把變數c和str聯絡了起來,其中我們定義迴圈控制變數的方式與定義任意一個普通變數是一樣的。 
此例中,通過使用auto關鍵字讓編譯器來決定變數c的型別,這裡c的型別是char。每次迭代,str的下一個字元被拷貝給c,因此該迴圈可以讀作”對於字串str中的每個字元c,”執行某某操作。此例中的”某某操作”即輸出一個字元,然後換行。

使用範圍for語句改變字串中的字元

如果想要改變string物件中字元的值,必須把迴圈變數定義成引用型別。記住,所謂引用只是給定物件的一個別名,因此當使用引用作為迴圈控制變數時,這個變數實際上被依次繫結到了序列的每個元素上。使用這個引用,我們就能改變它繫結的字元。

新的例子不再是統計標點符號的個數了,假設我們想要把字串改寫為大寫字母的形式。為了做到這一點可以使用標準庫函式toupper,該函式接收一個字元,然後輸出其對應的大寫形式。這樣,為了把整個string物件轉換成大寫,只要對其中的每個字元呼叫toupper函式並將結果再賦給原字元就可以了:

string s("Hello World!!!");  
// 轉換成大寫形式。  
for (auto &c : s)       // 對於s中的每個字元(注意:c是引用)  
    c = toupper(c);   // c是一個引用,因此賦值語句將改變s中字元的值  
cout << s << endl; 
  • 1
  • 2
  • 3
  • 4
  • 5

7.只處理一部分字元

如果要處理string物件中的每一個字元,使用範圍for語句是個好主意。如果需要訪問的只是其中一個字元,或者訪問多個字元但遇到某個條件就要停下來。例如,同樣是將字元改為大寫形式,不過新的要求不再是對整個字串都這樣做,而僅僅把string物件中的第一個字母或第一個單詞大寫化。

要想訪問string物件中的單個字元有兩種方式:一種是使用下標,另外一種是使用迭代器。

下標運算子([ ])接收的輸入引數是string::size_type型別的值,這個引數表示要訪問的字元的位置;返回值是該位置上字元的引用。

string物件的下標從0計起。如果string物件s至少包含兩個字元,則s[0]是第1個字元、s[1]是第2個字元、s[s.size()-1]是最後一個字元。

string物件的下標必須大於等於0而小於s.size()。