Try   HackMD

Python 在 Online judge 上的各種輸入輸出技巧

有鑑於目前大部分的online judge上的輸入輸出其實都是對 C/C++ 比較友善,大部分學習Python的初學者可能對於在judge上遇到各種輸入輸出格式就被困住。
因此筆者決定整理一些常見的輸入輸出樣式以及用如何用Python實作,希望能減少Python初學者解題時的阻礙。
本文針對每種常見的輸入輸出格式都列舉不只一種的方法,故內容較為繁雜,同學不必全部都記起來,可以挑選其中幾種上手的使用就好。

輸入

基本觀念

在開始示範之前,要建立Python輸入的兩個觀念:

  • input() 都是 以行為單位
  • input() 讀進來後都是以字串(str)的型態儲存

因此不像C/C++一樣可以一個參數一個參數的讀入,如果一行有很多的數字,要先整行讀入再處理
所以輸入大概可以分成以下三步驟:

  1. input()讀入
  2. 若有多個參數,用 split() 分割
  3. 針對資料個別轉型

會使用到的工具

s = input() #將輸入以字串型態儲存 s.split() #將文字以空格隔開,並用list儲存 int(s1) #將單一字串轉成整數 float(s2) #將單一字串轉成小數

常見範例

#一行輸入一個整數 a = int(input()) #一行輸入多個字串,以空格隔開 a = input().split() #一行輸入兩個整數,用空格分開,讀入後用兩變數儲存 a, b = input().split() a = int(a) b = int(b) #一行輸入多個整數,用空格分開,讀入後用list儲存 s = input().split() for i in range(len(s)): s[i] = int(s[i]) #一行數入多個數字,用逗點隔開,讀入後用list儲存 s = input().split(',') for i in range(len(s)): s[i] = int(s[i])

一些精簡的寫法

這裡提供幾個筆者自己為了精簡程式碼常用的輸入技巧,這些技巧並不會大幅加快輸入速度,只是讓程式碼更簡短,因此就算不會也沒關係,僅供各位參考。
這裡會用到 map()函數 , 型態轉換 以及 串列生成式 等概念,有興趣的可以去查相關文章

#一行輸入兩個整數,用空格分開,讀入後用兩變數儲存 #(三個以上也適用,只要再=左邊加上對應數量的變數) a, b = map(int, input().split()) #一行輸入多個整數,用空格分開,讀入後用list儲存 #用map搭配list轉型 s = list(map(int, input().split())) #一行數入多個數字,用逗點隔開,讀入後用list儲存 #用串列生成式 s = [int(i) for i in input().split()]

特別補充:多筆測資(輸入到EOF為止)

多筆測資的意思就是在一筆測資裡面可能會包含很多次輸入,也不會告訴你重複幾次或是輸入出現什麼時候停止,像是這題。這類輸入現在在 APCS 應該是不會出現,但是如果要參加競賽或是解ZeroJudge上各類題目時還是偶爾會遇到。因此還是放上參考程式碼,大家解題的時候當作模板照抄就好
這邊有用到 tryexcept 是例外處理的技巧,有一點小複雜,簡單說大概就是python會去"試著"執行下列程式,如果遇到粉紅色的錯誤直接果斷跳到except,而當input()沒有讀到東西的時候會被Python認定為 EOFerror ,所以就會自動跳到except,並跳出迴圈。

while True: try: #這裡放你的程式碼 #記得要縮排 #程式碼結束 except EOFError: break

其中EOFerror可加可不加,如果不加就代表所有的錯誤都會跳到except並結束程式,並且不會顯示錯誤訊息;加了就代表只有 EOFError 會跳到except,其他的錯誤還是會顯示錯誤訊息,如果程式中有錯誤比較方便Debug。
順帶一提,在 IDLE 中要輸入時要送出EOF代表輸入結束的訊號可以按 Ctrl+D

輸出

基本觀念

Python的print()函數預設是一行印出所有參數,以空格隔開,並在結尾換行。
但是其實我們可以透過 sepend 這兩個參數來改變輸出的格式,以滿足所有的要求。
也就是說print()函數中預設的 sep = ' ', end = '\n'
以下簡單示範一下如何運用

print(1,2,3)

輸出內容為

​​​​1 2 3
print(1,2,3, sep=',', end='.')

