Featured image of post 從零開始學Python (17) — 檔案讀寫:妳出現在我詩的每一頁(下)

從零開始學Python (17) — 檔案讀寫:妳出現在我詩的每一頁(下)

Day 17 檔案讀寫:妳出現在我詩的每一頁(下)

註:本篇文章同步刊載於iT邦幫忙,為鐵人賽之系列文章。
https://ithelp.ithome.com.tw/articles/10246077

上一回我們介紹了CSV的格式以及使用方法,
事實上很多檔案如果是以Excel能表達的狀態下,
CSV應該可以應付絕大多數的需求了!
但對於許多程式開發,或較為複雜的資料,
使用CSV就會稍嫌力有未逮,
因為CSV並不能夠簡單分出比較大量的層級。

舉例來說,如果我們現在有一組資料,
內含了一個學校的兩個班級,
那可能會長這樣:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
how_school = {
  "校長": "How哥",
  "工友": "林阿嘉",
  "class": {
    "A": {
      "teacher": "蔡阿嘎",
      "students": {
        "阿明": {"數學":55, "英文":70, "物理":55},
        "HowHow": {"數學":80, "英文":60, "物理":40}
      }
    },
    "B": {
      "teacher": "二伯",
      "students": {
        "小美": {"數學":90, "英文":88, "物理":100},
        "蔡哥": {"數學":50, "英文":50, "物理":40}
      }
    }
  }
}

像這樣子的東西,就很難使用csv簡單表現出來了!
所以我們一般會使用JSON來處理這樣子的結構。
JSON是JavaScript Object Notation的縮寫,
顧名思義,本來是用在JavaScript的格式。

後來大家發現實在是很好用,
所以也被拿來在很多地方做為資料交換用的格式。
當然,如果要人從頭刻出來,可能中間會有一些沒寫好的地方容易出錯,
這時候我們需要一個用來檢查格式的工具,
讀者可以搜尋 ’JSON online editor’ ,選擇自己用得順手的來嘗試編寫,
以下我們就拿在 https://jsoneditoronline.org/ 上操作做為範例:
進入時,幾個簡單的操作就看中間的部分:

左邊預設是code(原始的json碼),右邊預設是tree(JSON Object長的樣子);
我們可以寫好左邊以後,按一下**”Copy>”** 將其複製到右邊產生tree,
如果失敗的話,這個編輯器就會給你有關哪一行出錯的提示。
(反過來先產生tree再讓它翻成code也可以呦!)

預設裡應該會給出一段json碼如下,這是用來提示基本關於JSON的用法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
"array": [
    1,
    2,
    3
  ],
  "boolean": true,
  "color": "gold",
  "null": null,
  "number": 123,
  "object": {
    "a": "b",
    "c": "d"
  },

首先留意到,每個單位一般都會以一個字串做為它的名字,
和Python不同,字串請全數用**雙引號(“”)**來括住,單引號在這裡是行不通的!
JSON常用的格式有以下幾個:
array(陣列) :跟Python的串列基本相似,
除了前面有一個它的名字(可自行命名),
後面使用中括號框住,以**”,”** 逗號分隔。
boolean(布林值) :可以放在值的地方,使用true/false (注意是全小寫!)。
color(色彩) :用文字來表示色彩的話,有些編輯器 可以幫你解讀出來XD。
null(空) :相當於Python的None,表示沒有東西存在。
number(數字) :在JSON中數字可以是整數或小數。
object(物件) :使用大括號框起,裡面的每一段跟字典相似,
使用key:value 的型態放入,
可以多層(但是key必須要是字串 )。

那麼,一段Python中的資料結構,要怎麼轉換成JSON呢?
我們可以import json,並使用dumps() 方法來處理:
(如果是從檔案的話要用dump()方法)

1
2
3
import json
how_json = json.dumps(how_school)
print(how_json)

結果如下……咦?這什麼鬼東西?!

1
2
3
4
C:\Users\Desolve>python fromzero.py
{"\u6821\u9577": "How\u54e5", "\u5de5\u53cb": "\u6797\u963f\u5609", "class": {"A": {"teacher": "\u8521\u963f\u560e", "students": {"\u963f\u660e": {"\u6578\u5b78": 55, "\u82f1\u6587": 70, "\u7269\u7406
": 55}, "HowHow": {"\u6578\u5b78": 80, "\u82f1\u6587": 60, "\u7269\u7406": 40}}}, "B": {"teacher": "\u4e8c\u4f2f", "students": {"\u5c0f\u7f8e": {"\u6578\u5b78": 90, "\u82f1\u6587": 88, "\u7269\u7406":
 100}, "\u8521\u54e5": {"\u6578\u5b78": 50, "\u82f1\u6587": 50, "\u7269\u7406": 40}}}}}

Python提供的json模組在進行dump/dumps方法時,
預設所有輸進來的內容全都是ASCII Code,
所以當超出這個範圍,例如我們輸入的中文,
實質上應該要用UTF-8來表達才對。
所以我們改一下它的參數即可:

1
2
3
import json
how_json = json.dumps(how_school, ensure_ascii=False) # 我沒有要全ASCII,給我改回來XD
print(how_json)

執行結果如下:

