python——文件讀寫與異常處理

文件

在實際開發(fā)中,常常需要對程序中的數(shù)據(jù)進行持久化操作频伤,而實現(xiàn)數(shù)據(jù)持久化最直接簡單的方式就是將數(shù)據(jù)保存到文件中恳谎。說到“文件”這個詞,可能需要先科普一下關(guān)于文件系統(tǒng)的知識憋肖,對于這個概念因痛,維基百科上給出了很好的詮釋,這里不再浪費筆墨岸更。

在Python中實現(xiàn)文件的讀寫操作其實非常簡單鸵膏,通過Python內(nèi)置的open函數(shù),我們可以指定文件名怎炊、操作模式谭企、編碼信息等來獲得操作文件的對象,接下來就可以對文件進行讀寫操作了评肆。這里所說的操作模式是指要打開什么樣的文件(字符文件還是二進制文件)以及做什么樣的操作(讀债查、寫還是追加),具體的如下表所示瓜挽。


下面這張圖來自于菜鳥教程網(wǎng)站盹廷,它展示了如果根據(jù)應用程序的需要來設(shè)置操作模式。

讀寫文本文件

讀取文本文件時久橙,需要在使用open函數(shù)時指定好帶路徑的文件名(可以使用相對路徑或絕對路徑)并將文件模式設(shè)置為'r'(如果不指定俄占,默認值也是'r')管怠,然后通過encoding參數(shù)指定編碼(如果不指定,默認值是None缸榄,那么在讀取文件時使用的是操作系統(tǒng)默認的編碼)渤弛,如果不能保證保存文件時使用的編碼方式與encoding參數(shù)指定的編碼方式是一致的,那么就可能因無法解碼字符而導致讀取失敗甚带。下面的例子演示了如何讀取一個純文本文件她肯。

def main():
    f = open('致橡樹.txt', 'r', encoding='utf-8')
    print(f.read())
    f.close()


if __name__ == '__main__':
    main()

異常處理

請注意上面的代碼,如果open函數(shù)指定的文件并不存在或者無法打開鹰贵,那么將引發(fā)異常狀況導致程序崩潰辕宏。為了讓代碼有一定的健壯性和容錯性,我們可以使用Python的異常機制對可能在運行時發(fā)生狀況的代碼進行適當?shù)漠惓L幚砝常缦滤尽?/p>

def main():
    f = None
    try:
        f = open('致橡樹.txt', 'r', encoding='utf-8')
        print(f.read())
    except FileNotFoundError:
        print('無法打開指定的文件!')
    except LookupError:
        print('指定了未知的編碼!')
    except UnicodeDecodeError:
        print('讀取文件時解碼錯誤!')
    finally:
        if f:
            f.close()


if __name__ == '__main__':
    main()

在Python中,我們可以將那些在運行時可能會出現(xiàn)狀況的代碼放在try代碼塊中凄鼻,在try代碼塊的后面可以跟上一個或多個except來捕獲可能出現(xiàn)的異常狀況腊瑟。例如在上面讀取文件的過程中,文件找不到會引發(fā)FileNotFoundError块蚌,指定了未知的編碼會引發(fā)LookupError闰非,而如果讀取文件時無法按指定方式解碼會引發(fā)UnicodeDecodeError,我們在try后面跟上了三個except分別處理這三種不同的異常狀況峭范。最后我們使用finally代碼塊來關(guān)閉打開的文件财松,釋放掉程序中獲取的外部資源,由于finally塊的代碼不論程序正常還是異常都會執(zhí)行到(甚至是調(diào)用了sys模塊的exit函數(shù)退出Python環(huán)境纱控,finally塊都會被執(zhí)行辆毡,因為exit函數(shù)實質(zhì)上是引發(fā)了SystemExit異常),因此我們通常把finally塊稱為“總是執(zhí)行代碼塊”甜害,它最適合用來做釋放外部資源的操作舶掖。如果不愿意在finally代碼塊中關(guān)閉文件對象釋放資源,也可以使用上下文語法尔店,通過with關(guān)鍵字指定文件對象的上下文環(huán)境并在離開上下文環(huán)境時自動釋放文件資源眨攘,代碼如下所示。

def main():
    try:
        with open('致橡樹.txt', 'r', encoding='utf-8') as f:
            print(f.read())
    except FileNotFoundError:
        print('無法打開指定的文件!')
    except LookupError:
        print('指定了未知的編碼!')
    except UnicodeDecodeError:
        print('讀取文件時解碼錯誤!')


if __name__ == '__main__':
    main()

除了使用文件對象的read方法讀取文件之外嚣州,還可以使用for-in循環(huán)逐行讀取或者用readlines方法將文件按行讀取到一個列表容器中鲫售,代碼如下所示。

import time