輸出內容為

​​​​1,2,3.  (結尾不換行)

因此我們就可以透過一個簡單的print印出各種格式的輸出了

常見範例

#輸出兩個數字,用逗點+空格隔開 print(a, b, sep=', ') #輸出一個等式'a+b=c' print(a, '+', b, '=', c, sep='') #輸出日期時間 print(y, '/', m, '/', d, ' ', h, ':', m, ':', s, sep='') #一行一個list的所有內容,以空格隔開,結尾換行 n = len(s) for i in range(n): if i == n-1: print(s[i]) #預設結尾是換行 else: print(s[i], end = ' ')

進階技巧

1. 轉成字串再輸出

有時輸入的格式比較複雜時,我們會習慣先把變數拼成一個字串再進行輸入,以輸出時間為例
這裡會用到 str() 函數將變數轉成字串再用字串的加法拼湊

print(str(h)+':'+str(m)+':'+str(s))

但是大家可能會發現,不管是用上述的改sep的想法或是用加法拼湊,好像都滿容易出錯的,
接下來要介紹的format()函數不僅可以讓程式碼可讀性更高,寫起來不容易出錯。

2-1. format函數

format()函數的概念是把括號裡的參數一個蘿蔔一個坑的塞進去字串中預留的大括號。
使用上還可以設定預留空格,對齊以及小數點後位數。
但篇幅有限因此在這裡只示範幾個其中幾個技巧,有興趣者可以查其他資料研究。

#輸出一個等式'a+b=c' a, b = 5, 7 c = a+b print('{}+{}={}'.format(a, b, c)) #輸出日期時間(按照預留位數,前面補零) y, m, d = 2021, 5, 17 h, m ,s print('{:04d}/{:02d}/{:02d} {:02d}:{:02d}:{:02d}'.format(y, m, d, h, M, s)) #輸出一個小數,取到小數點後第二位 s = 2/3 print('{:.2f}'.format(s))
2-2. f-字串

Python 3.6 開始,為了增加程式的可讀性而新增了新方法,只要在字串前面加上f就可以直接再大括弧內放變數。

a, b = 5, 7 c = a+b #輸出一個等式'a+b=c' print(f'{a}+{b}={c}') #輸出日期時間(按照預留位數,前面補零) y, m, d = 2021, 5, 17 h, m ,s print(f'{y:04d}/{m:02d}/{d:02d} {h:02d}:{M:02d}:{s:02d}') #輸出一個小數,取到小數點後第二位 s = 2/3 print(f'{s:.2f}')
3. join函數

如果你的List裡面的所有元素都是字串,那join()是可以將一個List裡面的所有內容用一個字串串起來的快速方法
使用方法為 s.join(L)。其中s為你要串連的字串,L為要被鏈接的List
舉例來說

L = ['1','2','3','4','5']
print('. '.join(L))

輸出內容為

​​​​1. 2. 3. 4. 5

因此題目常見的"一行輸出一串數字,並用空格分開"可以先用map將數列都轉成文字,再用join函數串起來,實際操作如下

print(' '.join(map(str, L))
4.邪惡的*解壓縮大法

在python裡面, *符號有一個很特別的用法叫做解壓縮
如果L是一個list或是tuple,那在函式裡的參數中放入*L,效果等同於把所有元素放進去當參數,並用逗點隔開。
舉個例子來說:

L = [1,2,3,4,5] print(*L)

其效果等同於

L = [1,2,3,4,5] print(L[0], L[1], L[2], L[3], L[4])

輸出為

1 2 3 4 5

如此一來就可以輕鬆的處理最常見的將數列一行輸出,用空格隔開。
當然,如果題目有要求要改成用逗點隔開,或是結尾不換行,也可以直接在修改sep與end兩個參數,示範如下

L = [1,2,3,4,5] print(*L, sep=',', end='')

輸出為

1,2,3,4,5 (結尾不換行)

結語

在 Online Judge 中的輸入輸出格式非常多樣,雖然大部分的題目重點都是在問如何"處理",也就是演算法的部分,因此輸入輸出都會相對單純。
或許有些題目也可能會出現各種花樣的輸入輸出格式,但原則上都離不開上述的工具的組合應用,如果有發現有哪些題目有特別的要求,或是上述內容不夠詳盡,都歡迎告知。