# <font face='consolas'><font size=5 color=#000080><center>**csv檔案讀寫程式**</center></font> 難度:★★進階 :::info 致謝:本文站在文山社大Oliver同學肩膀上開展。沒有Oliver先前對csv檔案的整理報告,即無拙文。感謝Oliver。 ::: csv檔案功能、格式等略,不清楚的讀者請自行求教Google老師。以下單刀直入立馬介紹csv讀寫。 ## csv檔案讀取 ```python= # -*- coding: utf-8 -*- import csv # Python standard library中的csv模組。 # 讀取csv檔內容,放到csvDataToRead變數中。 csvFileToRead = open('d:\\python\\member.csv', 'r', encoding='utf-8-sig') csvDataToRead = csv.reader(csvFileToRead) # 將csvDataToRead(原csv檔內容)轉為list。 dataList = list(csvDataToRead) # 注意:必須在轉完list之後才關檔,否則會產生'ValueError: I/O operation on closed file.' #    的exception。 csvFileToRead.close() print(dataList) # 修改list內容。 dataList.append(['Alice', 521, '自學']) dataList[1][0] = 'Rebecca' print(dataList) ``` <font color=red><b>程式重點在第9列:</b></font> ```python=8 # 將csvDataToRead(原csv檔內容)轉為list。 dataList = list(csvDataToRead) ``` 運用Python standard library中的csv模組讀取csv檔案,可輕易將檔案內容轉為Python內建的list或tuple型態。<font color=red><b>轉成了這些型態(尤其是list),後續的查詢、修改、新增、刪除等常見資料存取作業,就變得非常簡單。</b></font> 試想一下:假如沒有csv模組(或其他類似third party)可用,要怎樣讀取csv檔呢?很可能拿文字檔I/O充數。程式大概如下: ```python= csvFileToRead = open('d:\\python\\member.csv', 'r', encoding='utf-8-sig') plainText = csvFileToRead.read() ``` plainText就是檔案內容。不過那是真"plain text",普通字串耳。轉換list/tuple,行,可惜轉出來的list/tuple完全走調。請看下面的程式碼和輸出: ```python= csvFileToRead = open('d:\\python\\member.csv', 'r', encoding='utf-8-sig') plainText = csvFileToRead.read() dataList = list(plainText) # 將字串轉為list。 print(dataList) ``` 原來的csv檔: ![](https://i.imgur.com/IvCsame.png =x100) 用一般文字檔方式讀檔,轉出的list亂七八糟,不成人樣。一看即知那不是正確的結果。 ![](https://i.imgur.com/G38VPZz.png =720x) 用csv模組讀檔,轉出的list才是我們想要的樣子。 ![](https://i.imgur.com/smhfahL.png =720x) 拿csv當一般文字檔來讀,又想轉出正確的list,大概得自行寫csv parser。不算太難,卻煩。功力不足寫出的parser肯定:beetle:一堆。 講到這裡,大家清楚csv模組的功效了吧?像parsing這種dirty work,模組會替我們代勞,乖巧勤快又品質保證。這麼好用的工具豈能擱著讓它:zzz:? --- 讀取csv檔有兩點補充: 1. 在Windows平台,csv檔如採utf-8編碼,[無論有BOM無BOM,亦不管內容是純英數,或中、泰、韓、日文,open()開檔方法都須加上`encoding='utf-8-sig'`參數](https://hackmd.io/s/BkN-q-Vob): ```python= open('d:\\python\\member.csv', 'r', encoding='utf-8-sig') ``` 但csv如為Big5編碼的中文檔案,則勿加`encoding`參數,或者`encoding`用`'Big5'`, `'cp950'`,甚至`None`: ```python= open('d:\\python\\member.csv', 'r') ``` 2. csv欄位轉為list後,list所有元素的型態都是str。各種不同內容測試結果如下(組合太多,未能窮舉): |<center>**No**</center>|<center>**csv檔欄位內容</br><font color=red>紅字</font>表重要**</center>|<center>**範例**</center>|<center>**轉換後的list元素**</center>|<center>**備註**</center>| |:---|:------|:-----------|:-----------|:-----------| |<font color=red>**1**</font>|<font color=red>**整數**</font>|`123`|`'123'`|不管文字數字,都</br>轉換成str。| |2|浮點數|`-.805`|`'-.805'`|同上。| |3|Boolean|`True`|`'True'`|仍然是字串,不會</br>自動轉為`True`。| |<font color=red>**4**</font>|<font color=red>**全欄無引號**</font>|`鳳閣恩仇未了情`|`'鳳閣恩仇未了情'`|就是字串。| |<font color=red>**5**</font>|<font color=red>**前後綴雙引號`""`**</font>|`"紫釵記"`|`'紫釵記'`|<font color=red>**雙引號去除,效果</br>等於全欄無引號。**</font>| |<font color=red>**6**</font>|<font color=red>**前後綴單引號`''`**</font>|`'胡不歸'` |`"'胡不歸'"`| 前後單引號均保留。 | |<font color=red>**7**</font>|<font color=red>**前後均無引號,中插單引號**</font>|`A Bug's Life`|`"A Bug's Life"`|單引號不須以`\`</br>跳脫。| |<font color=red>**8**</font>|<font color=red>**前後綴雙引號,中插單引號**</font>|`"A Bug's Life"`|`"A Bug's Life"`|同上。| |<font color=red>**9**</font>|<font color=red>**前後綴雙引號,中置逗點`,`**</font>|`"男燒衣,女燒衣", 1975, 白駒榮,杜煥`|`['男燒衣,女燒衣', ' 1975', ' 白駒榮', '杜煥']`|<font color=red>`"男燒衣,女燒衣"`</font>視</br>為一欄。list只有4個元素。</br><font color=red>**注意:**</font>原字串中的</br><font color=#8B4513>**`1975`**</font>、<font color=#8B4513>**`白駒榮`**</font>分別</br>和其前面的<font color=#8B4513>`,`</font>間有</br>一空白,轉出來的</br>list元素隨之前置</br>空白。而原<font color=#8B4513>**`杜煥`**</font>前</br>面並無空格,list</font></br>亦未留白。| |<font color=red>**10**</font>|<font color=red>**前後綴單引號,中置逗點**</font>|`'男燒衣,女燒衣', 1975, 白駒榮,杜煥`|`["'男燒衣", " 女燒衣'", ' 1975', ' 白駒榮', '杜煥']`</font>|<font color=blue>`'男燒衣,女燒衣'`</font>視</br>為兩欄。list共有5個元素。| |11|欄首一段包雙引號|`"萬惡"淫為首`|`'萬惡淫為首'`|雙引號刪除。| |12|欄中一段包雙引號|`萬惡"淫"為首`|`'萬惡"淫"為首'`|雙引號保留。| |13|欄末一段包雙引號|`萬惡淫為"首"`|`'萬惡淫為"首"'`|雙引號保留。| |14|首末各一段包雙</br>引號|`"萬惡"淫為"首"`|`'萬惡淫為"首"'`|欄首雙引號去除,欄末保留。| |15|欄首一段包單引號|`'萬惡'淫為首`|`"'萬惡'淫為首"`|單引號保留。| |16|欄中一段包單引號|`萬惡'淫'為首`|`"萬惡'淫'為首"'`|單引號保留。| |17|欄末一段包單引號|`萬惡淫為'首'`|`"萬惡淫為'首'"`|單引號保留。| |18|首末各一段包單</br>引號|`'萬惡'淫為'首'`|`"'萬惡'淫為'首'"`|單引號全保留。| |19|前單引號,後雙</br>引號|`'雷鳴金鼓戰笳聲"`|`'\'雷鳴金鼓戰笳聲"'`|單雙引號均保留。單引號以`\`跳脫(escape)。 | |20|欄中分插雙單引號|`大鬧"廣昌'隆`|`'大鬧"廣昌\'隆'`|同上。| |<font color=red>**21**</font>|<font color=red>**前綴雙引號,但</br>無相對應的後綴</br>雙引號**</font>|`"重慶森林,1994,王家衛` <font color=#009E60>↩️</font> `悲情城市,1989, 侯孝賢` <font color=#009E60>↩️</font> `霸王別姬,1993, 陳凱歌`|`[['重慶森林,1994,王家衛\n悲情城市,1989, 侯孝賢\n霸王別姬,1993, 陳凱歌']]`|系統誤判。原資料3筆各3欄,list誤作1筆1欄。| |<font color=red>**22**</font>|<font color=red>**中插單一雙引號**</font>|`重慶"森林,1994,王家衛` <font color=#009E60>↩️</font> `悲情城市,1989, 侯孝賢` <font color=#009E60>↩️</font> `霸王別姬,1993, 陳凱歌`|`[['重慶"森林', '1994', '王家衛'], ['悲情城市', '1989', ' 侯孝賢'], ['霸王別姬', '1993', ' 陳凱歌']]`|list正確列出</br>3筆3欄,無誤判。</br>雙引號保留。| ## csv檔案寫入 ```python= # -*- coding: utf-8 -*- import csv # Python standard library中的csv模組。 dataList = [['Alex', 1, True], ['Becca', 2, False], ['Thomas', 4, True]] # 將list內容寫入到副檔名為.csv的檔案。 # 注意:檔案寫入時,open()方法要加上newline=''參數。否則生出來的csv檔用Excel開啟,每筆 #    資料之間會有一空列。 csvFileToWrite = open(r'd:\python\member1.csv', 'w', encoding='utf-8', newline='') # 應用csv模組的writer()方法。 csvDataToWrite = csv.writer(csvFileToWrite) # 逐列寫入。 for row in dataList: csvDataToWrite.writerow(row) # 注意:必須在寫完之後才關檔,否則會產生'ValueError: I/O operation on closed file.' #    的exception。 csvFileToWrite.close() ``` 使用csv模組寫檔,同樣享受操作方便和資料正確的好處。以上程式輸出: ![](https://i.imgur.com/DHztQsn.png =300x) 用Excel開啟。正確無誤: ![](https://i.imgur.com/ZsW5D9N.png =300x) 假設以普通文字檔方式寫檔,程式碼大概會是: ```python= # -*- coding: utf-8 -*- # 用一般文字檔寫入方式。 dataList = [['Alex', 1, True], ['Becca', 2, False], ['Thomas', 4, True]] # open()還是得加newline=''參數。 csvFileToWrite = open(r'd:\python\plainText.csv', 'w', encoding='utf-8', newline='') # 逐列寫入。 for row in dataList: csvFileToWrite.write(str(row)) csvFileToWrite.write('\r\n') # \r\n是換列符號,即16進位的0D 0A。 csvFileToWrite.close() # 寫入完畢記得關檔。 ``` 寫入固然成功。但plainText.csv卻長這個模樣: ![](https://i.imgur.com/wJEhRGD.png =300x) 用Excel開啟: ![](https://i.imgur.com/9dX7Qix.png =300x) 資料滲沙挾石,雜草叢生。 一言蔽之,檔案寫入也請採用csv模組,別重新發明輪子。 --- 另外,寫檔須注意一點:`open()`方法要加上`newline=''`參數。 ```python= # 下面的open()方法未加newline=''參數。 csvFileToWrite = open(r'd:\python\member1.csv', 'w', encoding='utf-8') ``` 以上程式碼未加這參數,後果是:產生的csv檔在換列處會多製造一個carriage return符號,即16進位的0D。本來Windows文字檔換列碼是16進位的0D 0A,多一個c/r,變成<font color=red>0D</font> 0D 0A。以Windows的notepad「記事本」瀏覽,看似正常。用notepad++開啟,則看到兩列間插入一空列: ![](https://i.imgur.com/gv1Okhw.png =300x) Excel開檔亦如此: ![](https://i.imgur.com/4DVaIRy.png =300x) 所以,欲掃除文盲,不,是掃除空列,寫檔毋忘`newline=''`,除非真想製造空列效果。 ## 結論 建議無論是讀是寫,都恭請standard library的csv模組大爺出馬,勿土法練鋼拿一般文字檔I/O勉強湊合。文字檔I/O對象是普通文件。對csv這樣有格式要求的檔案,引進專屬模組才事半功倍。 >[<font color=#5E86C1>「工欲善其事,必先利其器」。</font>](http://img.taopic.com/uploads/allimg/110930/114-11093014455299.jpg "工具不嫌多,越多越順手。") >[color=#5E86C1] --- 外部參考資料: [如何使用Excel開啟UTF-8格式的CSV檔案](http://crm2.tw/salesforce-tips/excel-data-process-utf-8/ "本篇介紹如何使用Excel儲存及開啟UTF-8格式的CSV檔案。") ###### tags: `csv` `文字檔` `讀檔` `寫檔` `standard library` `csv模組` `newline`