# 進階輸入輸出與資料型態
這篇來講講實用的輸入以及輸出技巧。由於很多輸入輸出都跟使用的資料型態有關,所以也會帶到一點資料型態的部分
## 常用資料型態的輸入輸出
### 整數
#### 資料大小範圍
| 資料型態 | 範圍 | 估計值 |
| ----- | ----- | ----- |
| int |$-2,147,483,648$ ~ $2,147,483,647$| $\pm2\times10^9$ |
|long long| $-9223372036854775808$ ~ $9223372036854775807$ | $\pm9\times10^{18}$ |
| \_\_int128 | $-2^{127}$ ~ $2^{127} - 1$ | 很大 |
#### 使用範例
```cpp
int a;
cin >> a;
cout << a << endl;
```
```cpp
long long b;
cin >> b;
cout << b;
```
`__int128` 無法被直接輸入輸出,通常是運算較大的數字才會用到
### 浮點數
浮點數就是指有小數點的數字
| 資料型態 | 範圍 | 精度 |
| ----- | ----- | ----- |
| float | $3.4e\pm38$ | 小數點後七位 |
| double | $1.7e\pm308$ | 小數點後十五位 |
#### cin/cout 範例
```cpp
float a;
double b;
cin >> a >> b;
cout << a << ' ' << b;
```
#### 小數點後第幾位
記得要有標頭檔 `<iomanip>`
```cpp
// #include <iomanip>
double n = 3.1415;
cout << fixed << setprecision(2) << n;
```
#### 控制小數
記得要有標頭檔 `<cmath>`
`ceil()` : 無條件進位
`floor()` : 無條件捨去
`round()` : 四捨五入
```cpp
// #include <cmath>
double x = 3.1415926535
cout << ceil(x) << ' ';
cout << floor(x) << ' ';
cout << round(x);
```
### 字元與字串
一個 `char` 變數就是儲存 [ASCII](https://zh.wikipedia.org/zh-tw/ASCII) 表裡面的一個整數編號
#### 使用範例
```cpp
char c = 'c';
cout << (int)c << endl;
cout << (char)66 << endl;
c -= 2;
cout << c << endl;
```
#### 輸出
```cpp
99
B
a
```
### 字串
這裡使用 C++ 的方法來處理字串,C 語言的可以看[這篇](https://hackmd.io/@ShanC/ByV3i9bUC)
首先要先引入標頭檔 `<string>`
#### cin/cout 範例
```cpp
// #include <string>
string s;
cin >> s;
cout << s;
```
#### 讀入整行範例
`getline()` 可以讀入整行
```cpp
// #include <string>
string s;
getline(cin, s);
cout << s;
```
#### cin/getline 範例
要注意 `getline()` 會把 `'\n'` 處理掉,但是 `cin` 不會處理掉 `'\n'` (`'\n'` 還是存在輸入流裡面)。因此兩者混著使用時須要再多一行 `getline()` 來處理掉 `'\n'`
```cpp
string s, tmp;
int n;
cin >> n;
getline(cin, tmp); // 這一行專門把 '\n' 給處理掉
getline(cin, s);
```
#### 數字與字串做轉換
```cpp
string s = "123";
int n = stoi(s);
cout << n << '\n';
cout << to_string(n) << '\n';
```
#### stringstream 處理空白
記得要有標頭檔 `<sstream>`
```cpp
// #include <sstream>
stringstream ss;
string tmp, s = "12 31415 15 1357";
int arr[10], n = 0;
ss << s; // 將字串丟入 stringstream
while (ss >> tmp) // 一個一個將字串丟入 tmp
arr[n++] = stoi(tmp); // 把字串轉成數字、再存進 arr[]
for (int i = 0; i < n; i++) // 印出來
cout << arr[i] << '\n';
```
## 輸出格式調整
### 向右對齊
記得要有標頭檔 `<iomanip>`
#### 使用範例
```cpp
cout << setw(4) << 3 << setw(4) << 12 << '\n';
cout << setw(4) << 213 << setw(4) << 8 << '\n';
```
#### 輸出
```tex
3 12
213 8
```
### 向左對齊
記得要有標頭檔 `<iomanip>`
#### 使用範例
```cpp
cout << left << setw(4) << 3 << left << setw(4) << 12 << '\n';
cout << left << setw(4) << 213 << left << setw(4) << 8 << '\n';
```
#### 輸出
```tex
3 12
213 8
```
### 填入特定字元
記得要有標頭檔 `<iomanip>`
```cpp
int x = 3;
cout << setw(3) << setfill('0') << x << '\n'; // 003
```
## endl 與 '\n' 的差別
`endl` 除了暫存之外,還會 flush 掉 output buffer
`'\n'` 只會單純換行,因此會比較快
```cpp
cout << "hello, world" << endl;
cout << "It's MyGo!!!!! " << 123 << '\n';
```
## IO 優化
競程選手們習慣在主程式第一行就寫上
```cpp
ios::sync_with_stdio(false);
cin.tie(0);
```
這樣速度會快很多
有時候原本 **<font color="purple">TLE</font>** 的題目就會因此 **<font color="#008E00">AC</font>**
### sync_with_stdio()
先說 `sync_with_stdio()` 這個函數是什麼意思
這個函數是說「同步 C 與 C++ 的輸入/輸出流」。原本 C++ 的 `cin` 和 `cout` 是很快的,但是為了在混著使用時與 `scanf` 和 `printf` 同步,因此讓 C++ 的慢一點。原本 `sync_with_stdio()` 默認的數值是 `true`,所以我們上述那一行 `sync_with_stdio(false)` 就會把它關掉
### cin.tie()
在原本 C++ 的 `cin` 中,都會自動呼叫 `cout.flush()` 函數,這會導致 output buffer 每次出入都刷新一次,非常沒效率,因此需要 `cin.tie(0)` 把它關掉
### 避免 scanf/printf 與 cin/cout 混用
如果加了上面那兩行,可想而知,原本為了協調 C 與 C++ 的輸入輸出就會被破壞。一旦混用了就會出現不可預期的後果 (順序會亂掉),這部分請讀者自行體會幾次就會知道了
## 參考資料
- [海大競程 - 進階輸入輸出](https://hackmd.io/@LeeShoWhaodian/AdvancedIO)
- [cppreference - std::endl](https://en.cppreference.com/w/cpp/io/manip/endl)
- [std::endl ??](https://hackmd.io/@magical/ryMaPoaQ_)
- [stackoverflow - "std::endl" vs "\n"](https://stackoverflow.com/questions/213907/stdendl-vs-n)
- [Codeforces - Why is the ios_base::sync_with_stdio(false);cin.tie(NULL); not making speed of cin = scanf??](https://codeforces.com/blog/entry/89212)
----
> [ShanC 程式競賽筆記](https://hackmd.io/@ShanC/B1ouGxqcC)
> 作者: ShanC
> 更新: 2024/1/10