---
# System prepended metadata

title: File I/O

---

---
slideOptions:
  transition: slide
---

# File I/O

## Sprout Py2021

@robert1003

----

I/O = Input / Output = 輸入 / 輸出
<span>File I/O = 檔案輸入 / 輸出<!-- .element: class="fragment" data-fragment-index="2" --></span>

----

檔案輸入輸出發生在什麼時候？

----

輸入（Input）：開啟檔案的時候
![](https://i.imgur.com/8s2Fwcg.jpg)

----

輸出（Output）：儲存檔案的時候
![](https://i.imgur.com/VlB9pil.jpg)

---

## Outline
<!--- + 一些準備 --->
+ 目錄
+ 檔案
+ 路徑
+ 開檔
+ 讀檔
+ 寫檔
+ 關檔
+ 常用寫法

以下是一些補充

+ pickle
+ JSON

---

# 目錄（Directory）

----

其實就是資料夾
<span>桌面也是一個目錄（資料夾）<!-- .element: class="fragment" data-fragment-index="1" --></span>

---

# 檔案（File）

----

這些都是檔案：
+ txt
+ zip
+ docx
+ py
+ cpp
+ ...

----

檔案可以分成兩種：
| 文字檔（text file）| 二進位檔（binary file）|
|-|-|
|你看得懂|你看不懂|
| html, txt | png, zip, docx |
| ![](https://i.imgur.com/HBqN8Wh.png) | ![](https://i.imgur.com/Rm39H8Z.png) |



---

# 路徑（Path）

----

其實就是檔案的位置

----

路徑也可以分成兩種
<span>絕對路徑＆相對路徑<!-- .element: class="fragment" data-fragment-index="2" --></span>

----

## 絕對路徑

可以看成地址（？

----

Windows: `C:\windows\a.dll`
<span>C槽底下的windows資料夾裡的a.dll<!-- .element: class="fragment" data-fragment-index="3" --></span>

----

Linux: `/tmp/b07902047/out.png`
<span>根目錄底下的tmp底下的b07902047的out.png<!-- .element: class="fragment" data-fragment-index="4" --></span>

----

## 相對路徑

直接舉例可能比較好懂

----

`hello2.py` 
<span>當前目錄下的hello2.py<!-- .element: class="fragment" data-fragment-index="3" --></span>

----

`fileIO/hello3.py`
<span>當前目錄下的fileIO資料夾裡面的hello3.py<!-- .element: class="fragment" data-fragment-index="3" --></span>

----

`../hello.py`
<span>上一層資料夾的hello.py<!-- .element: class="fragment" data-fragment-index="3" --></span>

----

`../robert/ans/hw1.pdf`
<span>上一層資料夾的robert資料夾裡面的ans資料夾裡面的hw1.pdf<!-- .element: class="fragment" data-fragment-index="3" --></span>

----

所以他們差在哪裡？
<span>
絕對：一定從最上面的目錄開始描述
相對：從當前目錄開始描述<!-- .element: class="fragment" data-fragment-index="3" --></span>


---

# 開檔

----

一個指令
```python
[file_object] = open([file_path], [mode])
```
```python
f = open('hello.py', 'r')
```
<span>file_object: 檔案物件
file_path: 檔案路徑
mode: 開檔模式
<!-- .element: class="fragment" data-fragment-index="4" --></span>

----

檔案物件（file_object）？
<span>可以自己取名字<!-- .element: class="fragment" data-fragment-index="0" --></span>
<span>讓python可以存取電腦上的檔案<!-- .element: class="fragment" data-fragment-index="1" --></span>

----

檔案路徑（file_path）？
<span>就是剛剛講的路徑
絕對路徑跟相對路徑都可以<!-- .element: class="fragment" data-fragment-index="0" --></span>

----

開檔模式（mode）？
<span>簡單來說就是跟python講你現在要拿這個檔案幹嘛<!-- .element: class="fragment" data-fragment-index="0" --></span>
<span>
| 模式 | 功能 |
|-|-|-|
| r | 讀 |
| w | 寫 |
| a | 寫在最後面 |
<!-- .element: class="fragment" data-fragment-index="0" --></span>
<span>w: 如果檔案不存在會建一個新的，存在的話會覆蓋原本的檔案<!-- .element: class="fragment" data-fragment-index="0" --></span>

---

# 讀檔

----

兩個指令

----

讀一整列
```python
[string] = [file_object].readline()
```
```python
f = open('hello.txt', 'r')
s1 = f.readline()
s2 = f.readline()
print(s1, end='')
print(s2, end='')
```
<span>string: 把回傳的字串存到string裡
file_object: 檔案物件
<!-- .element: class="fragment" data-fragment-index="4" --></span>

----

讀特定數量的字元
```python
[string] = [file_object].read([length])
```
```python
f = open('hello.txt', 'r')
s = f.read(11)
print(s, end='')
```
<span>string: 把回傳的字串存到string裡
file_object: 檔案物件
length: 要讀幾個字元，型別是int
<!-- .element: class="fragment" data-fragment-index="4" --></span>

----

如果不打length會發生什麼事？
```python
s = f.read()
```
<span>他會一次把整個檔案讀進來<!-- .element: class="fragment" data-fragment-index="3" --></span>

---

# 寫檔

----

一個指令
```python
[file_object].write([string])
```
```python
f = open('hola.txt', 'w')
f.write('Hola, mondo!\n')
f.write('Me llamo Robert.\n')
```
<span>string: 要寫入的字串
file_object: 檔案物件
<!-- .element: class="fragment" data-fragment-index="4" --></span>

---

# 關檔

----

一個指令
```python
[file_object].close()
```
```python
f.close()
```
<span>file_object: 檔案物件
<!-- .element: class="fragment" data-fragment-index="4" --></span>

----

什麼時候要關檔？
使用完的時候（廢話），暫時不需要他了

----

一定要關檔案嗎？

----

不關檔案的壞處
1. 佔你記憶體的空間，讓你程式變慢
2. 在你關檔之前，檔案都不會被真的寫入
3. 你有可能開超過數量上限的檔案
4. 看起來很不專業

----

關於2.如果你想馬上寫入檔案怎麼辦？
<span>
```python
f = open('hola.txt', 'r')
print(f.read(), end='')
f.flush()
```
<!-- .element: class="fragment" data-fragment-index="4" --></span>

---

# 常用寫法

----

常用寫法 1
```python
with open([file_name], [mode]) as [file_object]:
    # do something
```
```python
with open('hola.txt', 'r') as f:
    print(f.read(), end='')
```
只有在那個迴圈裡檔案才是開的（所以不用關檔）

<span>注意上面的寫法等於下面的寫法：<!-- .element: class="fragment" data-fragment-index="4" --></span>
<span>
```python
f = open('hola.txt', 'r')
print(f.read(), end='')
f.close()
```
<!-- .element: class="fragment" data-fragment-index="4" --></span>

----

常用寫法 2
```python
with open([file_name], 'r') as [file_object]:
    for [line] in [file_object]:
        # do something
```
```python
with open('hello.txt', 'r') as f:
    for line in f:
        print(line, end='')
```
遍歷所有列

----

常用寫法 3
```python
with open([file_name], 'w') as [file_object]:
    print([string], file = [file_object])
```
```python
with open('bonjour.txt', 'w') as f:
    print('Bonjour, monde', file=f, flush=True)
    print("Je m'appelle Robert", file=f)
```
這樣就不用寫`f.write`了（？
可以直接用`print`

---

# 小練習

----

找出這個[檔案](https://gist.github.com/robert1003/0794860663de4177dd88e951132b2d21)裡最大的數字
找到了之後可以到[這裡](https://neoj.sprout.tw/problem/8763/)確認答案

---

# pickle

----

![](https://i.imgur.com/n6HianH.jpg)
小黃瓜？？

----

沒拉，pickle是一個直接存物件的東西

官網：[pickle](https://docs.python.org/3/library/pickle.html)

----

怎麼使用？
<span>
```python
# pickle.dump([object_name], [file_object])
# [var_name] = pickle.load([file_object])

import pickle # dump, load

dic = {1: 'a', 2: 'b', 3: 'c'}
print(dic)
with open('demo', 'wb') as f:
    pickle.dump(dic, f)
with open('demo', 'rb') as f:
    dic2 = pickle.load(f)
print(dic2)
```
<!-- .element: class="fragment" data-fragment-index="4" --></span>

----

基本上任何python裡面的東西都可以用pickle存成一個二進位檔（binary file）

---


# JSON

----

JSON (JavaScript Object Notation) 是一種人看得懂的存資料方法
官網：[JSON](https://www.json.org/json-en.html)

----

如果把它當成python物件來看，他其實就只是由 list 跟 dict 還有 value 所組成

* `{}` 組成一個 dict
* `[]` 組成一個 list
* value 就是 字串、整數、true、false、null
* dict 跟 list 可以互包

----

來看個例子：
![](https://i.imgur.com/3XeWgHY.png)

----

如果你想挑戰自己，可以自己寫寫看 json parser XD
你可能會需要用到遞迴的觀念（我們沒教）

----

如果你很懶也沒關係，我們來看看怎麼使用 `json` 這個套件～

----

我這邊示範的`sample.json`檔可以從[這裡](https://gist.githubusercontent.com/robert1003/4dbb3abb8b7d63aae1950f459ca7d4fa/raw/3ac196eb95d7034241bb676220aeb2df76d9ddf5/sample.json)下載～

```
{
    "arguments": {
        "number": 10
    },
    "url": "http://localhost:8080/restty-tester/collection",
    "method": "POST",
    "header": {
        "Content-Type": "application/json"
    },
    "body": [
        {
            "id": 0,
            "name": "name 0",
            "description": "description 0"
        },
        {
            "id": 1,
            "name": "name 1",
            "description": "description 1"
        }
    ],
    "output": "json",
    "delayed": false,
    "success": true,
    "ttl": 256.0,
    "secret": null
}
```

----

* `json.load`：從檔案讀 json 
* `json.loads`：從字串讀 json

```python
# [var_name] = json.load([file_object])
# [var_name] = json.loads([string_name])

with open('sample.json', 'r') as f:
    obj = json.load(f)
    print(obj)
    
with open('sample.json', 'r') as f:
    obj = json.loads(f.read())
    print(obj)
```

----

* `json.dump`：把 dict 或 list 換成 string 然後寫進檔案
* `json.dumps`：把 dict 或 list 換成 string

```python
# json.dump([dict_or_list], [file_object])
# json.dumps([dict_or_list])

# a 就是 sample.json 讀出來的結果
a = {'arguments': {'number': 10}, 'url': 'http://localhost:8080/restty-tester/collection', 'method': 'POST', 'header': {'Content-Type': 'application/json'}, 'body': [{'id': 0, 'name': 'name 0', 'description': 'description 0'}, {'id': 1, 'name': 'name 1', 'description': 'description 1'}], 'output': 'json', 'delayed': False, 'success': True, 'ttl': 256.0, 'secret': None}

with open('sample2.json', 'r') as f:
    json.dump(a, f, indent=4) # indent 參數可以不加，加了是為了排版 ㄖ
    
s = json.dumps(a, indent=4) 
print(s)
```

