Python文件

1.1. 字符編碼

1.1.1. 字符編碼的作用

計算機(jī)只認(rèn)識0和1組成的二進(jìn)制序列,因此任何文件中的內(nèi)容(比如"hello neuedu","你好,東軟睿道"這些字符串)要想被計算機(jī)識別或者想存儲在計算機(jī)上都需要轉(zhuǎn)換為二進(jìn)制序列。那么字符與二進(jìn)制序列怎么進(jìn)行相互轉(zhuǎn)換呢?于是人們嘗試建立一個表格來存儲一個字符與一個二進(jìn)制序列的對應(yīng)關(guān)系。

  • 編碼 將字符轉(zhuǎn)換為對應(yīng)的二進(jìn)制序列的過程叫做字符編碼
  • 解碼 將二進(jìn)制序列轉(zhuǎn)換為對應(yīng)的字符的過程叫做字符解碼

1.2. 字符編碼的簡單發(fā)展歷史

1.2.1. ASCII碼誕生

最早建立這個字符與十進(jìn)制數(shù)字對應(yīng)的關(guān)系的是美國,這張表被稱為ASCII碼(American Standard Code for Information Interface, 美國標(biāo)準(zhǔn)信息交換代碼)。ASCII碼是基于拉丁字母的一套電腦編程系統(tǒng),主要用于顯示現(xiàn)代英語和其他西歐語言。它被設(shè)計為用1個字節(jié)來表示一個字符,所以ASCII碼表最多只能表示2**8=256個字符。實際上ASCII碼表中只有128個字符,剩余的128個字符是預(yù)留擴(kuò)展用的。

image

1.2.2. GBK等各國編碼誕生

隨著計算機(jī)的普及和發(fā)展,很過國家都開始使用計算機(jī)。大家發(fā)現(xiàn)ASCII碼預(yù)留的128個位置根本無法存儲自己國家的文字和字符,因此各個國家開始制定各自的字符編碼表,其中中國的的字符編碼表有GB2312和GBK。

1.2.3. Unicode誕生(萬國碼)

后來隨著世界互聯(lián)網(wǎng)的形成和發(fā)展,各國的人們開始有了互相交流的需要。但是這個時候就存在一個問題,每個國家所使用的字符編碼表都是不同的。這個時候,人們希望有一個世界統(tǒng)一的字符編碼表來存放所有國家所使用的文字和符號,這就是Unicode。Unicode又被稱為 統(tǒng)一碼、萬國碼、單一碼,它是為了解決傳統(tǒng)的字符編碼方案的局限性而產(chǎn)生的,它為每種語言中的每個字符設(shè)定了統(tǒng)一并且為之一的二進(jìn)制編碼。Unicode規(guī)定所有的字符和符號最少由2個字節(jié)(16位)來表示,所以Unicode碼可以表示的最少字符個數(shù)為2**16=65536。

1.2.4. UTF-8誕生

為什么已經(jīng)有了Unicode還要UTF-8呢?因為美國人不樂意了,由于當(dāng)時存儲設(shè)備是非常昂貴的,而Unicode中規(guī)定所有字符最少要由2個字節(jié)表示。美國人認(rèn)為像原來ASCII碼中的字符用1個字節(jié)就可以了,因此決定創(chuàng)建一個新的字符編碼來節(jié)省存儲空間。UTF-8是對Unicode編碼的壓縮和優(yōu)化,它不在要求最少使用2個字節(jié),而是將所有字符和符號進(jìn)行分類:

  • ascii碼中的內(nèi)容用1個字節(jié)保存
  • 歐洲的字符用2個字節(jié)保存
  • 東亞的字符用3個字節(jié)保存
  • ...

UTF-8是目前最常用,也是被推薦使用的字符編碼。

1.3. 字符串和字節(jié)序列的轉(zhuǎn)換

1.3.1. 字符串轉(zhuǎn)字節(jié)序列

