《python》-11:文件和異常

每一個(gè)不曾起舞的日子,都是對(duì)生命的辜負(fù)悼沿!

文件和異常

在實(shí)際開發(fā)中屿笼,常常需要對(duì)程序中的數(shù)據(jù)進(jìn)行持久化操作,而實(shí)現(xiàn)數(shù)據(jù)持久化最直接簡(jiǎn)單的方式就是將數(shù)據(jù)保存到文件中翁巍。說到“文件”這個(gè)詞驴一,可能需要先科普一下關(guān)于文件系統(tǒng)的知識(shí),對(duì)于這個(gè)概念灶壶,維基百科上給出了很好的詮釋肝断,這里不再浪費(fèi)筆墨。

在Python中實(shí)現(xiàn)文件的讀寫操作其實(shí)非常簡(jiǎn)單驰凛,通過Python內(nèi)置的open函數(shù)胸懈,我們可以指定文件名、操作模式恰响、編碼信息等來獲得操作文件的對(duì)象趣钱,接下來就可以對(duì)文件進(jìn)行讀寫操作了。這里所說的操作模式是指要打開什么樣的文件(字符文件還是二進(jìn)制文件)以及做什么樣的操作(讀胚宦、寫還是追加)首有,具體的如下表所示。

操作模式 具體含義
r 讀仁嗳啊(默認(rèn))
w 寫入(會(huì)先截?cái)嘀暗膬?nèi)容)
x 寫入井联,如果文件已經(jīng)存在會(huì)產(chǎn)生異常
a 追加,將內(nèi)容寫入到已有文件的末尾
b 二進(jìn)制模式
t 文本模式(默認(rèn))
+ 更新(既可以讀又可以寫)

讀寫文本文件

讀文本文件

讀取文本文件時(shí)您旁,需要在使用open函數(shù)時(shí)指定好帶路徑的文件名(可以使用相對(duì)路徑絕對(duì)路徑)并將文件模式設(shè)置為'r'(如果不指定烙常,默認(rèn)值也是'r'),然后通過encoding參數(shù)指定編碼(如果不指定鹤盒,默認(rèn)值是None蚕脏,那么在讀取文件時(shí)使用的是操作系統(tǒng)默認(rèn)的編碼),如果不能保證保存文件時(shí)使用的編碼方式與encoding參數(shù)指定的編碼方式是一致的昨悼,那么就可能因無法解碼字符而導(dǎo)致讀取失敗蝗锥。下面的例子演示了如何讀取一個(gè)純文本文件。

def main():
   # 以'utf-8'格式打開文件
   f = open('hello.txt', 'r', encoding='utf-8')
   print(f.read())
   # 一定記得關(guān)閉
   f.close()


if __name__ == '__main__':
   main()

請(qǐng)注意上面的代碼率触,如果open函數(shù)指定的文件并不存在或者無法打開终议,那么將引發(fā)異常狀況導(dǎo)致程序崩潰。為了讓代碼有一定的健壯性和容錯(cuò)性葱蝗,我們可以使用Python的異常機(jī)制對(duì)可能在運(yùn)行時(shí)發(fā)生狀況的代碼進(jìn)行適當(dāng)?shù)奶幚硌ㄕ牛缦滤尽?/p>

def main():
    f = None
    try:
        f = open('hello.txt', 'r', encoding='utf-8')
        print(f.read())
    except FileNotFoundError as e:
        print('未知文件')
    except LookupError:
        print('未知編碼')
    except UnicodeDecodeError:
        print('解碼錯(cuò)誤')
    finally:
        if f:
            f.close()
    
    if __name__ == '__main__':
        main()