def main():
    # 一次性讀取整個文件內(nèi)容
    with open('致橡樹.txt', 'r', encoding='utf-8') as f:
        print(f.read())

    # 通過for-in循環(huán)逐行讀取
    with open('致橡樹.txt', mode='r') as f:
        for line in f:
            print(line, end='')
            time.sleep(0.5)
    print()

    # 讀取文件按行讀取到列表中
    with open('致橡樹.txt') as f:
        lines = f.readlines()
    print(lines)


if __name__ == '__main__':
    main()

要將文本信息寫入文件文件也非常簡單该肴,在使用open函數(shù)時指定好文件名并將文件模式設(shè)置為'w'即可情竹。注意如果需要對文件內(nèi)容進行追加式寫入,應該將模式設(shè)置為'a'沙庐。如果要寫入的文件不存在會自動創(chuàng)建文件而不是引發(fā)異常鲤妥。下面的例子演示了如何將19999直接的素數(shù)分別寫入三個文件中(199之間的素數(shù)保存在a.txt中佳吞,100999之間的素數(shù)保存在b.txt中,10009999之間的素數(shù)保存在c.txt中)棉安。

from math import sqrt


def is_prime(n):
    """判斷素數(shù)的函數(shù)"""
    assert n > 0
    for factor in range(2, int(sqrt(n)) + 1):
        if n % factor == 0:
            return False
    return True if n != 1 else False


def main():
    filenames = ('a.txt', 'b.txt', 'c.txt')
    fs_list = []
    try:
        for filename in filenames:
            fs_list.append(open(filename, 'w', encoding='utf-8'))
        for number in range(1, 10000):
            if is_prime(number):
                if number < 100:
                    fs_list[0].write(str(number) + '\n')
                elif number < 1000:
                    fs_list[1].write(str(number) + '\n')
                else:
                    fs_list[2].write(str(number) + '\n')
    except IOError as ex:
        print(ex)
        print('寫文件時發(fā)生錯誤!')
    finally:
        for fs in fs_list:
            fs.close()
    print('操作完成!')


if __name__ == '__main__':
    main()

讀寫二進制文件

知道了如何讀寫文本文件要讀寫二進制文件也就很簡單了底扳,下面的代碼實現(xiàn)了復制圖片文件的功能。

def main():
    try:
        with open('guido.jpg', 'rb') as fs1:
            data = fs1.read()
            print(type(data))  # <class 'bytes'>
        with open('吉多.jpg', 'wb') as fs2:
            fs2.write(data)
    except FileNotFoundError as e:
        print('指定的文件無法打開.')
    except IOError as e:
        print('讀寫文件時出現(xiàn)錯誤.')
    print('程序執(zhí)行結(jié)束.')


if __name__ == '__main__':
    main()

讀寫JSON文件

通過上面的講解贡耽,我們已經(jīng)知道如何將文本數(shù)據(jù)和二進制數(shù)據(jù)保存到文件中衷模,那么這里還有一個問題,如果希望把一個列表或者一個字典中的數(shù)據(jù)保存到文件中又該怎么做呢蒲赂?答案是將數(shù)據(jù)以JSON格式進行保存阱冶。JSON是“JavaScript Object Notation”的縮寫,它本來是JavaScript語言中創(chuàng)建對象的一種字面量語法滥嘴,現(xiàn)在已經(jīng)被廣泛的應用于跨平臺跨語言的數(shù)據(jù)交換木蹬,原因很簡單,因為JSON也是純文本若皱,任何系統(tǒng)任何編程語言處理純文本都是沒有問題的镊叁。目前JSON基本上已經(jīng)取代了XML作為異構(gòu)系統(tǒng)間交換數(shù)據(jù)的事實標準。關(guān)于JSON的知識走触,更多的可以參考JSON的官方網(wǎng)站晦譬,從這個網(wǎng)站也可以了解到每種語言處理JSON數(shù)據(jù)可以使用的工具或三方庫,下面是一個JSON的簡單例子互广。

{
    'name': '駱昊',
    'age': 38,
    'qq': 957658,
    'friends': ['王大錘', '白元芳'],
    'cars': [
        {'brand': 'BYD', 'max_speed': 180},
        {'brand': 'Audi', 'max_speed': 280},
        {'brand': 'Benz', 'max_speed': 320}
    ]
}

可能大家已經(jīng)注意到了敛腌,上面的JSON跟Python中的字典其實是一樣一樣的,事實上JSON的數(shù)據(jù)類型和Python的數(shù)據(jù)類型是很容易找到對應關(guān)系的惫皱,如下面兩張表所示像樊。




我們使用Python中的json模塊就可以將字典或列表以JSON格式保存到文件中,代碼如下所示

import json


def main():
    mydict = {
        'name': '駱昊',
        'age': 38,
        'qq': 957658,
        'friends': ['王大錘', '白元芳'],
        'cars': [
            {'brand': 'BYD', 'max_speed': 180},
            {'brand': 'Audi', 'max_speed': 280},
            {'brand': 'Benz', 'max_speed': 320}
        ]
    }
    try:
        with open('data.json', 'w', encoding='utf-8') as fs:
            json.dump(mydict, fs)
    except IOError as e:
        print(e)
    print('保存數(shù)據(jù)完成!')