bytes = '張三'.encode()
print(bytes)
print(type(bytes))
bytes = '張三'.encode('utf-8')
print(bytes)
print(type(bytes))
bytes = '張三'.encode('gbk')
print(bytes)
print(type(bytes))

輸出

b'\xe5\xbc\xa0\xe4\xb8\x89'
<class 'bytes'>
b'\xe5\xbc\xa0\xe4\xb8\x89'
<class 'bytes'>
b'\xd5\xc5\xc8\xfd'
<class 'bytes'>

encode默認(rèn)按utf-8編碼

1.3.2. 字節(jié)序列轉(zhuǎn)字符串


bytes = b'\xe5\xbc\xa0\xe4\xb8\x89'
msg1 = bytes.decode()
print(msg1)
print(type(msg1))

msg1 = bytes.decode('utf-8')
print(msg1)
print(type(msg1))

msg1 = bytes.decode('gbk')
print(msg1)
print(type(msg1))

輸出

張三
<class 'str'>
張三
<class 'str'>
寮犱笁
<class 'str'>

1.4. 文件的概念和作用

  • 計算機(jī)的 文件,就是存儲在硬盤上的 數(shù)據(jù)

1.5. 文件的存儲方式

  • 在計算機(jī)中,文件是以 二進(jìn)制 的方式保存在磁盤上的

1.5.1. 文本文件和二進(jìn)制文件

  • 文本文件(字符串)

    • 可以使用 文本編輯軟件 查看
    • 本質(zhì)上還是二進(jìn)制文件
    • 例如:python 的源程序
  • 二進(jìn)制文件

    • 保存的內(nèi)容 不是給人直接閱讀的,而是 提供給其他軟件使用的
    • 例如:圖片文件、音頻文件、視頻文件等等
    • 二進(jìn)制文件不能使用 文本編輯軟件 查看

1.6. 文件的基本操作

1.6.1. 操作文件的套路

計算機(jī) 中要操作文件的套路非常固定,一共包含三個步驟

  1. 打開文件
  2. 讀、寫文件
    • 將文件內(nèi)容讀入內(nèi)存
    • 將內(nèi)存內(nèi)容寫入文件
  3. 關(guān)閉文件

1.6.2. 操作文件的函數(shù)/方法

  • Python 中要操作文件需要記住 1 個函數(shù)和 3 個方法
序號 函數(shù)/方法 說明
01 open 打開文件,并且返回文件操作對象
02 read 將文件內(nèi)容讀取到內(nèi)存
03 write 將指定內(nèi)容寫入文件
04 close 關(guān)閉文件
  • open 函數(shù)負(fù)責(zé)打開文件,并且返回文件對象
  • read/write/close 三個方法都需要通過 文件對象 來調(diào)用

1.6.3. read 方法 —— 讀取文件

  • open 函數(shù)的第一個參數(shù)是要打開的文件名(文件名區(qū)分大小寫)
    • 如果文件 存在,返回 文件操作對象
    • 如果文件 不存在,會 拋出異常
  • read 方法可以一次性 讀入返回 文件的 所有內(nèi)容
  • close 方法負(fù)責(zé) 關(guān)閉文件
    • 如果 忘記關(guān)閉文件,會造成系統(tǒng)資源消耗,而且會影響到后續(xù)對文件的訪問 新建一個demo.txt文件,輸入neuedu,放到和readfile.py同級目錄下

readfile.py

# 1\. 打開 - 文件名需要注意大小寫
file = open("demo.txt")
print(file)
# 2\. 讀取
text = file.read()
print(text)

# 3\. 關(guān)閉
file.close()

輸出

<_io.TextIOWrapper name='demo.txt' mode='r' encoding='cp936'>
neuedu


新建一個demo2.txt文件,輸入東軟睿道,保存為utf-8模式,放到和readfile.py同級目錄下 readfile.py

# 1\. 打開 - 文件名需要注意大小寫
file = open("demo2.txt")
print(file)
# 2\. 讀取
text = file.read()
print(text)

# 3\. 關(guān)閉
file.close()

輸出