1
2
3
C:\Users\Desolve>python fromzero.py
{"校長": "How哥", "工友": "林阿嘉", "class": {"A": {"teacher": "蔡阿嘎", "students": {"阿明": {"數學": 55, "英文": 70, "物理": 55}, "HowHow": {"數學": 80, "英文": 60, "物理": 40}}}, "B": {"teacher": "
二伯", "students": {"小美": {"數學": 90, "英文": 88, "物理": 100}, "蔡哥": {"數學": 50, "英文": 50, "物理": 40}}}}}

剛剛是將一個Python的資料結構轉成JSON字串,
那我們想要從JSON字串轉回Python的結構的話,
可以使用loads方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> how2 = json.loads(how_json) # 接續前面的how_json
>>> how2
{'校長': 'How哥', '工友': '林阿嘉', 'class': {'A': {'teacher': '蔡阿嘎', 'students': {'阿明': {'數學': 55, '英文': 70, '物理': 55}, 'HowHow': {'數學': 80, '英文': 60, '物理': 40}}}, 'B': {'teacher': '
二伯', 'students': {'小美': {'數學': 90, '英文': 88, '物理': 100}, '蔡哥': {'數學': 50, '英文': 50, '物理': 40}}}}}
>>> how2['校長']
'How哥'
>>> how2['工友']
'林阿嘉'
>>> how2['class']['A']
{'teacher': '蔡阿嘎', 'students': {'阿明': {'數學': 55, '英文': 70, '物理': 55}, 'HowHow': {'數學': 80, '英文': 60, '物理': 40}}}
>>>

我們可以從上面的範例看到,
在使用json時,轉換出來的Python結構,
基本上可以當成是字典,裡面有可能含有串列的部分,
操作起來不會有任何額外的問題,所以這點才對於Python相當重要,
因為直接讀進來一個檔案,就可以做為字典使用是非常方便的一件事情!

同樣我們再來嘗試一下寫到檔案和讀出來:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 寫入到json,使用json.dump()
>>> classA = how2['class']['A']
>>> with open('classA.json', 'w') as f:
...     json.dump(classA, f)
...
>>> classA = how2['class']['A']
>>> with open('classA.json', 'w') as f:
...     json.dump(classA, f, ensure_ascii=False)
...
# 從json讀出,使用json.load()
>>> with open('classA.json', 'r') as f:
...     reclassA = json.load(f)
...
>>> reclassA
{'teacher': '蔡阿嘎', 'students': {'阿明': {'數學': 55, '英文': 70, '物理': 55}, 'HowHow': {'數學': 80, '英文': 60, '物理': 40}}}

最後,提醒讀者留意一件事情:
json當中的每個object必須要是獨一無二的,也就是不可以重名!
其實這點對照一下Python的字典應該就清楚了,
因為Python的字典的key也必須要是獨特的。
那,如果重名會怎樣呢?
一般狀況下,最後面出現的會蓋掉前面的:

1
2
3
>>> repeated_json = '{"數學": 80, "數學": 50, "數學": 30}'
>>> json.loads(repeated_json) # 越考越爛阿QQ
{'數學': 30}

當然,json還有不少其他的應用,
有興趣的讀者可以再閱讀相關文件學習:
https://docs.python.org/3/library/json.html?highlight=json#command-line-options

那麼,我們來做個練習:

  1. 政府資料開放平臺上有提供不少的免費取用的資料,
    請將 https://data.gov.tw/dataset/6224 的JSON格式檔案下載下來。
  2. 請用上面示範的方式將其打開並存成一個Python的資料結構,
    命名為bs,這中間可能會遇到讀取的問題,
    是JSON檔文字編碼的錯誤,
    請在打開檔案時額外給入encoding=”utf-8" 的參數。
  3. bs是一個什麼?(字串?串列?字典?)
  4. 請利用前面所學,將所有位在’臺北市’的書店資訊存成一個串列,
    名為taipei。
  5. taipei中特色書店點閱數超過2000的店家是哪幾家呢?請列出其名字。

那我們就明天見囉!

工商時間:

抽獎活動還在繼續累積人數(現在好像沒有人想抽XD)
Python Taiwan的連結第100篇的文章 底下,
公開分享到你的臉書、按讚該篇文章、並留言告訴我說,
「你最喜歡這一整個系列的哪一篇?為什麼?」或
「除了從LeetCode學演算法系列以外,
你還想要看到關於什麼方向的文章?」

超過20則留言的話 (有完成以上步驟的才算),我們就抽一組
「從Leetcode學演算法|進階篇」+「從Leetcode學演算法|面試篇」
課程的免費兌換券進行贈送!

期限嘛…就延長到滿人數吧XDD (不然也沒辦法哈哈)

容筆者工商一下,
「從Leetcode學演算法|進階篇」 開放預購啦!
這次選了40道難度加深的LeetCode題目,
同樣也會細部解說對應的技巧及須要掌握的演算法!
同時這次購買進階篇的話,
額外還加贈**「從Leetcode學演算法|面試篇」** !
當中包含了面試準備須知分享及訪談國內外不同經驗的工程師
讓你不論是想走前端/後端/一般軟工 或者是想找國外的工作
初學想轉職 還是正在工作 ,都能夠從中得到收穫呦!
有興趣的朋友可以使用下面的早鳥優惠~
「從Leetcode學演算法|進階篇」+「從Leetcode學演算法|面試篇」
https://bit.ly/advleetcode

「從Leetcode學演算法」全套(基礎/進階/面試篇)同捆優惠:
https://bit.ly/allleetcode

共發表了 171 篇文章 ‧ 總計 311.6k
使用 Hugo 建立
主題 StackJimmy 設計