本章節將專門介紹兩種與文字相關的型別字元char與字串string。
事實上在APCS與各種演算法題目中,需要處理文字的題目並不算多,依照筆者個人經驗大約兩成到三成的題目會需要處理文字而已。儘管如此,處理文字還是一個相當重要且基本的操作,也是需要熟悉才行。
字元char是單個文字,也就是前面提到'a'、'B'、' '(這是空白)、'\n'(這是換行視為一個字)這種寫法,而char其實是以0~255的整數來表示文字,至於編號對應到文字的方式是利用ASCII編碼,這是維基百科的相關介紹,可以發現這個編碼只有大小寫英文字、常見標點符號、數字與基本數學符號而已,因此C++內建的文字處理是無法處理中文的,不過如果單純用C++實作演算法的話基本上也遇不到英文以外的文字。
上面提到了char其實是以整數的形式來表達文字,參考ASCII表可以發現65對應的字母是A,也就是說,我們在C++中打65與'A',其實兩個的值都是65,唯一的差別只在型別不同,因此將這兩個東西輸出會分別視為「整數」與「字元」因此得到不同結果。
回想章節0-1提過的強制轉型,假設我們將int與char互相轉換,其實就可以得到完全一樣的結果了,如cout<<65;跟cout<<(int)'A';都會輸出65,而cout<<(char)65;跟cout<<'A';都會輸出A。
另外雖然這兩個型別是不同的,但由於可以互相轉換,因此是可以進行比較的,如'A'==65為真,會得到1。
另外由於ASCII對應到英文字母ABC...Z是連續的,因此「'A'+1」的值對應到'B',小寫亦同。
而利用這個特性還可以做到很多有趣的事,例如c>='a'&&c<='z'判斷字元c是不是小寫字母,又或者轉換大小寫字母,儲存大寫字母的字元c通過「c-'A'+'a'」可以得到對應的小寫字母,反之亦可由小寫字母取得大寫字母。
只不過需要注意char型別經過+-運算後會直接轉型為int,因此如果要直接輸出需要將結果強制轉型回字元如(int)(c-'A'+'a')。
另外關於char還有一個重點,就是當透過cin來讀入char時,與其他型別會以空白或換行將結果分割後依序讀入不同,char只會一次讀入一個字,可以想像成打字的游標往後移一格,並且同樣會跳過空白跟換行不讀。
以下提供一個混合輸入char與int的範例,但這是示範的程式碼,平常沒事不會特別需要這樣寫。
#include<bits/stdc++.h>
using namespace std;
int main(){
char c;
int n;
cin>>c>>n;
cout<<n;
//若輸入「123」會輸出「23」,因為第一個字'1'被c讀走了
}
字串string用來儲存一串英文字,也就是儲存很多個char,章節0-1提到的"Hello"這一種就是字串。
其實字串概念類似字元陣列,因此字串s利用s[i]就可以取出第i個字,且s[i]的型別為char,很重要,如果使用s[i]="a";來改變字串s的話,由於s[i]已經是字元,用"a"是字串會無法執行,要用s[i]='a';才行。
另外字串同樣從0開始編號,string s="Hello";只能取s[0]~s[4],如果取到超過字串長度的情況會造成程式掛掉。
而雖然說字串與字元陣列類似,但還是有不小的差別,最重要的就是字串的長度是可變的,只要改變字串存的文字,其長度就會隨之改變,如宣告string s="Hello";此時s長度為5,用s="Hi";改變s後長度就變成2了。
而字串s可以用「s.size()」來取得目前的字串長度,也就是包含的char數量,注意是s加上「點size括號」不要寫錯了。
由於前面提到字串同樣從0開始編號,所以s[i]的i也只能用0~s.size()-1,也就是s[s.size()-1]就是最後一個字元了。
#include<bits/stdc++.h>
using namespace std;
int main(){
string s="Hello."; //宣告字串s,並賦值「Hello.」
int n=s.size(); //字串長度s.size()為6,n賦值為6
char c=s[0]; //s[0]為字串s開頭的字元,c賦值為'H'
s[s.size()-1]='!'; //將s的最後一個字元,即s[5]改為'!',s變為「Hello!」
}
若要宣告string的陣列當然也是可以的,如string s[10];,此時只要仔細想,s[i]是從字串陣列中取出其中一項,會是字串,然後字串可以再用一次[]取出字元,也就是s[i][j]是字元,這樣就可以很好理解了。
而字串的輸入與其他型別一樣,會被空白與換行分隔成多個字串。不過在某些很早期的題目中會出現需要把整行文字包含空格讀成一個字串,此時可以參考以下補充內容。
使用「getline(字串變數,cin);」可以一次讀入一行資料到字串變數內。
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
getline(s,cin);
cout<<"Hello, "<<s;
//若輸入「Alice and Bob」會輸出「Hello, Alice and Bob」
//而倘若只使用cin>>s;則會被空白分隔,只會輸出「Hello, Alice」
}
重點:字串比大小
如果單純用數學下去思考,可能會覺得只有數字才能比較大小,但其實先前提過字元char是利用整數來表示字元因此可以比大小。
而字串string也是可以比大小的,你也許會好奇每個字串組成、長度可能都不同要怎麼比,其實在資訊領域經常用稱作「字典序」的方式來比較、排列字串,所謂字典序就是如同字典的編排方式,最前面放a開頭的單字,再來b開頭...最後z開頭,但因為a開頭的字還有很多,所以就看第二個字母的順序來決定,若還是一樣就繼續比第三個字母,C++就是以這樣的概念,從最前面的字元開始來比較字串。
先前提過字串是類似字元陣列的概念,因此衍伸出了一些神奇的小技巧,如字串" \n"(長度為2,包含一個空格跟一個換行),可以利用" \n"[i]來取出其中的字元,當i=0時會是空格,i=1時會換行,而假設我們要在特定情況下換行,否則使用空格的話,可以將i替換成一個條件判斷,由於判斷式成立時其實是1,不成立時是0,因此剛好可以直接控制換行或空格。
#include<bits/stdc++.h>
using namespace std;
int main(){
for(int x=0;x<3;x++){
for(int i=1;i<=n;i++)cout<<i<<" \n"[i==n];
}
//以上會輸出3行「1 2 3 ... n」
for(int i=1;i<=9;i++)cout<<i<<" \n"[i%3==0];
//以上會輸出:
//1 2 3
//4 5 6
//7 8 9
}
若有回覆需求可以選擇提供email。