在Python中,我們可以將那些在運(yùn)行時(shí)可能會(huì)出現(xiàn)狀況的代碼放在try代碼塊中两曼,在try代碼塊的后面可以跟上一個(gè)或多個(gè)except來捕獲可能出現(xiàn)的異常狀況皂甘。例如在上面讀取文件的過程中,文件找不到會(huì)引發(fā)FileNotFoundError悼凑,指定了未知的編碼會(huì)引發(fā)LookupError偿枕,而如果讀取文件時(shí)無法按指定方式解碼會(huì)引發(fā)UnicodeDecodeError璧瞬,我們?cè)趖ry后面跟上了三個(gè)except分別處理這三種不同的異常狀況。最后我們使用finally代碼塊來關(guān)閉打開的文件渐夸,釋放掉程序中獲取的外部資源嗤锉,由于finally塊的代碼不論程序正常還是異常都會(huì)執(zhí)行到(甚至是調(diào)用了sys模塊的exit函數(shù)退出Python環(huán)境,finally塊都會(huì)被執(zhí)行墓塌,因?yàn)閑xit函數(shù)實(shí)質(zhì)上是引發(fā)了SystemExit異常)瘟忱,因此我們通常把finally塊稱為“總是執(zhí)行代碼塊”,它最適合用來做釋放外部資源的操作苫幢。如果不愿意在finally代碼塊中關(guān)閉文件對(duì)象釋放資源访诱,也可以使用上下文語法,通過with關(guān)鍵字指定文件對(duì)象的上下文環(huán)境并在離開上下文環(huán)境時(shí)自動(dòng)釋放文件資源韩肝,代碼如下所示触菜。

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

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

import time


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

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

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

if __name__ == '__main__':
    main()   

讀寫文本文件

要將文本信息寫入文件文件也非常簡(jiǎn)單,在使用open函數(shù)時(shí)指定好文件名并將文件模式設(shè)置為'w'即可谜诫。注意如果需要對(duì)文件內(nèi)容進(jìn)行追加式寫入漾峡,應(yīng)該將模式設(shè)置為'a'。如果要寫入的文件不存在會(huì)自動(dòng)創(chuàng)建文件而不是引發(fā)異常喻旷。下面的例子演示了如何將1-9999直接的素?cái)?shù)分別寫入三個(gè)文件中(1-99之間的素?cái)?shù)保存在a.txt中生逸,100-999之間的素?cái)?shù)保存在b.txt中,1000-9999之間的素?cái)?shù)保存在c.txt中)且预。

from math import sqrt


def is_prime(n):
   """判斷素?cái)?shù)的函數(shù)"""
   assert n > 0, '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('寫文件時(shí)發(fā)生錯(cuò)誤!')
   finally:
       for fs in fs_list:
           fs.close()
   print('操作完成!')


if __name__ == '__main__':
   main()

上面代碼中槽袄,assert作用是聲明后面的布爾值必須為真(即保證n>0,否則會(huì)觸發(fā)異常)的判定,如果發(fā)生異常就說明表達(dá)示為假锋谐”槌撸可以理解assert斷言語句為raise-if-not,用來測(cè)試表示式涮拗,其返回值為假乾戏,就會(huì)觸發(fā)異常。

讀寫二進(jìn)制文件

知道了如何讀寫文本文件要讀寫二進(jìn)制文件也就很簡(jiǎn)單了三热,下面的代碼實(shí)現(xiàn)了復(fù)制圖片文件的功能鼓择。

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


if __name__ == '__main__':
    main()

讀寫JSON文件

通過上面的講解就漾,我們已經(jīng)知道如何將文本數(shù)據(jù)和二進(jìn)制數(shù)據(jù)保存到文件中呐能,那么這里還有一個(gè)問題,如果希望把一個(gè)列表或者一個(gè)字典中的數(shù)據(jù)保存到文件中又該怎么做呢抑堡?答案是將數(shù)據(jù)以JSON格式進(jìn)行保存摆出。JSON是“JavaScript Object Notation”的縮寫朗徊,它本來是JavaScript語言中創(chuàng)建對(duì)象的一種字面量語法,現(xiàn)在已經(jīng)被廣泛的應(yīng)用于跨平臺(tái)跨語言的數(shù)據(jù)交換懊蒸,原因很簡(jiǎn)單荣倾,因?yàn)镴SON也是純文本,任何系統(tǒng)任何編程語言處理純文本都是沒有問題的骑丸。目前JSON基本上已經(jīng)取代了XML作為異構(gòu)系統(tǒng)間交換數(shù)據(jù)的事實(shí)標(biāo)準(zhǔn)。關(guān)于JSON的知識(shí)妒貌,更多的可以參考JSON的官方網(wǎng)站通危,從這個(gè)網(wǎng)站也可以了解到每種語言處理JSON數(shù)據(jù)格式可以使用的工具或三方庫,下面是一個(gè)JSON的簡(jiǎn)單例子灌曙。