<_io.TextIOWrapper name='demo2.txt' mode='r' encoding='cp936'>
涓滆蔣鐫塊亾

可見發(fā)生了中文亂碼問題。

上面代碼發(fā)生的過程是將存儲在文件中的字符串,讀取到變量中(內(nèi)存中),這里涉及的事情的先后順序是這樣的,理解這些非常重要:

1.最開始,我們用某個編輯軟件(記事本程序),編輯了"東軟睿道"四個字符,按utf-8編碼方式保存到磁盤上,此處放生了編碼過程(字符串--->字節(jié))。 2.接下來我們通過上面的python代碼,打開(open)文件,此時將存儲在文件中的字符串(字節(jié),二進(jìn)制),讀取到變量中(內(nèi)存中),轉(zhuǎn)換成字符串。這會發(fā)生一個解碼過程(字節(jié)--->字符串)。Python默認(rèn)是按照什么編碼方式解碼的呢,輸出信息給出了答案

<_io.TextIOWrapper name='demo2.txt' mode='r' encoding='cp936'>

open函數(shù)返回的是TextIOWrapper類型的文件對象,cp936是默認(rèn)的文本編碼格式(gb2312) 說明文件打開時默認(rèn)按gb2312編碼方式進(jìn)行解碼。編碼和解碼的方式不同,故而發(fā)生亂碼。

解決辦法有2個: 1.記事本編輯完保存時,按'gb2312'方式保存到磁盤(ANSI)
image.png

2.文件打開時指定編碼方式為utf-8:

# 1\. 打開 - 文件名需要注意大小寫
file = open("demo2.txt",encoding='utf-8')
print(file)
# 2\. 讀取
text = file.read()
print(text)

# 3\. 關(guān)閉
file.close()

輸出

<_io.TextIOWrapper name='demo2.txt' mode='r' encoding='utf-8'>
東軟睿道

1.6.4. write 方法 —— 寫入文件

寫入文件示例

# 打開文件
f = open("abc.txt", "w")
print(f)
f.write("hello neuedu!\n")
f.write("今天天氣真好")

# 關(guān)閉文件
f.close()

查看abc.txt

hello neuedu!
今天天氣真好

如果看到的的中文是亂碼,確認(rèn)是否是以記事本程序打開,如果是用pycharm打開,看到的是亂碼,和讀文件是同樣的道理,python默認(rèn)是以gb2312的方式將中文編碼,寫入到文件中,pycharm默認(rèn)以utf-8格式解碼打開,故而看到是亂碼,要想以utf-8方式將中文編碼,寫入到文件中,需要:

f = open("abc.txt", "w",encoding='utf-8')

注意,要想向文件中寫入數(shù)據(jù),open方法必須寫上第二個參數(shù)w。

f = open("abc.txt", "w")

open方法默認(rèn)以讀的方式打開,以下2行代碼是等價的。

f = open("abc.txt")
f = open("abc.txt","r")

所以,讀文件的時候我們可以省略第二個參數(shù)r,寫文件不可以省略。

1.7. read(),readline(),readlines()區(qū)別與用法

0.準(zhǔn)備 假設(shè)a.txt的內(nèi)容如下所示:

Hello
Welcome
What is the luck...

  1. read([size])方法 read([size])方法從文件當(dāng)前位置起讀取size個字節(jié),若無參數(shù)size,則表示讀取至文件結(jié)束為止,它的返回值為字符串對象

    f = open("a.txt")
    lines = f.read()
    print(lines)
    print(type(lines))
    f.close()
    
    

    輸出結(jié)果:

    Hello
    Welcome
    What is the luck...
    <type 'str'> #字符串類型
    
    

    2.readline()方法 從字面意思可以看出,該方法每次讀出一行內(nèi)容,所以,讀取時占用內(nèi)存小,比較適合大文件,該方法返回一個字符串對象。

    f = open("a.txt")
    line = f.readline()
    print(type(line))
    while line:
     print(line)
     line = f.readline()
    f.close()
    
    

    輸出結(jié)果:

    <type 'str'>
    Hello
    Welcome
    What is the luck...
    
    

    3.readlines()方法讀取整個文件所有行,保存在一個列表(list)變量中,每行作為一個元素,但讀取大文件會比較占內(nèi)存。

    f = open("a.txt")
    lines = f.readlines()
    print(type(lines))
    for line in lines:
     print(lines)
    f.close()
    
    

    輸出結(jié)果:

    <type 'list'>
    Hello
    Welcome
    What is the luck...
    
    

    4.最簡單、最快速的逐行處理文本的方法:直接for循環(huán)文件對象

    f = open("a.txt")
    for line in f:
     print(line)
    f.close()
    
    

    1.8. 使用 with open() as 讀寫文件

