文件讀寫
讀寫文件就是請求操作系統(tǒng)打開一個(gè)文件對象(通常稱為文件描述符),然后,通過操作系統(tǒng)提供的接口從這個(gè)文件對象中讀取數(shù)據(jù)(讀文件),或者把數(shù)據(jù)寫入這個(gè)文件對象(寫文件)
讀文件
open() 函數(shù)用于打開一個(gè)文件對象
f = open('/Users/michael/test.txt', 'r')
標(biāo)示符'r'表示讀
如果文件不存在,open() 函數(shù)就會拋出一個(gè) IOError 的錯(cuò)誤
如果文件打開成功,調(diào)用 read()方法可以一次讀取文件的全部內(nèi)容,Python 把內(nèi)容讀到內(nèi)存,用一個(gè) str 對象表示
str1 = f.read()
最后一步是調(diào)用close()方法關(guān)閉文件
f.close()
為了確保文件能正確被關(guān)閉,可用 try ... finally :
try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
with 語句來自動幫我們調(diào)用 close() 方法:
with open('/path/to/file', 'r') as f:
print(f.read())
-
read()會一次性讀取文件的全部內(nèi)容 -
read(size)每次最多讀取size個(gè)字節(jié)的內(nèi)容 -
readline()每次讀取一行內(nèi)容 -
readlines()一次讀取所有內(nèi)容并按行返回list
file-like Object
像 open() 函數(shù)返回的這種有個(gè)read() 方法的對象,在 Python 中統(tǒng)稱為 file-like Object 。除了file外,還可以是內(nèi)存的字節(jié)流,網(wǎng)絡(luò)流,自定義流等等
file-like Object不要求從特定類繼承,只要寫個(gè)read()方法就行
字符編碼
指定讀取文件的編碼(默認(rèn) utf-8):
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
遇到有些編碼不規(guī)范的文件,你可能會遇到 UnicodeDecodeError ,因?yàn)樵谖谋疚募锌赡軍A雜了一些非法編碼的字符
open() 函數(shù)還接收一個(gè) errors 參數(shù),表示如果遇到編碼錯(cuò)誤后如何處理
最簡單的方式是直接忽略:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
二進(jìn)制文件
要讀取二進(jìn)制文件,用 'rb' 模式打開文件即可:
f = open('/Users/michael/test.jpg', 'rb')
f.read()
寫文件
寫文件調(diào)用 open() 函數(shù)時(shí),傳入標(biāo)識符 'w' 或者 'wb' 表示寫文本文件或?qū)懚M(jìn)制文件:
f = open('/Users/michael/test.txt', 'w')
f.write('Hello, world!')
f.close()
with open('/Users/michael/test.txt', 'w') as f:
f.write('Hello, world!')
當(dāng)我們寫文件時(shí),操作系統(tǒng)往往不會立刻把數(shù)據(jù)寫入磁盤,而是放到內(nèi)存緩存起來,空閑的時(shí)候再慢慢寫入。只有調(diào)用close()方法時(shí),操作系統(tǒng)才保證把沒有寫入的數(shù)據(jù)全部寫入磁盤
StringIO 和 BytesIO
StringIO
把str寫入StringIO:
from io import StringIO
# 創(chuàng)建一個(gè)StringIO
f = StringIO()
# 返回字符數(shù)
f.write('hello')
# 獲得寫入后的str
print(f.getvalue())
像讀文件一樣讀取StringIO:
from io import StringIO
f = StringIO('Hello!\nHi!\nGoodbye!')
while True:
s = f.readline()
if s == '':
break
print(s.strip())
BytesIO
from io import BytesIO
f = BytesIO()
# 返回字節(jié)數(shù)
f.write('中文'.encode('utf-8'))
print(f.getvalue())
from io import BytesIO
f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
f.read()
操作文件和目錄
Python 內(nèi)置的 os 模塊也可以直接調(diào)用操作系統(tǒng)提供的接口函數(shù)
import os
# 操作系統(tǒng)類型,如果是 posix,說明系統(tǒng)是Linux、Unix或Mac OS X,如果是nt,就是Windows系統(tǒng)
os.name
# 獲取詳細(xì)的系統(tǒng)信息,在Windows上不提供
os.uname()
# 獲取在操作系統(tǒng)中定義的環(huán)境變量
os.environ
# 獲取某個(gè)環(huán)境變量的值
os.environ.get('PATH')
os.environ.get('x', 'default')
操作文件和目錄
操作文件和目錄的函數(shù)一部分放在 os 模塊中,一部分放在 os.path 模塊中
# 查看當(dāng)前目錄的絕對路徑
os.path.abspath('.') ==> 'C:\\Users\\lianwx'
# 列出當(dāng)前路徑下的所有文件和子目錄
os.listdir('.')
# 判斷路徑下是否目錄
os.path.isdir('path')
# 判斷路徑下是否文件
os.path.isfile('path')
# 合并、拆分路徑的函數(shù)并不要求目錄和文件要真實(shí)存在,它們只對字符串進(jìn)行操作
# 在某個(gè)目錄下創(chuàng)建一個(gè)新目錄,首先把新目錄的完整路徑表示出來
os.path.join('C:\\Users\\lianwx', 'testdir')
# 拆分路徑
os.path.split('/Users/michael/testdir/file.txt') ==> ('/Users/michael/testdir', 'file.txt')
# 得到文件擴(kuò)展名
os.path.splitext('/path/to/file.txt') ==> ('/path/to/file', '.txt')
# 創(chuàng)建一個(gè)目錄
os.mkdir('/Users/michael/testdir')
# 刪掉一個(gè)目錄
os.rmdir('/Users/michael/testdir')
# 對文件重命名
os.rename('test.txt', 'test.py')
# 刪掉文件
os.remove('test.py')
# 復(fù)制文件
# 讀寫文件可以完成文件復(fù)制
# 使用 `shutil` 模塊的 `copyfile()` 函數(shù)
利用Python的特性來過濾文件
# 列出當(dāng)前目錄下的所有目錄
[x for x in os.listdir('.') if os.path.isdir(x)]
# 列出所有的.py文件
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
序列化
Python提供了 pickle 模塊來實(shí)現(xiàn)序列化
pickle.dumps() 方法把任意對象序列化成一個(gè) bytes
pickle.dump() 直接把對象序列化后寫入一個(gè) file-like Object
把一個(gè) dict 序列化并寫入文件:
import pickle
d = dict(name='Bob', age=20, score=88)
# 得到一個(gè)bytes,然后,就可以把這個(gè)bytes寫入文件
pickle.dumps(d)
f = open('dump.txt', 'wb')
# 直接把對象序列化后寫入一個(gè)file-like Object
pickle.dump(d, f)
f.close()
反序列化:
pickle.loads() 方法把 bytes 反序列化出對象
pickle.load() 方法從一個(gè)file-like Object中直接反序列化出對象
f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()
JSON
Python 內(nèi)置的 json 模塊提供了非常完善的 Python 對象到 JSON 格式的轉(zhuǎn)換
json.dumps() 方法返回一個(gè) str ,內(nèi)容就是標(biāo)準(zhǔn)的 JSON
json.dump() 方法可以直接把 JSON 寫入一個(gè) file-like Object
import json
d = dict(name='Bob', age=20, score=88)
json.dumps(d)
json.loads() 方法把JSON的字符串反序列化
json.load() 方法從file-like Object中讀取字符串并反序列化
json_str = '{"age": 20, "score": 88, "name": "Bob"}'
json.loads(json_str)
JSON進(jìn)階
序列化一個(gè) class
默認(rèn)情況下,dumps() 方法不知道如何將一個(gè)實(shí)例變?yōu)橐粋€(gè) JSON 的 {} 對象
dumps() 方法還提供了一大堆的可選參數(shù) ,其中參數(shù) default 接受一個(gè)函數(shù),函數(shù)可傳入一個(gè)對象,返回一個(gè) dict,作為轉(zhuǎn)換函數(shù)
通常class的實(shí)例都有一個(gè) __dict__ 屬性,它就是一個(gè) dict ,用來存儲實(shí)例變量。也有少數(shù)例外,比如定義了slots的class。
import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('Bob', 20, 88)
print(json.dumps(s, default=lambda obj: obj.__dict__))
把JSON反序列化為一個(gè) Student 對象實(shí)例,loads() 方法首先轉(zhuǎn)換出一個(gè) dict 對象,然后,我們傳入的 object_hook 函數(shù)負(fù)責(zé)把 dict 轉(zhuǎn)換為 Student 實(shí)例:
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
json_str = '{"age": 20, "score": 88, "name": "Bob"}'
print(json.loads(json_str, object_hook=dict2student))