## 3.2 字符序列 (Character Sequences)
前面基礎知識部分講C++變量類型的時候,我們已經提到過C++的標準函數庫提供了一個string類來支持對字符串的操作。然而,字符串實際就是一串連續的字符序列,所以我們也可以用簡單的字符數組來表示它。
例如,下面這個數組:
`char jenny [20];`
是一個可以存儲最多20個字符類型數據的數組。你可以把它想象成:

理論上這數組可以存儲長度為20的字符序列,但是它也可以存儲比這短的字符序列,而且實際中常常如此。例如,jenny 在程序的某一點可以只存儲字符串"Hello" 或者"Merry christmas"。因此,既然字符數組經常被用于存儲短于其總長的字符串,就形成了一種習慣在字符串的有效內容的結尾處加一個空字符(null character)來表示字符結束,它的常量表示可寫為0 或'\0'。
我們可以用下圖表示jenny (一個長度為20的字符數組) 存儲字符串"Hello" 和"Merry Christmas" :

注意在有效內容結尾是如何用空字符null character ('\0')來表示字符串結束的。 后面灰色的空格表示不確定數值。
### 初始化以空字符結束的字符序列(Initialization of null-terminated character sequences)
因為字符數組其實就是普通數組,它與數組遵守同樣的規則。例如,如果我們想將數組初始化為指定數值,我們可以像初始化其它數組一樣用:
`char mystring[] = { 'H', 'e', 'l', 'l', 'o', '\0' };`
在這里我們定義了一個有6個元素的字符數組,并將它初始化為字符串Hello 加一個空字符(null character '\0')。
除此之外,字符串還有另一個方法來進行初始化:用字符串常量。
在前幾章的例子中,字符串常量已經出現過多次,它們是由雙引號引起來的一組字符來表示的,例如:
`"the result is: "`
是一個字符串常量,我們在前面的例子中已經使用過。
與表示單個字符常量的單引號(')不同,雙引號 (")是表示一串連續字符的常量。由雙引號引起來的字符串末尾總是會被自動加上一個空字符 ('\0') 。
因此,我們可以用下面兩種方法的任何一種來初始化字符串mystring:
char mystring [ ] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char mystring [ ] = "Hello";
在兩種情況下字符串或數組mystring都被定義為6個字符長(元素類型為字符char):組成Hello的5個字符加上最后的空字符('\0')。在第二種用雙引號的情況下,空字符('\0')是被自動加上的。
**注意:**同時給數組賦多個值只有在數組初始化時,也就是在聲明數組時,才是合法的。象下面代碼現實的表達式都是錯誤的:
mystring = "Hello";
mystring[ ] = "Hello";
mystring = { 'H', 'e', 'l', 'l', 'o', '\0' };
因此記住:我們只有在數組初始化時才能夠同時賦多個值給它。其原因在學習了指針(pointer)之后會比較容易理解,因為那時你會看到一個數組其實只是一個指向被分配的內存塊的常量指針(constant pointer),數組自己不能夠被賦予任何數值,但我們可以給數組中的每一個元素賦值。
在數組初始化的時候是特殊情況,因為它不是一個賦值,雖然同樣使用了等號(=) 。不管怎樣,牢記前面標下畫線的規則。
### 給字符序列的賦值
因為賦值運算的lvalue 只能是數組的一個元素,而不能使整個數組,所以,用以下方式將一個字符串賦給一個字符數組是合法的:
mystring[0] = 'H';
mystring[1] = 'e';
mystring[2] = 'l';
mystring[3] = 'l';
mystring[4] = 'o';
mystring[5] = '\0';
但正如你可能想到的,這并不是一個實用的方法。通常給數組賦值,或更具體些,給字符序列賦值的方法是使用一些函數,例如strcpy。strcpy (string copy) 在函數庫cstring (string.h) 中被定義,可以用以下方式被調用:
`strcpy (string1, string2);`
這個函數將string2 中的內容拷貝給string1。string2 可以是一個數組,一個指針,或一個字符串常量constant string。因此用下面的代碼可以將字符串常量"Hello"賦給mystring:
`strcpy (mystring, "Hello");`
例如:
~~~
| // setting value to string
#include
#include
int main () {
char szMyName [20];
strcpy (szMyName,"J. Soulie");
cout << szMyName;
return 0;
} | J. Soulie |
~~~
**注意:**我們需要包括頭文件才能夠使用函數**strcpy**。
雖然我們通常可以寫一個像下面**setstring**一樣的簡單程序來完成與**cstring** 中**strcpy**同樣的操作:
~~~
| // setting value to string
#include
void setstring (char szOut [ ], char szIn [ ]) {
int n=0;
do {
szOut[n] = szIn[n];
} while (szIn[n++] != '\0');
}
int main () {
char szMyName [20];
setstring (szMyName,"J. Soulie");
cout << szMyName;
return 0;
} | J. Soulie |
~~~
另一個給數組賦值的常用方法是直接使用輸入流(cin)。在這種情況下,字符序列的值是在程序運行時由用戶輸入的。
當cin 被用來輸入字符序列值時,它通常與函數getline 一起使用,方法如下:
`cin.getline ( char buffer[], int length, char delimiter = ' \n');`
這里buffer 是用來存儲輸入的地址(例如一個數組名),length 是一個緩存buffer 的最大容量,而delimiter 是用來判斷用戶輸入結束的字符,它的默認值(如果我們不寫這個參數時)是換行符newline character ('\n')。
下面的例子重復輸出用戶在鍵盤上的任何輸入。這個例子簡單的顯示了如何使用cin.getline來輸入字符串:
~~~
| // cin with strings
#include
int main () {
char mybuffer [100];
cout << "What's your name? ";
cin.getline (mybuffer,100);
cout << "Hello " << mybuffer << ".\n";
cout << "Which is your favourite team? ";
cin.getline (mybuffer,100);
cout << "I like " << mybuffer << " too.\n";
return 0;
}
| What's your name? Juan
Hello Juan.
Which is your favourite team? Inter Milan
I like Inter Milan too.
|
~~~
**注意**上面例子中兩次調用cin.getline 時我們都使用了同一個字符串標識 (mybuffer)。程序在第二次調用時將新輸入的內容直接覆蓋到第一次輸入到buffer 中的內容。
你可能還記得,在以前與控制臺(console)交互的程序中,我們使用extraction operator (>>) 來直接從標準輸入設備接收數據。這個方法也同樣可以被用來輸入字符串,例如,在上面的例子中我們也可以用以下代碼來讀取用戶輸入:
`cin >> mybuffer;`
這種方法也可以工作,但它有以下局限性是cin.getline所沒有的:
* 它只能接收單獨的詞(而不能是完整的句子),因為這種方法以任何空白符為分隔符,包括空格spaces,跳躍符tabulators,換行符newlines和回車符arriage returns。
* 它不能給buffer指定容量,這使得程序不穩定,如果用戶輸入超出數組長度,輸入信息會被丟失。
因此,建議在需要用cin來輸入字符串時,使用cin.getline來代替cin >>。
### 字符串和其它數據類型的轉換(Converting strings to other types)
鑒于字符串可能包含其他數據類型的內容,例如數字,將字符串內容轉換成數字型變量的功能會有用處。例如一個字符串的內容可能是"1977",但這一個5個字符組成序列,并不容易轉換為一個單獨的整數。因此,函數庫cstdlib (stdlib.h) 提供了3個有用的函數:
* atoi: 將字符串string 轉換為整型int
* atol: 將字符串string 轉換為長整型long
* atof: 將字符串string 轉換為浮點型float
所有這些函數接受一個參數,返回一個指定類型的數據(int, long 或 float)。這三個函數與cin.getline 一起使用來獲得用戶輸入的數值,比傳統的cin>> 方法更可靠:
~~~
| // cin and ato* functions
#include
#include
int main () {
char mybuffer [100];
float price;
int quantity;
cout << "Enter price: ";
cin.getline (mybuffer,100);
price = atof (mybuffer);
cout << "Enter quantity: ";
cin.getline (mybuffer,100);
quantity = atoi (mybuffer);
cout << "Total price: " << price*quantity;
return 0;
}
| Enter price: 2.75
Enter quantity: 21
Total price: 57.75 |
~~~
### 字符串操作函數(Functions to manipulate strings)
函數庫cstring (string.h) 定義了許多可以像C語言類似的處理字符串的函數 (如前面已經解釋過的函數strcpy)。這里再簡單列舉一些最常用的:
* `[strcat](http://www.cplusplus.com/ref/cstring/strcat.html): char* strcat (char* dest, const char* src); <font class="codecomment">//將字符串src 附加到字符串dest 的末尾,返回dest。</font>`
* `[strcmp](http://www.cplusplus.com/ref/cstring/strcmp.html): int strcmp (const char* string1, const char* string2); <font class="codecomment">//比較兩個字符串string1 和string2。如果兩個字符串相等,返回0。</font>`
* `[strcpy](http://www.cplusplus.com/ref/cstring/strcpy.html): char* strcpy (char* dest, const char* src); <font class="codecomment">//將字符串src 的內容拷貝給dest,返回dest 。</font>`
* `[strlen](http://www.cplusplus.com/ref/cstring/strlen.html): size_t strlen (const char* string); <font class="codecomment">//返回字符串的長度。</font>`
**注意:**char* 與char[] 相同。
關于這個函數庫的更多信息,參閱 [C++ Reference](http://www.cplusplus.com/ref/) 。