# File I/O
## Sprout Py2021
@robert1003
----
I/O = Input / Output = 輸入 / 輸出
<span>File I/O = 檔案輸入 / 輸出<!-- .element: class="fragment" data-fragment-index="2" --></span>
----
檔案輸入輸出發生在什麼時候?
----
輸入(Input):開啟檔案的時候

----
輸出(Output):儲存檔案的時候

---
## 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 |
|  |  |
---
# 路徑(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
----

小黃瓜??
----
沒拉,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 可以互包
----
來看個例子:

----
如果你想挑戰自己,可以自己寫寫看 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)
```
{"metaMigratedAt":"2023-06-15T07:50:37.635Z","metaMigratedFrom":"YAML","title":"File I/O","breaks":true,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"3d3e812e-7132-40aa-b06f-8f5380bf64d1\",\"add\":8447,\"del\":284}]"}