# Python 及 C++ 由純文字檔輸入資料 > 作者:王一哲 > 日期:2023年8月8日 <br /> ## 前言 在程式競賽及 APCS 檢定中,需要從標準輸入讀取測資,通常測資會是用空格分隔的純文字檔,而且經常沒有說明測資的行數,要一直讀取到檔案結尾 (end-of-file, EOF),甚至每行的資料數量也不固定。假設純文字檔 data.txt 的內容為 ```python 1 2 3 4 5 6 7 8 9 10 ``` 以下是用 Python 及 C++ 從純文字檔讀取資料的方法。 <br /> ## Python 方法1:使用 sys.stdin 例如以下的程式碼 ```python= import sys a = [] # 儲存資料用的空白串列 a for line in sys.stdin: # 如果 sys.stdin 有讀到資料,將資料儲存到字串 line,繼續執行 for 迴圈 a.append(list(map(int, line.split()))) # 讀取用空格分隔的資料 #a.append(list(map(int, line.split(',')))) # 讀取用逗號分隔的資料 print(a) # 印出串列 a ``` <br /> 其中最難看懂的是第6行,因為 Python 經常把好幾毎個工具擠在同一行,解讀程式碼的原則為 1. 由最內層的括號向外、向左讀 2. 同層的括號內由左向右讀 第6行的功能是 1. line.split():將字串 line 使用 split() 依照空格拆解成數個字串。 2. map(int, line.split()):將拆解後的字串用 map 轉換成整數 int。 3. list(...):將轉換後的一串整數用 list 組成串列。 4. a.append(...):將轉換後的串列用 append 加到串列 a 最後面,因此 a 是二維串列。 <br /> 於 Linux 的命令列介面 (command-line interface, CLI) 執行程式時,如果程式碼 read.py 與 data.txt 放在同一個資料夾中,可以使用以下的指令執行程式 ```bash python3 read.py < data.txt ``` 如果是使用 Windows PowerShell,則要改用以下的指令 ```bash Get-Content data.txt | python.exe test.py ``` 畫面上會印出 ```python [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]] ``` <br /> ## Python 方法2:使用 try & except 例如以下的程式碼 ```python= a = [] # 儲存資料用的空白串列 a while True: # 無窮迴圈 try: # 試著用 input() 從標準輸入讀取資料 a.append(list(map(int, input().split()))) # 讀取用空格分隔的資料 #a.append(list(map(int, input().split(',')))) # 讀取用逗號分隔的資料 except EOFError: # 如果 input() 沒有讀到資料會回傳錯誤訊息 EOFError break # 停止 while 迴圈 print(a) # 印出串列 a ``` <br /> 使用 input() 可以從標準輸入讀取資料,但是沒有讀到資料時會回傳錯誤訊息 EOFError 並停止程式,因此 except EOFError: break 的功能,就是遇到 EOFError 時會執行冒號後的程式碼 break,跳出 while 迴圈。如果程式碼 read.py 與 data.txt 放在同一個資料夾中,可以使用以下的指令執行程式 ```bash python3 read.py < data.txt ``` 如果是使用 Windows PowerShell,則要改用以下的指令 ```bash Get-Content data.txt | python.exe test.py ``` 畫面上會印出 ```python [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]] ``` <br /> ## C++:使用 sstream 例如以下的程式碼 ```cpp= #include <iostream> #include <string> #include <sstream> #include <vector> using namespace std; int main() { string s; // 暫存資料用的字串 s stringstream ss; // 暫存資料用的字串流 ss vector<vector<int>> a; // 儲存資料用的二維 vector // 逐行讀取資料的 while 迴圈 while(getline(cin, s)) { // 如果 getline 有從 cin 讀到資料,將資料指定給 s,繼續執行 while 迴圈 ss.clear(); // 先清除 ss 的內容,否則無法存入第2行及後面的資料 ss << s; // 將 s 的指定給 ss vector<int> b; // 暫存資料用的一維 vector while(ss >> s) { // 由 ss 依序將資料指定給 s,如果 ss 中有資料繼續執行 while 迴圈 b.push_back(stoi(s)); // 將 s 轉成 int,加到 b 最後面 } a.push_back(b); // 將 b 加到 a 最後面 } // 印出 a 的內容,資料用空格分隔,每列最後一筆資料後方不加空格、直接換行 for(auto it = a.begin(); it != a.end(); it++) { for(auto it2 = (*it).begin(); it2 != (*it).end(); it2++) { cout << *it2 << " \n"[it2 == (*it).end()-1]; } } return 0; } ``` <br /> 於命令列介面 (command-line interface, CLI) 執行程式時,如果編譯後的可執行檔 a.out 與 data.txt 放在同一個資料夾中,可以使用以下的指令執行程式 ```bash ./a.out < data.txt ``` 如果是使用 Windows PowerShell,編譯後的可執行檔 test.exe 與 data.txt 放在同一個資料夾中,改用以下的指令 ```bash Get-Content data.txt | .\test.exe ``` 畫面上會印出 ```cpp 1 2 3 4 5 6 7 8 9 10 ``` <br /> ## 結語 以上是3種我常用的方法,網路上還能找到有許多不同的方法,甚至 Python 的 NumPy 當中還有更方便的工具,例如 genfromtxt。不過在程式競賽及 APCS 檢定不能使用 NumPy,畢竟它太過強大。 <br /> ## 延伸閱讀 〈[使用 numpy.genfromtxt 從文字檔中讀取資料](https://hackmd.io/@yizhewang/HyFxPWKXB)〉 <br /> ## 參考資料 1. [sys — System-specific parameters and functions: stdin](https://docs.python.org/3/library/sys.html#sys.stdin) 2. [8. Errors and Exceptions](https://docs.python.org/3/tutorial/errors.html) 3. [Python EOFError](https://docs.python.org/3/library/exceptions.html#EOFError) 4. [cplusplus.com String streams](https://cplusplus.com/reference/sstream/) --- ###### tags:`C++`、`Python`