由于文件讀寫時都有可能產(chǎn)生IOError,一旦出錯,后面的f.close()就不會調(diào)用。所以,為了保證無論是否出錯都能正確地關(guān)閉文件,我們可以使用try ... finally來實現(xiàn):

try:
    f = open('a.txt', 'r')
    print(f.read())
finally:
    if f:
        f.close()

每次都這么寫實在太繁瑣,所以,Python引入了with語句來自動幫我們調(diào)用close()方法:

with open('a.txt', 'r') as f:
    print(f.read())

這和前面的try ... finally是一樣的,但是代碼更加簡潔,并且不必調(diào)用f.close()方法。

1.9. 文件指針

文件指針 標(biāo)記 從哪個位置開始讀取數(shù)據(jù)

  • 第一次打開 文件時,通常 文件指針會指向文件的開始位置
  • 當(dāng)執(zhí)行了 read 方法后,文件指針 會移動到 讀取內(nèi)容的末尾

思考 如果執(zhí)行了一次 read 方法,讀取了所有內(nèi)容,那么再次調(diào)用 read 方法,還能夠獲得到內(nèi)容嗎?

答案 不能 第一次讀取之后,文件指針移動到了文件末尾,再次調(diào)用不會讀取到任何的內(nèi)容

控制文件指針移動 方法:f.seek(offset,whence) offset代表文件指針的偏移量,單位是字節(jié)bytes whence代表參照物,有三個取值 (1)0:參照文件的開頭 (2)1:參照當(dāng)前文件指針?biāo)诘奈恢?(3)2:參照文件末尾
PS:快速移動到文件末尾f.seek(0,2) 強(qiáng)調(diào):其中whence=1和whence=2只能在b 模式下使用 f.tell()函數(shù)可以得到當(dāng)前文件指針的位置

with open(r'rrf.txt','rb')as f:
#     f.readlines()
#     f.seek(6,0)    #從開頭移動6個字節(jié)
#     print(f.readline().decode()  )
#     print(f.tell() )

# with open(r'rrf.txt', 'rb')as f:
#     f.readline()
#     f.seek(9,1)   #從當(dāng)前指針位置移動9個字節(jié)
#     print(f.readline() .decode() )

with open(r'rrf.txt', 'rb')as f:
    f.seek(-5,2)                    #指針在末尾,往前讀5個字節(jié)
    print(f.read() .decode() )
    print(f.tell())

rrf.txt

abcdefghijklmnopqrstuvwxyz

1.10. 打開文件的方式總結(jié)

  • open 函數(shù)默認(rèn)以 只讀方式 打開文件,并且返回文件對象

語法如下:

f = open("文件名", "訪問方式")

訪問方式 說明
r 只讀方式打開文件。文件的指針將會放在文件的開頭,這是默認(rèn)模式。如果文件不存在,拋出異常
w 只寫方式打開文件。如果文件存在會被覆蓋。如果文件不存在,創(chuàng)建新文件
a 追加方式打開文件。如果該文件已存在,文件指針將會放在文件的結(jié)尾。如果文件不存在,創(chuàng)建新文件進(jìn)行寫入
r+ 讀寫方式打開文件。文件的指針將會放在文件的開頭。如果文件不存在,拋出異常
w+ 讀寫方式打開文件。如果文件存在會被覆蓋。如果文件不存在,創(chuàng)建新文件
a+ 讀寫方式打開文件。如果該文件已存在,文件指針將會放在文件的結(jié)尾。如果文件不存在,創(chuàng)建新文件進(jìn)行寫入