{
  "name": "月上秦少",
  "intro": "人生似水豈無涯菊碟。浮云吹作雪,世味煮成茶在刺。",
  "job": "一個(gè)前端小菜鳥",
  "web": "http://www.reibang.com/u/3075de2e101e",
  "img": "xxx.jpg",
  "hoby": [
    {"lang": "Python"},
    {"game": "lol"},
    {"xx": "xx"},
    {"yy": "yy"},
  ]
}

可能大家已經(jīng)注意到了逆害,上面的JSON跟Python中的字典其實(shí)是一樣一樣的,事實(shí)上JSON的數(shù)據(jù)類型和Python的數(shù)據(jù)類型是很容易找到對(duì)應(yīng)關(guān)系的蚣驼,如下面兩張表所示魄幕。

JSON Python
object dict
array list
string str
number (int / real) int / float
true / false True / False
null None
Python JSON
dict object
list,tuple array
str string
int,float,int-&float-derived Enums number
True / False true / false
None null

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

import json

def main():
    mydict = {
      "name": "月上秦少",
      "intro": "人生似水豈無涯纯陨。浮云吹作雪,世味煮成茶留储。",
      "job": "一個(gè)前端小菜鳥",
      "web": "http://www.reibang.com/u/3075de2e101e",
      "img": "xxx.jpg",
      "hoby": [
        {"lang": "Python"},
        {"game": "lol"},
        {"xx": "xx"},
        {"yy": "yy"},
      ]
    }
    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模塊主要有四個(gè)比較重要的函數(shù)翼抠,分別是:

  • dump - 將Python對(duì)象按照J(rèn)SON格式序列化到文件中
  • dumps - 將Python對(duì)象處理成JSON格式的字符串
  • load - 將文件中的JSON數(shù)據(jù)反序列化成對(duì)象
  • loads - 將字符串的內(nèi)容反序列化成Python對(duì)象

這里出現(xiàn)了兩個(gè)概念,一個(gè)叫序列化获讳,一個(gè)叫反序列化阴颖。自由的百科全書維基百科上對(duì)這兩個(gè)概念是這樣解釋的:“序列化(serialization)在計(jì)算機(jī)科學(xué)的數(shù)據(jù)處理中,是指將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问截はィ@樣在需要的時(shí)候能夠恢復(fù)到原先的狀態(tài)量愧,而且通過序列化的數(shù)據(jù)重新獲取字節(jié)時(shí),可以利用這些字節(jié)來產(chǎn)生原始對(duì)象的副本(拷貝)尤误。與這個(gè)過程相反的動(dòng)作侠畔,即從一系列字節(jié)中提取數(shù)據(jù)結(jié)構(gòu)的操作,就是反序列化(deserialization)”损晤。

