# 進階輸入輸出與資料型態 這篇來講講實用的輸入以及輸出技巧。由於很多輸入輸出都跟使用的資料型態有關,所以也會帶到一點資料型態的部分 ## 常用資料型態的輸入輸出 ### 整數 #### 資料大小範圍 | 資料型態 | 範圍 | 估計值 | | ----- | ----- | ----- | | 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