思考 在使用python的open函數(shù)對文件進(jìn)行打開時,

  • 用什么模式打開才最合適?
  • 是用r? 還是w? 還是a? ,后面還要不要加個+???
  • 讀寫圖片等二進(jìn)制文件怎么辦?
    with open('test.txt','打開模式選啥?????') as w:
            w.write("要寫入的內(nèi)容")

總結(jié)

  1. 已經(jīng)確定要打開的文件已存在(如果不存在會報錯)。

    • 只讀文本文件 ? 用r
    • 只讀非文本文件(圖片等)? 用rb
    • 要既讀又寫? 在后邊添個+號增加權(quán)限, 用r+ 或者 rb+
  2. 不確定要打開的文件是否存在,如果該文件已存在則打開文件,并從開頭開始編輯,即原有內(nèi)容會被替換。如果該文件不存在,創(chuàng)建新文件。

    • 只寫文本文件 ? 用w
    • 只寫非文本文件(圖片等)? 用wb
    • 要既讀又寫? 在后邊添個+號增加權(quán)限, 用w+ 或者 wb+
  3. 不確定要打開的文件是否存在,而且想要追加寫入(不替換原有內(nèi)容,新內(nèi)容寫入到已有內(nèi)容后)。如果該文件不存在,創(chuàng)建新文件。

    • 只寫文本文件 ? 用a
    • 只寫非文本文件(圖片等)? 用ab
    • 要既讀又寫? 在后邊添個+號增加權(quán)限, 用a+ 或者 ab+

1.11. 課上練習(xí)

1.11.1. 文件操作

1). 創(chuàng)建文件data.txt, 文件共100000行, 每行存放一個1~100之間的整數(shù). 2). 找出文件中數(shù)字出現(xiàn)次數(shù)最多的10個數(shù)字, 寫入文件mostNum.txt;

import random

f = open('data.txt', 'w+')
for i in range(100000):
    f.write(str(random.randint(1,100)) + '\n')
print(f.read())
f.close()

from collections import Counter
dict={}
f = open('data.txt', 'r+')
for i in f:
    if i not in dict:
        dict[i] = 1
    else:
        dict[i] = dict[i] + 1
d = Counter(dict)
with open('mostNum.txt', 'w+') as k:
    for i in d.most_common(10):
        k.write(f'{i[0].strip()}--------{i[1]}\n')
    k.seek(0, 0)
    print(k.read())
f.close()

利用Counter類里的most_common方法來直接比較字典的value值取value值最大的前十個元素。

1.11.2. 添加行號

1). 編寫程序, 將a.txt操作生成文件a_num.txt文件, 2). 其中文件內(nèi)容與a.txt一致, 但是在每行的行首加上行號。

with open('a.txt', 'r+') as f1, open('a_num.txt', 'w+') as f2:
    for i, j in enumerate(f1):
        f2.write(str(i)+'  '+j.strip()+'\n')
    f2.seek(0, 0)
    print(f2.read())

a.txt

Hello
Welcome
What is the luck...

a_num.txt

0  Hello
1  Welcome
2  What is the luck...

1.11.3. 非文本文件的讀取

如果讀取圖片, 音樂或者視頻(非文本文件), 需要通過二進(jìn)制的方式進(jìn)行讀取與寫入;b

  • 讀取二進(jìn)制文件 rb:rb+:wb:wb+:ab:ab+:
  • 讀取文本文件 rt:rt+:wt:wt+:at:at+ 等價于 r:r+:w:w+:a:a+

先讀取二進(jìn)制文件內(nèi)容, 保存在變量content里面

f1 = open("a.png", mode='rb')
content = f1.read()
f1.close()
# print(content)

f2 = open('b.png', mode='wb')
f2.write(content)
f2.close()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容