目前絕大多數(shù)網(wǎng)絡(luò)數(shù)據(jù)服務(wù)(或稱之為網(wǎng)絡(luò)API)都是基于HTTP協(xié)議提供JSON格式的數(shù)據(jù)软棺,關(guān)于HTTP協(xié)議的相關(guān)知識(shí),可以看看阮一峰老師的《HTTP協(xié)議入門》尤勋,如果想了解國(guó)內(nèi)的網(wǎng)絡(luò)數(shù)據(jù)服務(wù)喘落,可以看看聚合數(shù)據(jù)阿凡達(dá)數(shù)據(jù)等網(wǎng)站茵宪,國(guó)外的可以看看{API}Search網(wǎng)站。下面的例子演示了如何使用requests模塊(封裝得足夠好的第三方網(wǎng)絡(luò)訪問模塊)訪問網(wǎng)絡(luò)API獲取國(guó)內(nèi)新聞瘦棋,如何通過json模塊解析JSON數(shù)據(jù)并顯示新聞標(biāo)題稀火,這個(gè)例子使用了天行數(shù)據(jù)提供的國(guó)內(nèi)新聞數(shù)據(jù)接口,其中的APIKey需要自己到該網(wǎng)站申請(qǐng)赌朋。免費(fèi)模擬API接口RandomUser – 生成隨機(jī)用戶 JSON 數(shù)據(jù)的 API

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中要實(shí)現(xiàn)序列化反序列化除了使用json模塊之外凰狞,還可以使用pickleshelve模塊,但是這兩個(gè)模塊是使用特有的序列化協(xié)議來序列化數(shù)據(jù)沛慢,因此序列化后的數(shù)據(jù)只能被Python識(shí)別赡若。關(guān)于這兩個(gè)模塊的相關(guān)知識(shí)可以自己看看網(wǎng)絡(luò)上的資料。另外团甲,如果要了解更多的關(guān)于Python異常機(jī)制的知識(shí)逾冬,可以看看segmentfault上面的文章《總結(jié):Python中的異常處理》,這篇文章不僅介紹了Python中異常機(jī)制的使用躺苦,還總結(jié)了一系列的最佳實(shí)踐身腻,很值得一讀。

exercise python 100 days from https://github.com/jackfrued/Python-100-Days

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匹厘,一起剝皮案震驚了整個(gè)濱河市嘀趟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌集乔,老刑警劉巖去件,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扰路,居然都是意外死亡尤溜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門汗唱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宫莱,“玉大人,你說我怎么就攤上這事哩罪∈诎裕” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵际插,是天一觀的道長(zhǎng)碘耳。 經(jīng)常有香客問我,道長(zhǎng)框弛,這世上最難降的妖魔是什么辛辨? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上斗搞,老公的妹妹穿的比我還像新娘指攒。我一直安慰自己,他們只是感情好僻焚,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布允悦。 她就那樣靜靜地躺著,像睡著了一般虑啤。 火紅的嫁衣襯著肌膚如雪隙弛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天狞山,我揣著相機(jī)與錄音驶鹉,去河邊找鬼。 笑死铣墨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的办绝。 我是一名探鬼主播伊约,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼孕蝉!你這毒婦竟也來了屡律?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤降淮,失蹤者是張志新(化名)和其女友劉穎超埋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佳鳖,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霍殴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了系吩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片来庭。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖穿挨,靈堂內(nèi)的尸體忽然破棺而出月弛,到底是詐尸還是另有隱情,我是刑警寧澤科盛,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布帽衙,位于F島的核電站,受9級(jí)特大地震影響贞绵,放射性物質(zhì)發(fā)生泄漏厉萝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冀泻。 院中可真熱鬧常侣,春花似錦、人聲如沸弹渔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肢专。三九已至舞肆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間博杖,已是汗流浹背椿胯。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剃根,地道東北人哩盲。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像狈醉,于是被迫代替她去往敵國(guó)和親廉油。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    草里有只羊閱讀 18,334評(píng)論 0 85
  • 一苗傅、Python簡(jiǎn)介和環(huán)境搭建以及pip的安裝 4課時(shí)實(shí)驗(yàn)課主要內(nèi)容 【Python簡(jiǎn)介】: Python 是一個(gè)...
    _小老虎_閱讀 5,748評(píng)論 0 10
  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí)抒线,會(huì)觸發(fā)此異常。 O...
    我想起個(gè)好名字閱讀 5,334評(píng)論 0 9
  • 文件 在實(shí)際開發(fā)中渣慕,常常需要對(duì)程序中的數(shù)據(jù)進(jìn)行持久化操作嘶炭,而實(shí)現(xiàn)數(shù)據(jù)持久化最直接簡(jiǎn)單的方式就是將數(shù)據(jù)保存到文件中。...
    Jason_c8d4閱讀 5,605評(píng)論 1 0
  • 天空沒有飛鳥 劃過那片樹林 沉眠于此的人 再聽不到鳥聲 不知名的白花 開滿整片樹間 模糊的墓志銘 仿佛吟誦著舊詩歌...
    須提閱讀 302評(píng)論 0 1