---
# System prepended metadata

title: 'string 字串 '
tags: [程設班講義]

---

# 簡介

`string` 很像   `char arr[]` 的加強版或`vector<char>`(之後會介紹)，但其提供了更多方便使用的函數。

![](https://i.imgur.com/adgNy3W.png)

雖然`<iostream>`函式庫中可以宣告`string`，但<font color="#EB6E41">完整功能還是要引入名為</font> `<string>` <font color="#EB6E41">的標頭檔</font>。

```cpp=
#include <string>
```


# 宣告
```cpp=
string st1;                    //空字串。

string str2 = "Hello World";  // 把str2初始化為字串字面常數。

string str3("Hello World");  // 把str3初始化為字串字面常數。

string str4(str3);          //把str4初始化為str3，str4為Hello World。

string str5(9, 'A');       //把str5初始為9個A，str5為AAAAAAAAA。

string str6(str2, 6, 2);  //把str6初始化為str2的第6項往後2項(包含自身)，str6為Wo。
```
::: warning
這樣宣告含有 10 個 str 的陣列，而不是大小為 10 的 str 字串。
```cpp=
string str[10];
```
:::

---
# 輸入&輸出
```cpp=
#include <iostream>
#include <string>
using namespace std;
int main(){
    string s;
    cin >> s;
}
```
這樣輸入不會截取到空格，例如輸入 chrislai is god ，s 只會輸出 chrislai ，即 s = chrislai ，若想包含空格，可以用`getline()` 。

## getline()
```cpp=
getline(輸入流, 目的地, 停止字元)
```

輸入流基本上只會用到`cin`，目的地是你要輸入給誰，停止字元就是他遇到誰會停，不打則為預設`'\n'` 。

```cpp=
string str;
getline(cin, str, 'c');
```

輸入 AaBbCcDd 時，str= AaBbC 。

一般 cin 輸入如果遇到空格就會斷開，而 `getline()` 就可以連同空格一起擷取。
```cpp=
#include<iostream>
#include<string>
using namespace std;

int main() {
    string str;
    
    getline(cin, str);
    cout << str << '\n';
}
```

這樣輸入就會截取到空格，例如輸入 chrislai is god ，s 會輸出 chrislai is god ，即 s = chrislai is god。

:::warning
如果在使用 `getline(cin, str)` 之前使用 `cin`，`getline` 的輸入會取到上面 `cin` 裡緩存區的東西，所以要特別加上 `cin.ignore()` 讓 `getline` 釋放緩存區的東西。
</span>
```cpp=
cin >> str;
cin.ignore();
getline(cin, str);
```
:::

## cin.ignore()
其功能是忽略掉一些輸入。

```cpp=
cin.ignore(int a, char c) ;
```

a 代表最多忽略幾個， c 代表遇到哪個字元時停止(停止 ignore，<font color="#EB6E41">即繼續讀取</font>)，而這兩個條件只要一個成立就會結束 `igonre`。

```cpp=
string str;
cin.ignore(3, 's');
cin >> str;
```
輸入`abcd`時str=`d` ; 輸入`asdf`時str=`df` 。

其預設為`cin.ignore(1, EOF)`(EOF為End Of File)，即忽略一個字元的輸入，放在`cin`下面的意義就是把cin完按的那個 enter 鍵(`'\n'`字元)忽略掉。


# 比較&修改
## 判斷
可以使用一整個字串比較、修改。

```cpp=
#include <iostream>
#include <string>
using namespace std;
int main(){
    string a = "123",b = "123";
    if(a == "123")
        cout << 1;  //此行會執行。
    if(a == b)
        cout << 1;  //此行會執行。
}
```

## 指定索引
跟陣列一樣，從`0`開始(0-based)。

```cpp=
#include <iostream>
#include <string>
using namespace std;
int main(){
    string s = "0123456789";
    cout << s[9] << '\n';        //輸出 9。
    for(int i = 0; i < 10; i++)
        cout << s[i] << ' ';     //輸出 0 1 2 3 4 5 6 7 8 9。
}
```

也陣列一樣，可進行比較或修改。

```cpp=
#include <iostream>
#include <string>
using namespace std;
int main(){
string s = "0123456789";
s[8] = '0';         //將原本為數字8的位置變成0。
cout << s;          //輸出0123456709。
if(s[9] == '9')
    cout << 1;      //此行會執行。
}
```
:::warning
使用 [ ] 存取的結果為char型態，因此進行操作時必須使用單引號，否則可能會編譯錯誤。

:::


## 加法
可以使用`+`或`+=`將想要的東西加在字串後方(沒有減、乘、除，更沒有取餘數)，把字串接在一起，要注意順序，在左邊的會靠左。
```cpp=
string str1 = "Hello", str2 = "World";

cout << str1 + str2;   //輸出HelloWorld。
```

## 大小關係
字串的比較會從左到右依照根據每個字元的ASCII碼逐一比較，直到符合以下任一情況。

* 兩個對應字元的 [ASCII碼](#ASCII碼) 不相等，則將其比較的結果做為字串間比較的結果。
* 找不到任何差異，但有一個字串的字元數比另一個字串還多，則較長的字串較大。
* 找不到任何差異，並且字串長度相同，則字串相等。

```cpp=
string str1 = "123", str2 = "12345";
string str3 = "123", str4 = "45";

cout << (str1 < str2);  //輸出 1。
cout << (str1 == str3); //輸出 1。
cout << (str3 > str4);  //輸出 0。
```

# 內建函式
## size() & length()

會回傳字串的長度。
```cpp=
string str = "HelloWorld";
cout << str.size() << ' ' << str.length(); //輸出10 10。
```

常搭配 for 迴圈，根據字串長度來決定執行次數。
```cpp=
string str;
cin >> str;
int n = str.length();   //n根據輸入的字串長度變化。
```
```cpp=
string str = "HelloWorld";

for (int i = 0; i < str.size(); i++)
    cout << str[i] << ' ';  //輸出H e l l o W o r l d 。
```

:::info
:::spoiler `.size()` 和 `.length` 的比較。
`.size()` 是為了兼容STL ( 標準樣板函式庫，之後會介紹 ) 的容器，而`.length` 是 C 語言的習慣而保留下來的，兩者原始碼相同，所以速度一致，也就是沒有差。
:::


## find()
如果找到該字元(串)，則回傳最前面的位置(`0-based`)，否則回傳 `string::npos `。


### string::npos
`npos` 被定義為常數，但不是特定的某個值，而是代表 `string::size_type` ( 以下簡稱`size_t` ) 的最大值，也就是 `string` 的最後一個字元，但通常 `stirng` 不會塞滿，所以也用來表示不存在的字元。

### string::size_type ( size_t )
我們在使用 `string` 的函式時，他回傳值是由 `string` 函式庫所定義的`size_t`，對於 `size_t` 值域的定義為無符號 ( `unsigned` )，所以他不是任何整數型態，而是照當前位元系統來決定。





```cpp=
//在32位元系統中
str.find(目標字串,起始位置) 

string a = "abcdeee";
    
cout << a.find('a') << '\n';    //輸出0。
    
cout << a.find("cde") << '\n';  //輸出2。
    
cout << a.find("e") << '\n';    //輸出4。
    
cout << a.find("z") << '\n';    //輸出string::npos，在32位元系統下 size_t 最大值為 2^32 - 1。
    
int f = a.find("z");
cout << f << '\n';              //輸出-1，因為string::npos在int會自動溢位成-1。
    
cout << a.find('d', 6) << '\n'; //輸出string::npos(一個數)。
   
cout << (a.find('f') == string::npos) << '\n'; //輸出1，沒搜索到就回傳 string::npos。  

cout << (str.find(" ") == -1) << '\n';         //輸出1，-1 的 size_t 會自動溢位成最大值。

int index = -1 ;
cout << (str.find(" ") == index) << '\n';      //輸出0，兩者的型別與值皆不相同。
```

## to_string()
將其他型態轉化成字串型態。

```cpp=
int n = 114514;
double d = 3.141;
float f = 3.141;

cout << to_string(n);  //輸出114514。
cout << to_string(d);  //輸出3.141000。
cout << to_string(f);  //輸出3.141000。
```

## stoi() & stod() & stoll() & stoull()
將字串型態轉化成其他型態。
- stoi   -->  `int` 型態。
- stod   -->  `double`型態。
- stoll  -->  `long long`型態。
- stoull -->  `unsigned long long`型態。

```cpp=
string a = "64645" ;
cout << stoi(a) - 645; //輸出64000;
```

:::warning
使用 **stoi() / stod() / stoll() / stoull()** 函式時，如果字串內有非數字之字元，會發生錯誤。

如果字串內數字超過欲轉換型態之範圍，也會發生錯誤。
:::

## toupper() & tolower()
將該字元轉為大寫或小寫(若非英文字母則相同)。

```cpp=
string a = "Aa.Bb.Cc";
    
for(int i = 0; i < a.length(); i++){
    a[i] = tolower(a[i]);
}
cout << a << '\n';                  //輸出aa.bb.cc。

for(int i = 0; i < a.length(); i++){
    a[i] = toupper(a[i]);
}
cout << a << '\n';                  //輸出AA.BB.CC。
```
::: warning
`toupper()` 和 `tolower()` 只能改變字元型態。
:::

## insert() & erase()
插入字串。

```cpp=
s.insert(位置,字串)
    
string s = "asd";
string i = "56"
s.insert(0, i);         //也可寫成s.insert(0,"56")。
cout << s << '\n';      //輸出56asd。
```

移除字串。

```cpp=
s.erase(位置,數量)

string s = "456789";
s.erase(0, 3);
cout << s << '\n';      //從第0個開始往後刪除3個，輸出789。
```

# ASCII碼
[ASCII碼](https://zh.wikipedia.org/zh-hk/ASCII)（發音： /ˈæski/，American Standard Code for Information Interchange，美國標準資訊交換碼）是目前世界上使用最廣泛的字元編碼，即用數字來表示字元，也可以互相轉換。

```cpp=
string str = "ABC";

cout << str[0] - 'A' << '\n';  //輸出0。
cout << str[1] - 'A' << '\n';  //輸出1。
cout << str[2] - 'A' << '\n';  //輸出2。
```
```cpp=
char chr = 'A';

cout << char(65);  //輸出A。
cout << int('A');  //輸出65。
cout << int(chr);  //輸出65。
```
![image](https://hackmd.io/_uploads/rJe-aP2TTR.png)

# StringStream
需要引入標頭檔。
```cpp=
#include <sstream>
```
`StringStream` 像是數字與字串中間的橋梁，同時也是類似於 `cin` 的輸入流，所以當測資是一長串無法確定筆數時可以用 `StringStream` 配合 `getline` 切分。

```cpp=
string str = "ABC123";

stringstream ss;      
stringstream ss(str);   //其值為ABC123。
```

## 輸入&輸出
```cpp=
#include<iostream>
#include<string>
#include<sstream>
using namespace std;

int main () {
    stringstream ss;
    int n = 114514;
    string str;
    
    ss << n;
    ss >> str;
    
    cout << str;   //輸出114514。
}
```
這表示其可以將 `int` 轉換成 `string` 型態，反之亦然，但還是較常用前面介紹的函式。

## 初始化
因為 `sstream` 重複宣告很消耗時間與記憶體，但已經寫入的 `sstream` 不能直接使用，必須經過初始化。
```cpp=
ss.str("");
ss.clear();
```
<font color="#EB6E41">兩行都要打</font>。

若沒有打 `s.str("")`，`sstream` 結尾的 `EOF` 不會被清除。

若沒有打 `ss.clear()` 雖然可以正常輸入輸出，但原先的字串還是會保留在 `sstream` 中，造成記憶體的消耗。

## getline() 用 stringstream 切分

```cpp=
#include<iostream>
#include<string>
#include<sstream>
using namespace std;

int main () {
    string str;
    getline(cin, str);
    stringstream ss(str);          //直接宣告含有str字串的sstream。

    while (getline(ss, str, ' ')) { //當遇到空格時停止擷取。
        cout << str << endl;
    }
}
```
這樣可以把輸入存取到陣列裡或 `Vector` (之後會介紹) 裡面。

## 換行擷取
因為換行字符對該函數來說是有效輸入，因此在擷取時會遇到擷取空字串的狀況。

```cpp=
#include<iostream>
#include<string>
#include<sstream>
using namespace std;

int main () {
    int n;
    cin >> n; 
    
    while (n--) {
        string str;
        getline(cin, str);
        
        cout << str << '\n';
    }
}
```
輸入
```cpp=
abc def geh
```

輸出
```cpp=
abc 

def
```

為了解決此問題，可以藉由判斷來解決問題。

```cpp=
#include<iostream>
#include<string>
#include<sstream>
using namespace std;

int main () {
    int n;
    cin >> n;
    
    while (n--) {
        string str;
        while (str.length() == 0)
            getline(cin, str);
        
        cout << str << '\n';
    }
}
```

輸入
```cpp=
abc def geh
```

輸出
```cpp=
abc 
def
geh
```

# 例題
###### 有什麼題目沒提到或有新增的煩請留言告知。

[a003: 提款卡密碼](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a003)

[a010: 聖經密碼](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a010)

[a012. Johnny B. Goode](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a012)

[a013: You can say that again!](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a013)

[a102: 字串操作練習](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a102)

[a168: 箱子裡面是什麼](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a168)

[a188: 數字翻轉](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a188)

[a199: 文字獄(一)](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a199)

[a202: 文字獄(二)](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a202)

[a205: 文字獄(三)](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a205)

[a228: pJ Count! Count! Count!](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a228)

[a233: 西元年轉天干地支年](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a233)

[a240: 秘密差 (APCS 106/03/04)](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a240)

[a241: 2019 Forbes](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a241)

[a247: pE 幾的"被"數？](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a247)

[a251: 多元選修好難喔](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a251)

[a261: Neko我婆#ΦωΦ](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a261)

[a423: 光頭學英文](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a423)

[a450: 成為數學genius](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a450)

[a467: 二則運算](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a467)

[a533: pC Sentence Compressor](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a533)

[a578: 文字獄(四)](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a578)

[a711: ahoy!! 我想要警告](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a711)

[a747: 都不揪](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a747)

[a832: 蘿莉圖真香](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a832)

[a872: 神奇的字串(2): 強迫症辛苦Coding日常](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a872)

[a877: 迴文針](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a877)

[a878: 好想海底撈____________________________月](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a878)

[a892: 用Code寫Code?](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=a892)

- [b166: 改作文](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=b166)

- [b172: 等式(1)](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=b172)

- [b174: 等式(2)](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=b174)

- [b183: 密碼](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=b183)

- [b184: 我的字典沒有困難!!!](https://dandanjudge.fdhs.tyc.edu.tw/ShowProblem?problemid=b184)

###### 都看到這了難道不按個愛心支持一下嗎?