本篇文章將介紹python里面的I/O編程视事。更多內(nèi)容請(qǐng)參考:python學(xué)習(xí)指南
I/O編程
讀寫文件時(shí)最常見的IO操作礼旅。Python內(nèi)置了讀寫文件的函數(shù),用法和C是兼容的菲嘴。
讀寫文件前龄坪,我們必須了解一下健田,在磁盤上讀寫文件的功能都是由操作系統(tǒng)提供的妓局,現(xiàn)代操作系統(tǒng)不允許普通的程序直接操作磁盤呈宇,所以,讀寫文件就是請(qǐng)求操作系統(tǒng)打開一個(gè)文件對(duì)象(通常稱為文件描述符)存炮,然后穆桂,通過操作系統(tǒng)提供的接口從這個(gè)文件對(duì)象中讀取數(shù)據(jù)(讀文件),或者把數(shù)據(jù)寫入這個(gè)文件對(duì)象(寫文件)。
在I/O編程中享完,Stream(流)是一種重要的概念驼侠,分為輸入流(Input Stream)和輸入流(Output Stream)倒源。我們可以把流理解為一個(gè)水管句狼,一個(gè)負(fù)責(zé)輸入腻菇,一個(gè)負(fù)責(zé)輸出,這樣讀寫就可以實(shí)現(xiàn)同步糖耸。
文件讀寫
打開文件
讀寫文件是最常見的IO操作嘉竟。Python內(nèi)置了讀寫文件的函數(shù)舍扰,方便了文件的IO操作希坚。
文件讀寫之前需要打開文件,確定文件的讀寫模式个束。open函數(shù)用來打開文件播急,語法如下:
open(name[, mode[, buffering]])
open函數(shù)使用一個(gè)文件作為唯一的強(qiáng)制參數(shù)售睹,然后返回一個(gè)文件對(duì)象昌妹。模式(mode)和緩沖區(qū)(buffering)參數(shù)都是可選的,默認(rèn)模式是讀模式烂叔,默認(rèn)緩沖區(qū)是無蒜鸡。
如果文件不存在,open()
函數(shù)就會(huì)拋出一個(gè)IOError
錯(cuò)誤叶沛,并且給出錯(cuò)誤碼和詳細(xì)的信息告訴你文件不存在:
>>>f = open("user/michael/notfound.txt", "r")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'user/michael/notfound.txt'
文件模式
open函數(shù)打開中的mode參數(shù)灰署,通過改變mode參數(shù)可以實(shí)現(xiàn)對(duì)文件的不同操作
值 | 功能描述 |
---|---|
'r' | 讀模式 |
'w' | 寫模式 |
'a' | 追加模式 |
'b' | 二進(jìn)制模式(可添加到其它模式中使用) |
'+' | 讀/寫模式(可添加到其它模式中使用) |
一般處理文本文件時(shí)溉箕,是用不到'b'參數(shù)的肴茄,但如果處理其它類型的文件(二進(jìn)制文件)独郎,比如mp3或者圖形氓癌,就應(yīng)該在模式中加上'b',這在爬蟲中處理媒體文件很常用贪婉。
文件緩沖區(qū)
open
函數(shù)中第三個(gè)可選參數(shù)buffering
控制著文件的緩沖疲迂。如果參數(shù)是0尤蒿,I/O操作就是無緩沖的幅垮,直接將數(shù)據(jù)寫到硬盤上;如果參數(shù)是1讳侨,I/O操作就是有緩沖的,數(shù)據(jù)先寫入到內(nèi)存里跨跨,只有使用flush
函數(shù)或者close
函數(shù)才會(huì)將數(shù)據(jù)更新到硬盤勇婴;如果參數(shù)大于1的數(shù)據(jù)則代表著緩沖區(qū)的大小(單位是字節(jié))咆耿,-1(或者任何負(fù)數(shù))代表使用默認(rèn)緩沖區(qū)的大小。
文件讀取
文件讀取主要分為按字節(jié)讀取和按行讀取愧驱,經(jīng)常用到的方法有read()
椭盏、readline()
掏颊、readlines()
、close()
盆偿。
>>>f = open(r"c:\\text\\xiaoqi.txt", "r")
>>>f.read() #調(diào)用read()方法可以一次性地將文件內(nèi)容全部讀到內(nèi)存中
'xiaoqi'
f.close() #文件讀取完成后必須關(guān)閉
由于文件操作可能會(huì)出現(xiàn)IO異常事扭,一旦出現(xiàn)IO異常求橄,后面的close()
方法就不會(huì)調(diào)用罐农。所以為了保證程序的健壯性涵亏,我們需要使用try...finally來實(shí)現(xiàn)溯乒。
try
f = open(r'c:\\text\\xiaoqi.txt')
print(f.read())
finally:
if f:
f.close()
Python提供了一種更加簡(jiǎn)單有趣的寫法裆悄,使用with語句來替代try...finally
代碼塊和close()
方法
with open(r'c:\text\xiaoqi.txt') as fileReader:
print(fileReader.read())
利用read()
一次將文件內(nèi)容讀到內(nèi)存,但是如果文件過大或南,將會(huì)出現(xiàn)內(nèi)存不足的問題采够。一般對(duì)于大文件蹬癌,可以重復(fù)調(diào)用read(size)
方法虹茶,一次最多讀取size個(gè)字節(jié)蝴罪。如果文件是文本文件要门,Python提供了更加合理的做法欢搜,調(diào)用了readline()
可以每次讀取一行內(nèi)容撑毛,調(diào)用readlines()
一次讀取所有內(nèi)容并按行返回列表唧领。大家可以根據(jù)自己的具體需求采取不同的讀取方式:
- 小文件可以直接采取read()方法讀到內(nèi)存;
- 大文件更加安全的方法是連續(xù)調(diào)用read(size);
- 面對(duì)于配置文件等文本文件斩个,使用readlines()方法更加合理
with open(r'c:\\text\\ss.txt') as fileReader:
for line in fileReader.readlines():
print(line.strip())
文件寫入
寫文件和讀文件是一樣的受啥,唯一的區(qū)別實(shí)在調(diào)用open方法時(shí)鸽心,傳入標(biāo)識(shí)符w
或者wb
表示寫入文本文件或者寫入二進(jìn)制文件顽频,示例如下:
f = open(r'c:\text\qie.txt', 'w')
f.write('xiaoqi')
f.close()
我們可以反復(fù)調(diào)用write()
方法寫入文件太闺,最后必須使用close()
方法來關(guān)閉文件糯景。使用write()
方法的時(shí)候,操作系統(tǒng)不是立即將數(shù)據(jù)寫入文件中的省骂,而是先寫入內(nèi)存中緩存起來蟀淮,等到空閑時(shí)候再寫入文件中,最后使用close()
方法就將數(shù)據(jù)完整地寫入文件中了钞澳。當(dāng)然也可以使用f.flush()
方法怠惶,不斷地將數(shù)據(jù)立即寫入文件中,最后使用close()
方法來關(guān)閉文件轧粟。和讀文件同樣道理策治,文件操作中可能會(huì)出現(xiàn)IO異常,所以還是推薦使用with語句:
with open(r'c:\text\csd.txt', 'w') as fileWriter:
fileWriter.write('sdfs')
file-like Object
像open()
函數(shù)返回的這種有個(gè)read()
方法的對(duì)象兰吟,在Python中統(tǒng)稱為file-like Object,除了file外,還可以是內(nèi)存的字節(jié)流,網(wǎng)路流料按,自定義流等。file-like Object不要求從特定類繼承,只要寫個(gè)read()
方法就行。
StringIO
就是內(nèi)存中創(chuàng)建的file-like Object,常用作臨時(shí)緩沖逃贝。
二進(jìn)制文件
一般我們用open()
讀取的是文本文件,并且是UTF-8
編碼的文本文件。要讀取二進(jìn)制文件卓起,比如圖片奕筐、視頻,就需要用rb
模式打開文件:
f = open("/Users/michael/test.jpg", 'rb')
f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六進(jìn)制表示的字節(jié)
字符編碼
要讀取非UTF-8編碼的文本文件,需要給open()
函數(shù)傳入encoding
參數(shù),例如,讀取GBK編碼的文件:
f = open("/Users/michael/gbk.text", "r", encoding="gbk")
f.read()
'測(cè)試'
遇到有些編碼不規(guī)范的文件,你可能會(huì)遇到UnicodeDecodeError遗座,因?yàn)樵谖谋疚募锌赡軍A雜了一些非法編碼的字符碎绎。遇到這種情況奸晴,open()
函數(shù)還接收一個(gè)errors
參數(shù)代箭,表示如果遇到編碼錯(cuò)誤后如何處理极景。最簡(jiǎn)單的方式是直接忽略:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
StringIO
很多時(shí)候,數(shù)據(jù)讀寫并不一定是文件也可以在內(nèi)幕才能中讀寫。
StringIO顧名思義就是在內(nèi)存中讀寫str。
要把str寫入StringIO,我們需要先創(chuàng)建一個(gè)StringIO,然后睛竣,像文件一樣寫入即可:
>>>from io import StringIO
>>>f = StringIO()
>>>f.write('hello')
5
>>>f.write(" ")
1
>>>f.write("world!")
6
>>>print(f.getvalue()) #getvalue()方法用于獲得寫入后的str
'hello world!'
要讀取StringIO,可以用一個(gè)str初始哈StringIO,然后猖吴,像讀文件一樣讀裙睬:
>>>from io import StringIO
>>>f = StringIO("Hello!\nGoodbye!")
>>>while True:
s = f.readline()
if s == " ":
break
print(s.strip())
Hello!
Hi!
Goodbye
BytesIO
StringIO操作的只能是str,如果要操作二進(jìn)制數(shù)據(jù)矾削,就需要使用ByetsIO.
BytesIO實(shí)現(xiàn)了在內(nèi)存中讀寫bytes,我們創(chuàng)建了一個(gè)BytesIO,然后寫入一些bytes
>>>from io import BytesIO
>>>f = BytesIO()
>>>f.write('中文'.encode("utf-8"))
6
>>>print(f.getvalues)
b'\xe4\xb8\xad\xe6\x96\x87'
注意:寫入的不是str,而是經(jīng)過UTF-8編碼的bytes吝梅。
和StringIO類似做瞪,可以用一個(gè)bytes初始化BytesIO,然后牍帚,像讀文件一樣讀取:
>>>from io import BytesIO
>>>f = BytesIO(b'\xe4\xb8\xad\xa6\x96\x87')
>>>f.read()
b'\xe4\xb8\xad\xe6\x96\x87'