這篇文章給大家分享的是有關(guān)python讀寫(xiě)文件操作的方法的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧。
成都創(chuàng)新互聯(lián)公司是專(zhuān)業(yè)的青白江網(wǎng)站建設(shè)公司,青白江接單;提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專(zhuān)業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行青白江網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專(zhuān)業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專(zhuān)業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
I/O操作概述
文件讀寫(xiě)實(shí)現(xiàn)原理與操作步驟
文件打開(kāi)模式
Python文件操作步驟示例
Python文件讀取相關(guān)方法
文件讀寫(xiě)與字符編碼
I/O在計(jì)算機(jī)中是指Input/Output,也就是Stream(流)的輸入和輸出。這里的輸入和輸出是相對(duì)于內(nèi)存來(lái)說(shuō)的,Input Stream(輸入流)是指數(shù)據(jù)從外(磁盤(pán)、網(wǎng)絡(luò))流進(jìn)內(nèi)存,Output Stream是數(shù)據(jù)從內(nèi)存流出到外面(磁盤(pán)、網(wǎng)絡(luò))。程序運(yùn)行時(shí),數(shù)據(jù)都是在內(nèi)存中駐留,由CPU這個(gè)超快的計(jì)算核心來(lái)執(zhí)行,涉及到數(shù)據(jù)交換的地方(通常是磁盤(pán)、網(wǎng)絡(luò)操作)就需要IO接口。
那么這個(gè)IO接口是由誰(shuí)提供呢?高級(jí)編程語(yǔ)言中的IO操作是如何實(shí)現(xiàn)的呢?
操作系統(tǒng)是個(gè)通用的軟件程序,其通用目的如下:
硬件驅(qū)動(dòng)
進(jìn)程管理
內(nèi)存管理
網(wǎng)絡(luò)管理
安全管理
I/O管理
操作系統(tǒng)屏蔽了底層硬件,向上提供通用接口。因此,操作I/O的能力是由操作系統(tǒng)的提供的,每一種編程語(yǔ)言都會(huì)把操作系統(tǒng)提供的低級(jí)C接口封裝起來(lái)供開(kāi)發(fā)者使用,Python也不例外。
文件讀寫(xiě)就是一種常見(jiàn)的IO操作。那么根據(jù)上面的描述,可以推斷python也應(yīng)該封裝操作系統(tǒng)的底層接口,直接提供了文件讀寫(xiě)相關(guān)的操作方法。事實(shí)上,也確實(shí)如此,而且Java、PHP等其他語(yǔ)言也是。
那么我們要操作的對(duì)象是什么呢?我們又如何獲取要操作的對(duì)象呢?
由于操作I/O的能力是由操作系統(tǒng)提供的,且現(xiàn)代操作系統(tǒng)不允許普通程序直接操作磁盤(pán),所以讀寫(xiě)文件時(shí)需要請(qǐng)求操作系統(tǒng)打開(kāi)一個(gè)對(duì)象(通常被稱(chēng)為文件描述符--file descriptor, 簡(jiǎn)稱(chēng)fd),這就是我們?cè)诔绦蛑幸僮鞯奈募?duì)象。
通常高級(jí)編程語(yǔ)言中會(huì)提供一個(gè)內(nèi)置的函數(shù),通過(guò)接收"文件路徑"以及“文件打開(kāi)模式”等參數(shù)來(lái)打開(kāi)一個(gè)文件對(duì)象,并返回該文件對(duì)象的文件描述符。因此通過(guò)這個(gè)函數(shù)我們就可以獲取要操作的文件對(duì)象了。這個(gè)內(nèi)置函數(shù)在Python中叫open(), 在PHP中叫fopen(),
不同的編程語(yǔ)言讀寫(xiě)文件的操作步驟大體都是一樣的,都分為以下幾個(gè)步驟:
1)打開(kāi)文件,獲取文件描述符2)操作文件描述符--讀/寫(xiě)3)關(guān)閉文件
只是不同的編程語(yǔ)言提供的讀寫(xiě)文件的api是不一樣的,有些提供的功能比較豐富,有些比較簡(jiǎn)陋。
需要注意的是:文件讀寫(xiě)操作完成后,應(yīng)該及時(shí)關(guān)閉。一方面,文件對(duì)象會(huì)占用操作系統(tǒng)的資源;另外一方面,操作系統(tǒng)對(duì)同一時(shí)間能打開(kāi)的文件描述符的數(shù)量是有限制的,在Linux操作系統(tǒng)上可以通過(guò)ulimit -n
來(lái)查看這個(gè)顯示數(shù)量。如果不及時(shí)關(guān)閉文件,還可能會(huì)造成數(shù)據(jù)丟失。因?yàn)槲覍?shù)據(jù)寫(xiě)入文件時(shí),操作系統(tǒng)不會(huì)立刻把數(shù)據(jù)寫(xiě)入磁盤(pán),而是先把數(shù)據(jù)放到內(nèi)存緩沖區(qū)異步寫(xiě)入磁盤(pán)。當(dāng)調(diào)用close方法時(shí),操作系統(tǒng)會(huì)保證把沒(méi)有寫(xiě)入磁盤(pán)的數(shù)據(jù)全部寫(xiě)到磁盤(pán)上,否則可能會(huì)丟失數(shù)據(jù)。
我們先來(lái)看下在Python、PHP和C語(yǔ)言中打開(kāi)文件的函數(shù)定義
# Python2open(name[, mode[, buffering]])# Python3open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
resource fopen ( string $filename , string $mode [, bool $use_include_path = false [, resource $context ]] )
int open(const char * pathname, int flags);
會(huì)發(fā)現(xiàn)以上3種編程語(yǔ)言內(nèi)置的打開(kāi)文件的方法接收的參數(shù)中,除了都包含一個(gè)“文件路徑名稱(chēng)”,還會(huì)包含一個(gè)mode參數(shù)(C語(yǔ)言的open函數(shù)中的flags參數(shù)作用相似)。這么mode參數(shù)定義的是打開(kāi)文件時(shí)的模式,常見(jiàn)的文件打開(kāi)模式有:只讀、只寫(xiě)、可讀可寫(xiě)、只追加。不同的編程語(yǔ)言中對(duì)文件打開(kāi)模式的定義有些微小的差別,我們來(lái)看下Python中的文件打開(kāi)模式有哪些。
文件打開(kāi)模式 | 描述 |
---|---|
r | 以只讀模式打開(kāi)文件,并將文件指針指向文件頭;如果文件不存在會(huì)報(bào)錯(cuò) |
w | 以只寫(xiě)模式打開(kāi)文件,并將文件指針指向文件頭;如果文件存在則將其內(nèi)容清空,如果文件不存在則創(chuàng)建 |
a | 以只追加可寫(xiě)模式打開(kāi)文件,并將文件指針指向文件尾部;如果文件不存在則創(chuàng)建 |
r+ | 在r的基礎(chǔ)上增加了可寫(xiě)功能 |
w+ | 在w的基礎(chǔ)上增加了可讀功能 |
a+ | 在a的基礎(chǔ)上增加了可讀功能 |
b | 讀寫(xiě)二進(jìn)制文件(默認(rèn)是t,表示文本),需要與上面幾種模式搭配使用,如ab,wb, ab, ab+(POSIX系統(tǒng),包括Linux都會(huì)忽略該字符) |
思考1:r+、w+和a+都可以實(shí)現(xiàn)對(duì)文件的讀寫(xiě),那么他們有什么區(qū)別呢?
r+會(huì)覆蓋當(dāng)前文件指針?biāo)谖恢玫淖址缭瓉?lái)文件內(nèi)容是"Hello,World",打開(kāi)文件后寫(xiě)入"hi"則文件內(nèi)容會(huì)變成"hillo, World"
w+與r+的不同是,w+在打開(kāi)文件時(shí)就會(huì)先將文件內(nèi)容清空,不知道它有什么用
a+與r+的不同是,a+只能寫(xiě)到文件末尾(無(wú)論當(dāng)前文件指針在哪里)
思考2:為什么要定義這些模式呢?為什么不能像我們用word打開(kāi)一篇文檔一樣既可以讀,又可以寫(xiě),還可修改呢?
關(guān)于這個(gè)問(wèn)題,我查了很多資料,也沒(méi)找到很權(quán)威的說(shuō)明。在跟同行朋友交流過(guò)程中,發(fā)現(xiàn)大家主要有兩種觀點(diǎn):
跟安全有關(guān),有這種觀點(diǎn)的大部分是做運(yùn)維的朋友,他們認(rèn)為這就像linux上的rwx(讀、寫(xiě)、執(zhí)行)權(quán)限。
跟操作系統(tǒng)內(nèi)核管理I/O的機(jī)制有關(guān),有這種觀點(diǎn)的大部分是做C開(kāi)發(fā)的,特別是與內(nèi)核相關(guān)的開(kāi)發(fā)人員。為了提高讀寫(xiě)速度,要寫(xiě)入磁盤(pán)的數(shù)據(jù)會(huì)先放進(jìn)內(nèi)存緩沖區(qū),之后再回寫(xiě)。由于可能會(huì)同時(shí)打開(kāi)很多文件,當(dāng)要回寫(xiě)數(shù)據(jù)時(shí),需要遍歷以打開(kāi)的文件判斷是否需要回寫(xiě)。他們認(rèn)為如果打開(kāi)文件時(shí)指定了讀寫(xiě)模式,那么需要回寫(xiě)時(shí),只要去查找以“可寫(xiě)模式”打開(kāi)的文件就可以了。
我們來(lái)讀取這樣一個(gè)文本文件:song.txt,該文件的字符編碼為utf-8。
匆匆那年我們 究竟說(shuō)了幾遍 再見(jiàn)之后再拖延 可惜誰(shuí)有沒(méi)有 愛(ài)過(guò)不是一場(chǎng) 七情上面的雄辯 匆匆那年我們 一時(shí)匆忙撂下 難以承受的諾言 只有等別人兌現(xiàn)
Python3實(shí)現(xiàn):
# 第一步:(以只讀模式)打開(kāi)文件f = open('song.txt', 'r', encoding='utf-8')# 第二步:讀取文件內(nèi)容print(f.read())# 第三步:關(guān)閉文件f.close()
這里說(shuō)下Python2的實(shí)現(xiàn)
# 第一步:(以只讀模式)打開(kāi)文件f = open('song.txt', 'r')# 第二步:讀取文件內(nèi)容print(f.read().decode('utf-8'))# 第三步:關(guān)閉文件f.close()
說(shuō)明:
Python3中已經(jīng)內(nèi)置對(duì)Unicode的支持,字符串str已經(jīng)是真正的Unicode字符串。也就是說(shuō)Python3中的文件讀取方法已經(jīng)自動(dòng)完成了解碼處理,因此無(wú)需再手動(dòng)進(jìn)行解碼,可以直接將讀取的文件中的內(nèi)容進(jìn)行打??;Python2中的字符串str是字節(jié)串,讀取文件得到的也是字節(jié)串,在打印之前應(yīng)該手動(dòng)將其解碼成Unicode字符串。關(guān)于這部分的說(shuō)明,可以參考之前這篇文章<<再談Python中的字符串與字符編碼>>。
在實(shí)現(xiàn)基本功能的前提下,考慮一些可能的意外因素。因?yàn)槲募x寫(xiě)時(shí)都有可能產(chǎn)生IO錯(cuò)誤(IOError),一旦出錯(cuò),后面包括f.close()在內(nèi)的所有代碼都不會(huì)執(zhí)行了。因此我們要保證文件無(wú)論如何都能被關(guān)閉。那么可以用try...finally來(lái)實(shí)現(xiàn),這實(shí)際上就是try...except..finally的簡(jiǎn)化版(我們只用Python3來(lái)進(jìn)行示例演示):
f = ''try: f = open('song.txt', 'r', encoding='utf-8') print(f.read()) num = 10 / 0finally: print('>>>>>>finally') if f: f.close()
輸出結(jié)果:
匆匆那年我們 究竟說(shuō)了幾遍 再見(jiàn)之后再拖延 可惜誰(shuí)有沒(méi)有 愛(ài)過(guò)不是一場(chǎng) 七情上面的雄辯 匆匆那年我們 一時(shí)匆忙撂下 難以承受的諾言 只有等別人兌現(xiàn)>>>>>>finally Traceback (most recent call last): File "<stdin>", line 4, in <module>ZeroDivisionError: division by zero
輸出結(jié)果說(shuō)明,盡管with代碼塊中出現(xiàn)了異常,但是”>>>>>>finally“ 信息還是被打印了,說(shuō)明finally代碼塊被執(zhí)行,即文件關(guān)閉操作被執(zhí)行。但是結(jié)果中錯(cuò)誤信息還是被輸出了,因此還是建議用一個(gè)完成的try...except...finally語(yǔ)句對(duì)異常信息進(jìn)行捕獲和處理。
為了避免忘記或者為了避免每次都要手動(dòng)關(guān)閉文件,我們可以使用with語(yǔ)句(一種語(yǔ)法糖,語(yǔ)法糖語(yǔ)句通常是為了簡(jiǎn)化某些操作而設(shè)計(jì)的)。with語(yǔ)句會(huì)在其代碼塊執(zhí)行完畢之后自動(dòng)關(guān)閉文件。因此我們可以這樣來(lái)改寫(xiě)上面的程序:
with open('song.txt', 'r', encoding='utf-8') as f: print(f.read())print(f.closed)
輸出結(jié)果:
匆匆那年我們 究竟說(shuō)了幾遍 再見(jiàn)之后再拖延可惜誰(shuí)有沒(méi)有 愛(ài)過(guò)不是一場(chǎng) 七情上面的雄辯匆匆那年我們 一時(shí)匆忙撂下 難以承受的諾言只有等別人兌現(xiàn)True
是不是變得簡(jiǎn)介多了,代碼結(jié)構(gòu)也比較清晰了。with之后打印的f.closed屬性值為T(mén)rue,說(shuō)明文件確實(shí)被關(guān)閉了。
思考:
with語(yǔ)句會(huì)幫我們自動(dòng)處理異常信息嗎?
要回答這個(gè)問(wèn)題就要提到“上下文管理器” 和 with語(yǔ)句的工作流程。
with語(yǔ)句不僅僅可以用于文件操作,它實(shí)際上是一個(gè)很通用的結(jié)構(gòu),允許使用所謂的上下文管理器(context manager)。上下文管理器是一種支持__enter__()和__exit__()這兩個(gè)方法的對(duì)象。__enter__()方法不帶任何參數(shù),它在進(jìn)入with語(yǔ)句塊的時(shí)候被調(diào)用,該方法的返回值會(huì)被賦值給as關(guān)鍵字之后的變量。__exit__()方法帶有3個(gè)參數(shù):type(異常類(lèi)型), value(異常信息), trace(異常棧),當(dāng)with語(yǔ)句的代碼塊執(zhí)行完畢或執(zhí)行過(guò)程中因?yàn)楫惓6唤K止都會(huì)調(diào)用__exit__()方法。正常退出時(shí)該方法的3個(gè)參數(shù)都為None,異常退出時(shí)該方法的3個(gè)參數(shù)會(huì)被分別賦值。如果__exit__()方法返回值(真值測(cè)試結(jié)果)為T(mén)rue則表示異常已經(jīng)被處理,命令執(zhí)行結(jié)果中就不會(huì)拋出異常信息了;反之,如果__exit__()方法返回值(真值測(cè)試結(jié)果)為False,則表示異常沒(méi)有被處理并且會(huì)向外拋出該異常。
現(xiàn)在我們應(yīng)該明白了,異常信息會(huì)不會(huì)被處理是由with后的語(yǔ)句返回對(duì)象的__exit__()方法決定的。文件可以被用作上下文管理器。它的__enter__方法返回文件對(duì)象本身,__exit__方法會(huì)關(guān)閉文件并返回None。我們看下file類(lèi)中關(guān)于這兩個(gè)方法的實(shí)現(xiàn):
def __enter__(self): # real signature unknown; restored from __doc__ """ __enter__() -> self. """ return self def __exit__(self, *excinfo): # real signature unknown; restored from __doc__ """ __exit__(*excinfo) -> None. Closes the file. """ pass
可見(jiàn),file類(lèi)的__exit__()方法的返回值為None,None的真值測(cè)試結(jié)果為False,因此用于文件讀寫(xiě)的with語(yǔ)句代碼塊中的異常信息還是會(huì)被拋出來(lái),需要我們自己去捕獲并處理。
with open('song.txt', 'r', encoding='utf-8') as f: print(f.read()) num = 10 / 0
輸出結(jié)果:
匆匆那年我們 究竟說(shuō)了幾遍 再見(jiàn)之后再拖延 可惜誰(shuí)有沒(méi)有 愛(ài)過(guò)不是一場(chǎng) 七情上面的雄辯 匆匆那年我們 一時(shí)匆忙撂下 難以承受的諾言 只有等別人兌現(xiàn) Traceback (most recent call last): File "<stdin>", line 3, in <module> ZeroDivisionError: division by zero
注意:上面所說(shuō)的__exit__()方法返回值(真值測(cè)試結(jié)果)為T(mén)rue則表示異常已經(jīng)被處理,指的是with代碼塊中出現(xiàn)的異常。它對(duì)于with關(guān)鍵字之后的代碼中出現(xiàn)的異常是不起作用的,因?yàn)檫€沒(méi)有進(jìn)入上下文管理器就已經(jīng)發(fā)生異常了。因此,無(wú)論如何,還是建議在必要的時(shí)候在with語(yǔ)句外面套上一層try...except來(lái)捕獲和處理異常。
有關(guān)“上下文管理器”這個(gè)強(qiáng)大且高級(jí)的特性的更多信息,請(qǐng)參看Python參考手冊(cè)中的上下文管理器部分。或者可以在Python庫(kù)參考中查看上下文管理器和contextlib部分。
我們知道,對(duì)文件的讀取操作需要將文件中的數(shù)據(jù)加載到內(nèi)存中,而上面所用到的read()方法會(huì)一次性把文件中所有的內(nèi)容全部加載到內(nèi)存中。這明顯是不合理的,當(dāng)遇到一個(gè)幾個(gè)G的的文件時(shí),必然會(huì)耗光機(jī)器的內(nèi)存。這里我們來(lái)介紹下Python中讀取文件的相關(guān)方法:
方法 | 描述 |
---|---|
read() | 一次讀取文件所有內(nèi)容,返回一個(gè)str |
read(size) | 每次最多讀取指定長(zhǎng)度的內(nèi)容,返回一個(gè)str;在Python2中size指定的是字節(jié)長(zhǎng)度,在Python3中size指定的是字符長(zhǎng)度 |
readlines() | 一次讀取文件所有內(nèi)容,按行返回一個(gè)list |
readline() | 每次只讀取一行內(nèi)容 |
此外,還要兩個(gè)與文件指針位置相關(guān)的方法
方法 | 描述 |
---|---|
seek(n) | 將文件指針移動(dòng)到指定字節(jié)的位置 |
tell() | 獲取當(dāng)前文件指針?biāo)谧止?jié)位置 |
下面來(lái)看下操作實(shí)例
with open('song.txt', 'r') as f: print(f.read(12).decode('utf-8'))
輸出結(jié)果:
匆匆那年
結(jié)果說(shuō)明:Python2中read(size)方法的size參數(shù)指定的要讀取的字節(jié)數(shù),而song.txt文件是UTF-8編碼的內(nèi)容,一個(gè)漢字占3個(gè)字節(jié),因此12個(gè)字節(jié)剛好是4個(gè)漢字。
with open('song.txt', 'r', encoding='utf-8') as f: print(f.read(12))
輸出結(jié)果:
匆匆那年我們 究竟說(shuō)
結(jié)果說(shuō)明:Python3中read(size)方法的size參數(shù)指定的要讀取的字符數(shù),這與文件的字符編碼無(wú)關(guān),就是返回12個(gè)字符。
with open('song.txt', 'r', encoding='utf-8') as f: print(f.readline())
with open('song.txt', 'r') as f: print(f.readline().decode('utf-8'))
輸出結(jié)果都一樣:
匆匆那年我們 究竟說(shuō)了幾遍 再見(jiàn)之后再拖延
這里我們只以Python3來(lái)進(jìn)行實(shí)例操作,Python2僅僅是需要在讀取到內(nèi)容后進(jìn)行手動(dòng)解碼而已,上面已經(jīng)有示例。
with open('song.txt', 'r', encoding='utf-8') as f: for line in f.readlines(): print(line)
輸出結(jié)果:
匆匆那年我們 究竟說(shuō)了幾遍 再見(jiàn)之后再拖延 可惜誰(shuí)有沒(méi)有 愛(ài)過(guò)不是一場(chǎng) 七情上面的雄辯 匆匆那年我們 一時(shí)匆忙撂下 難以承受的諾言 只有等別人兌現(xiàn)
這種方式的缺點(diǎn)與read()方法是一樣的,都是會(huì)消耗大量的內(nèi)存空間。
with open('song.txt', 'r', encoding='utf-8', newline='') as f: for line in f: print(line)
輸出結(jié)果:
匆匆那年我們 究竟說(shuō)了幾遍 再見(jiàn)之后再拖延 可惜誰(shuí)有沒(méi)有 愛(ài)過(guò)不是一場(chǎng) 七情上面的雄辯 匆匆那年我們 一時(shí)匆忙撂下 難以承受的諾言 只有等別人兌現(xiàn)
另外,發(fā)現(xiàn)上面的輸出結(jié)果中行與行之間多了一個(gè)空行。這是因?yàn)槲募恳恍械哪J(rèn)都有換行符,而print()方法也會(huì)輸出換行,因此就多了一個(gè)空行。去掉空行也比較簡(jiǎn)單:可以用line.rstrip()
去除字符串右邊的換行符,也可以通過(guò)print(line, end='')避免print方法造成的換行。
file類(lèi)的其他方法:
方法 | 描述 |
---|---|
flush() | 刷新緩沖區(qū)數(shù)據(jù),將緩沖區(qū)中的數(shù)據(jù)立刻寫(xiě)入文件 |
next() | 返回文件下一行,這個(gè)方法也是file對(duì)象實(shí)例可以被當(dāng)做迭代器使用的原因 |
truncate([size]) | 截取文件中指定字節(jié)數(shù)的內(nèi)容,并覆蓋保存到文件中,如果不指定size參數(shù)則文件將被清空; Python2無(wú)返回值,Python3返回新文件的內(nèi)容字節(jié)數(shù) |
write(str) | 將字符串寫(xiě)入文件,沒(méi)有返回值 |
writelines(sequence) | 向文件寫(xiě)入一個(gè)字符串或一個(gè)字符串列表,如果字符串列表中的元素需要換行要自己加入換行符 |
fileno() | 返回一個(gè)整型的文件描述符,可以用于一些底層IO操作上(如,os模塊的read方法) |
isatty() | 判斷文件是否被連接到一個(gè)虛擬終端,是則返回True,否則返回False |
前面已經(jīng)寫(xiě)過(guò)一篇介紹Python中字符編碼的相關(guān)文件<<再談Python中的字符串與字符編碼>> 里面花了很大的篇幅介紹Python中字符串與字符編碼的關(guān)系以及轉(zhuǎn)換過(guò)程。其中談到過(guò)兩個(gè)指定的字符編碼的地方,及其作用:
PyCharm等IDE開(kāi)發(fā)工具指定的項(xiàng)目工程和文件的字符編碼:它的主要作用是告訴Pycharm等IDE開(kāi)發(fā)工具保存文件時(shí)應(yīng)該將字符轉(zhuǎn)換為怎樣的字節(jié)表示形式,以及打開(kāi)并展示文件內(nèi)容時(shí)應(yīng)該以什么字符編碼將字節(jié)碼轉(zhuǎn)換為人類(lèi)可識(shí)別的字符。
Python源代碼文件頭部指定的字符編碼,如*-* coding:utf-8 -*-
:它的主要作用是告訴Python解釋器當(dāng)前python代碼文件保存時(shí)所使用的字符編碼,Python解釋器在執(zhí)行代碼之前,需要先從磁盤(pán)讀取該代碼文件中的字節(jié)然后通過(guò)這里指定的字符編碼將其解碼為unicode字符。Python解釋器執(zhí)行Python代碼的過(guò)程與IDE開(kāi)發(fā)工具是沒(méi)有什么關(guān)聯(lián)性的。
那么這里為什么又要談起字符編碼的問(wèn)題呢?
或者換個(gè)問(wèn)法,既然從上面已經(jīng)指定了字符編碼,為什么對(duì)文件進(jìn)行讀寫(xiě)時(shí)還要指定字符編碼呢?從前面的描述可以看出:上面兩個(gè)地方指定的是Python代碼文件的字符編碼,是給Python解釋器和Pycharm等程序軟件用的;而被讀寫(xiě)文件的字符編碼與Python代碼文件的字符編碼沒(méi)有必然聯(lián)系,讀寫(xiě)文件時(shí)指定的字符編碼是給我們寫(xiě)的程序軟件用的。這是不同的主體和過(guò)程,希望我說(shuō)明白了。
讀寫(xiě)文件時(shí)怎樣指定字符編碼呢?
上面解釋了讀寫(xiě)文件為什么要指定字符編碼,這里要說(shuō)下怎樣指定字符編碼(其實(shí)這里主要討論是讀取外部數(shù)據(jù)時(shí)的情形)。這個(gè)問(wèn)題其實(shí)在上面的文件讀取示例中已經(jīng)使用過(guò)了,這里我們?cè)僭敿?xì)的說(shuō)一下。
首先,再次看一下Python2和Python3中open函數(shù)的定義:
# Python2open(name[, mode[, buffering]])# Python3open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
可以看到,Python3的open函數(shù)中多了幾個(gè)參數(shù),其中包括一個(gè)encoding參數(shù)。是的,這個(gè)encoding就是用來(lái)指定被操作文件的字符編碼的。
# 讀操作with open('song.txt', 'r', encoding='utf-8') as f: print(f.read())# 寫(xiě)操作with open('song.txt', 'w', encoding='utf-8') as f: print(f.write('你好'))
那么Python2中怎樣指定呢?Python2中的對(duì)文件的read和write操作都是字節(jié),也就說(shuō)Python2中文件的read相關(guān)方法讀取的是字節(jié)串(如果包含中文字符,會(huì)發(fā)現(xiàn)len()方法的結(jié)果不等于讀取到的字符個(gè)數(shù),而是字節(jié)數(shù))。如果我們要得到 正確的字符串,需要手動(dòng)將讀取到的結(jié)果decode(解碼)為字符串;相反,要以特定的字符編碼保存要寫(xiě)入的數(shù)據(jù)時(shí),需要手動(dòng)encode(編碼)為字節(jié)串。這個(gè)encode()和decode()函數(shù)可以接收一個(gè)字符編碼參數(shù)。Python3中read和write操作的都是字符串,實(shí)際上是Python解釋器幫我們自動(dòng)完成了寫(xiě)入時(shí)的encode(編碼)和讀取時(shí)的decode(解碼)操作,因此我們只需要在打開(kāi)文件(open函數(shù))時(shí)指定字符編碼就可以了。
# 讀操作with open('song.txt', 'r') as f: print(f.read().decode('utf-8')) # 寫(xiě)操作with open('song2.txt', 'w') as f: # f.write(u'你好'.encode('utf-8')) # f.write('你好'.decode('utf-8').encode('utf-8')) f.write('你好')
Python3中open函數(shù)的encoding參數(shù)顯然是可以不指定的,這時(shí)候就會(huì)用一個(gè)“默認(rèn)字符編碼”。
看下Python3中open函數(shù)文檔對(duì)encoding參數(shù)的說(shuō)明:
encoding is the name of the encoding used to decode or encode thefile. This should only be used in text mode. The default encoding isplatform dependent, but any encoding supported by Python can be passed. See the codecs module for the list of supported encodings.
也就是說(shuō),encoding參數(shù)的默認(rèn)值是與平臺(tái)有關(guān)的,比如Window上默認(rèn)字符編碼為GBK,Linux上默認(rèn)字符編碼為UTF-8。
而對(duì)于Python2來(lái)說(shuō),在進(jìn)行文件寫(xiě)操作時(shí),字節(jié)會(huì)被直接保存;在進(jìn)行文件讀操作時(shí),如果不手動(dòng)進(jìn)行來(lái)decode操作自然也就用不著默認(rèn)字符編碼了。但是這時(shí)候在不同的字符終端打印的時(shí)候,會(huì)用當(dāng)前平臺(tái)的字符編碼自動(dòng)將字節(jié)解碼為字符,此時(shí)可能會(huì)出現(xiàn)亂碼。如song.txt文件時(shí)UTF-8編碼的,在windows(字符編碼為GBK)的命令行終端進(jìn)行如下操作就會(huì)出現(xiàn)亂碼:
>>> with open('song.txt', 'r') as f: ... print(f.read()) ... 鍖嗗寙閭e勾鎴戜滑 絀剁珶璇翠簡(jiǎn)鍑犻亶 鍐嶈涔嬪悗鍐嶆嫋寤? 鍙儨璋佹湁娌℃湁 鐖辮繃涓嶆槸涓€鍦?涓冩儏涓婇潰鐨勯泟杈? 鍖嗗寙閭e勾鎴戜滑 涓€鏃跺寙蹇欐拏涓?闅句互鎵垮彈鐨勮璦€ 鍙湁絳夊埆浜哄厬鐜
我們應(yīng)該盡可能的獲取被操作文件的字符編碼,并明確指定encoding參數(shù)的值。
感謝各位的閱讀!關(guān)于python讀寫(xiě)文件操作的方法就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
網(wǎng)站題目:python讀寫(xiě)文件操作的方法
網(wǎng)站URL:http://chinadenli.net/article30/gisgso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、網(wǎng)站營(yíng)銷(xiāo)、企業(yè)網(wǎng)站制作、服務(wù)器托管、做網(wǎng)站、品牌網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)