if __name__ == '__main__':
    main()

json模塊主要有四個比較重要的函數(shù)逸吵,分別是:

  • dump - 將Python對象按照JSON格式序列化到文件中
  • dumps - 將Python對象處理成JSON格式的字符串
  • load - 將文件中的JSON數(shù)據(jù)反序列化成對象
  • loads - 將字符串的內(nèi)容反序列化成Python對象
    這里出現(xiàn)了兩個概念凶硅,一個叫序列化,一個叫反序列化扫皱。自由的百科全書維基百科上對這兩個概念是這樣解釋的:“序列化(serialization)在計算機科學的數(shù)據(jù)處理中足绅,是指將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換為可以存儲或傳輸?shù)男问剑@樣在需要的時候能夠恢復到原先的狀態(tài)韩脑,而且通過序列化的數(shù)據(jù)重新獲取字節(jié)時氢妈,可以利用這些字節(jié)來產(chǎn)生原始對象的副本(拷貝)。與這個過程相反的動作段多,即從一系列字節(jié)中提取數(shù)據(jù)結(jié)構(gòu)的操作首量,就是反序列化(deserialization)”。

目前絕大多數(shù)網(wǎng)絡(luò)數(shù)據(jù)服務(或稱之為網(wǎng)絡(luò)API)都是基于HTTP協(xié)議提供JSON格式的數(shù)據(jù),關(guān)于HTTP協(xié)議的相關(guān)知識加缘,可以看看阮一峰老師的《HTTP協(xié)議入門》鸭叙,如果想了解國內(nèi)的網(wǎng)絡(luò)數(shù)據(jù)服務,可以看看聚合數(shù)據(jù)阿凡達數(shù)據(jù)等網(wǎng)站拣宏,國外的可以看看{API}Search網(wǎng)站沈贝。下面的例子演示了如何使用requests模塊(封裝得足夠好的第三方網(wǎng)絡(luò)訪問模塊)訪問網(wǎng)絡(luò)API獲取國內(nèi)新聞,如何通過json模塊解析JSON數(shù)據(jù)并顯示新聞標題勋乾,這個例子使用了天行數(shù)據(jù)提供的國內(nèi)新聞數(shù)據(jù)接口宋下,其中的APIKey需要自己到該網(wǎng)站申請。

import requests
import json


def main():
    resp = requests.get('http://api.tianapi.com/guonei/?key=APIKey&num=10')
    data_model = json.loads(resp.text)
    for news in data_model['newslist']:
        print(news['title'])


if __name__ == '__main__':
    main()

在Python中要實現(xiàn)序列化和反序列化除了使用json模塊之外辑莫,還可以使用pickle和shelve模塊学歧,但是這兩個模塊是使用特有的序列化協(xié)議來序列化數(shù)據(jù),因此序列化后的數(shù)據(jù)只能被Python識別各吨。關(guān)于這兩個模塊的相關(guān)知識可以自己看看網(wǎng)絡(luò)上的資料枝笨。另外,如果要了解更多的關(guān)于Python異常機制的知識揭蜒,可以看看segmentfault上面的文章《總結(jié):Python中的異常處理》伺帘,這篇文章不僅介紹了Python中異常機制的使用,還總結(jié)了一系列的最佳實踐忌锯,很值得一讀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末领炫,一起剝皮案震驚了整個濱河市偶垮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帝洪,老刑警劉巖似舵,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異葱峡,居然都是意外死亡砚哗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門砰奕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛛芥,“玉大人,你說我怎么就攤上這事军援〗鍪纾” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵胸哥,是天一觀的道長涯竟。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么庐船? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任银酬,我火速辦了婚禮,結(jié)果婚禮上筐钟,老公的妹妹穿的比我還像新娘揩瞪。我一直安慰自己,他們只是感情好盗棵,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布壮韭。 她就那樣靜靜地躺著,像睡著了一般纹因。 火紅的嫁衣襯著肌膚如雪喷屋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天瞭恰,我揣著相機與錄音屯曹,去河邊找鬼。 笑死惊畏,一個胖子當著我的面吹牛恶耽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颜启,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼偷俭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缰盏?” 一聲冷哼從身側(cè)響起涌萤,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎口猜,沒想到半個月后负溪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡济炎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年川抡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片须尚。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡崖堤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耐床,到底是詐尸還是另有隱情倘感,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布咙咽,位于F島的核電站老玛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜡豹,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一麸粮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镜廉,春花似錦弄诲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至塔插,卻和暖如春梗摇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背想许。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工伶授, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人流纹。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓糜烹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漱凝。 傳聞我的和親對象是個殘疾皇子疮蹦,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容