文件操作對於編程語言的重要性不言而喻,如果數據不能持久保存,信息技術也就失去了意義。
文件操作的內容包括打開文件,操作文件,關閉文件
關於文件操作的相關模塊請參考博客:https://www.cnblogs.com/wj-1314/p/8557077.html
一,打開文件
python中打開文件的函數為open('filename',mode='r',encode='None'),open函數默認返回文件的句柄,我們可以根據句柄來對文件進行增,刪,改,查的操作。將句柄賦給我們定義的變量,假設我們定義變量為f,則f=open('filename',mode='r',encode='utf-8') 或者with open('filename') as f。
注意點:
1.python解釋器打開文件時,是對硬盤進行操作,需要內核態才可以操作硬盤,故此時python解釋器是調用操作系統的文件讀取接口。windows中文版本默認使用GBK編碼表,linux默認使用utf-8,所有如果操作的文件在windows下,非GBK編碼的,需要在open函數中聲明編碼類型,使操作系統運用相應的編碼規則進行解碼讀取,防止串碼,亂碼現象。
2.open主要有三種模式,讀(r),寫(w),追加(a),其中,默認為讀模式。各個模式的詳解,見下文。
二,關閉文件
關閉文件有兩組方式:
1.使用f.close() ,f為open返回的句柄賦值的變量名。
2.程序結束后,自動關閉。第一個方法容易造成文件寫操作時,數據的丟失。原因是寫數據時,數據會先保存在內存中,文件關閉時才會寫入硬盤,此時如果文件未關閉,軟件因為異常崩潰,導致內存中的數據丟失,且未寫入硬盤中。作為第一種關閉方法的優化,是使用:with open('filename') as f 。with會創建一個程序塊,將文件操作置於with程序塊下,這樣with控制塊結束,文件也會自動關閉。
語法如下:
with open('f1.txt') as f1 , open('f2.txt') as f2: ......
三,操作文件
3.1 file的基本方法
方法 |
功能 |
F.read([size]) |
size為讀取的長度,以byte為單位 |
F.readline([size]) |
如果定義了size,有可能返回的只是一行的一部分 |
F.readlines([size]) |
把文件每一行作為一個list的一個成員,並返回這個list。其實它的內部是通過循環調用readline()來實現的。如果提供size參數,size是表示讀取內容的總長,也就是說可能只讀到文件的一部分 |
F.write(str) |
把str寫到文件中,write()並不會在str后加上一個換行符 |
F.writelines(seq) |
把seq的內容全部寫到文件中。這個函數也只是忠實地寫入,不會在每行后面加上任何東西 |
F.close() |
關閉文件。python會在一個文件不用后自動關閉文件,不過這一功能沒有保證,最好還是養成自己關閉的習慣。如果一個文件在關閉后還對其進行操作會產生ValueError |
F.flush() |
把緩沖區的內容寫入硬盤 ,即將內存中的數據刷新到銀盤上 |
F.fileno() |
返回一個長整型的”文件標簽“ |
F.isatty() |
文件是否是一個終端設備文件(unix系統中的) |
F.tell() |
返回文件操作標記的當前位置,以文件的開頭為原點 |
F.next() |
返回下一行,並將文件操作標記位移到下一行。把一個file用於for ... in file這樣的語句時,就是調用next()函數來實現遍歷的。 |
F.seek(offset[,whence]) |
將文件打操作標記移到offset的位置。這個offset一般是相對於文件的開頭來計算的,一般為正數。但如果提供了whence參數就不一定了,whence可以為0表示從頭開始計算,1表示以當前位置為原點計算。2表示以文件末尾為原點進行計算。需要注意,如果文件以a或a+的模式打開,每次進行寫操作時,文件操作標記會自動返回到文件末尾 |
F.truncate([size]) |
把文件裁成規定的大小,默認的是裁到當前文件操作標記的位置。如果size比文件的大小還要大,依據系統的不同可能是不改變文件,也可能是用0把文件補到相應的大小,也可能是以一些隨機的內容加上去。如果沒有指定 size,則從當前位置起截斷;截斷之后 size 后面的所有字符被刪除。 |
3.2 文件的讀取、創建、追加、刪除、清空
一,用python創建一個新文件,內容是0到9的整數,每個數字占一行
f = open('f.txt','w') for i in range(0,10): f.write(str(i)+'\n')
f.close()
二,文件內容追加,從0到9的10個隨機整數
import random f = open('f.txt','a') for i in range(0,10): f.write(str(random.randint(0,9))) f.write('\n') f.close()
三、文件內容追加,從0到9的隨機整數, 10個數字一行,共10行
import random f = open('f.txt','a') for i in range(0,10): for i in range(0,10): f.write(str(random.randint(0,9))) f.write('\n') f.close()
四、把標准輸出定向到文件
import sys sys.stdout = open('stdout.txt','w')
五,文件的讀寫
5.1,文件打開
f = file(name[, mode[, buffering]]) name 文件名 mode 選項,字符串 buffering 是否緩沖 (0=不緩沖,1=緩沖, >1的int數=緩沖區大小)
文件的讀寫屬性
file對象有自己的屬性和方法。先來看看file的屬性。(+和b可以和其他的字符組合成mode,例如rb以二進制只讀方式打開,mode參數是可選的,如果沒有默認為r)
(注意:文件打開之后,應當被及時關閉,可以查看f.closed屬性以確認文件是否被關閉)
mode |
function |
r |
只讀模式(默認,文件不存在,則發生異常)文件的指針將會放在文件的開頭 |
w |
只寫模式(可讀,文件不存在則創建,存在則刪除內容,再打開文件) |
a |
追加模式(只能寫,文件不存在則創建,存在則追加內容) |
r+ |
可讀寫模式(可讀,可寫,可追加),如果文件存在,則覆蓋當前文件指針所在位置的字符,如原來文件內容是"Hello,World",打開文件后寫入"hi"則文件內容會變成"hillo, World" |
b |
以二進制方式打開(如:FTP發送上傳ISO鏡像文件,linux可忽略,windows處理二進制文件時需標注) |
w+ |
先寫再讀(可讀,可寫,可追加) 如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。 |
a+ |
同a(可讀可寫,文件不存在則創建,存在則追加內容)。如果該文件已存在,文件指針將會放在文件的結尾。文件打開時會是追加模式。如果該文件不存在,創建新文件用於讀寫。 |
rb |
以二進制讀方式打開,只能讀文件 , 如果文件不存在,會發生異常 |
wb |
以二進制寫方式打開,只能寫文件, 如果文件不存在,創建該文件 |
ab |
二進制追寫文件。 從文件頂部讀取內容 從文件底部添加內容 不存在則創建 |
rt |
以文本讀方式打開,只能讀文件 , 如果文件不存在,會發生異常 |
wt |
以文本寫方式打開,只能讀文件 , 如果文件不存在,創建該文件。如果文件存在。先清空,再打開文件 |
at |
以文本讀寫方式打開,只能讀文件 , 如果文件不存在,創建該文件。如果文件存在。先清空,再打開文件 |
rb+ |
以二進制讀方式打開,可以讀、寫文件 , 如果文件不存在,會發生異常 |
wb+ |
以二進制寫方式打開,可以讀、寫文件, 如果文件不存在,創建該文件.如果文件存在。先清空,再打開文件 |
ab+ |
追讀寫二進制。從文件頂部讀取內容 從文件底部添加內容 不存在則創建 |
5.2,關閉文件
f.close()
當文件讀寫完畢后,應關閉文件
六,清空文件內容
f.truncate()
注意:僅當以 "r+" "rb+" "w" "wb" "wb+"等以可寫模式打開的文件才可以執行該功能
七,文件的指針定位與查詢
7.1,文件指針:
文件被打開后,其對象保存在 f 中, 它會記住文件的當前位置,以便於執行讀、寫操作,這個位置稱為文件的指針( 一個從文件頭部開始計算的字節數 long 類型 )。
7.2,文件打開時的位置:
以"r" "r+" "rb+" 讀方式, "w" "w+" "wb+"寫方式 打開的文件,
一開始,文件指針均指向文件的頭部。
7.3,獲取文件指針的值:
L = f.tell()
7.4,移動文件的指針
f.seek( 偏移量, 選項 ) 選項 =0 時, 表示將文件指針指向從文件頭部到 "偏移量"字節處。 選項 =1 時, 表示將文件指針指向從文件的當前位置,向后移動 "偏移量"字節。 選項 =2 時, 表示將文件指針指向從文件的尾部,,向前移動 "偏移量"字節。
八,從文件讀取內容
8.1,文本文件(以"rt"方式打開的文件)的讀取
s = f.readline( ) 返回值: s 是字符串,從文件中讀取的一行,含行結束符。 說明: (1) 如果 len( s ) =0 表示已到文件尾 (2) 如果是文件的最后一行,有可能沒有行結束符
8.2,二進制文件(以"rb"、"rb+"、"wb+" 方式打開的文件)的讀取
s = f.read( n ) 說明: (1) 如果 len( s ) =0 表示已到文件尾 (2) 文件讀取后,文件的指針向后移動 len(s) 字節。 (3)如果磁道已壞,會發生異常。
九,向文件寫入一個字符串
f.write( s ) 參數: s 要寫入的字符串 說明: (1)文件寫入后,文件的指針向后移動 len(s) 字節。 (2)如果磁道已壞,或磁盤已滿會發生異常。 返回值: s 是字符串,從文件中讀取的內容
十,刪除文件
import os os.remove(file)
import os
os.remove('s.txt')
3.3 python逐行讀取文件內容的兩種方法
方法一:
for line in open('f.txt'): print(line)
方法二:
f =open('f.txt','r') lines =f.readlines() for i in lines: print(i)
3.4文件定位
tell()方法告訴你文件內的當前位置, 換句話說,下一次的讀寫會發生在文件開頭這么多字節之后。
seek(offset [,from])方法改變當前文件的位置。Offset變量表示要移動的字節數。From變量指定開始移動字節的參考位置。
offset -- 偏移量,也就是代表需要移動偏移的字節數,注意是按照字節算的,字符編碼存每個字符所占的字節長度不一樣。
如“好好學習” 用gbk存是2個字節一個字,用utf-8就是3個字節,因此以gbk打開時,seek(4) 就把光標切換到了“飛”和“學”兩個字中間。
但如果是utf8,seek(4)會導致,拿到了飛這個字的一部分字節,打印的話會報錯,因為處理剩下的文本時發現用utf8處理不了了,因為編碼對不上了。少了一個字節。
如果from被設為0,這意味着將文件的開頭作為移動字節的參考位置。
如果設為1,則使用當前的位置作為參考位置。
如果它被設為2,那么該文件的末尾將作為參考位置。
例子:
#打開一個文件 f =open('f.txt','r+') str_read = f.read(10) print("讀取的字符串是:%s" % str_read) #查找當前位置 position = f.tell() print("當前位置: %s" %position) #把指針再次重新定位到文件開頭 position =f.seek(0,0) str_read =f.read(10) print("重新讀取讀取的字符串是:%s" % str_read) #關閉文件 f.close() 結果: 讀取的字符串是:2204513940 當前位置: 10 重新讀取讀取的字符串是:2204513940
3.5 重命名和刪除文件
Python的os模塊提供了幫你執行文件處理操作的方法,比如重命名和刪除文件。
要使用這個模塊,你必須先導入它,然后才可以調用相關的各種功能。
rename()方法:
rename()方法需要兩個參數,當前的文件名和新文件名。
語法:
os.rename(current_file_name, new_file_name)
例子:
import os #重命名文件f.txt問哦file.txt os.rename('f.txt','file.txt')
remove方法
你可以用remove()方法刪除文件,需要提供要刪除的文件名作為參數。
語法:
os.remove(file_name)
例子:
import os os.remove('stdout.txt')
3.6 文件修改
硬盤的存儲原理就是,當你把文件存到硬盤上,就在硬盤上划了一塊空間,存數據,等你下次打開這個文件,會seek到一個位置,每改一個字,就是把原來的覆蓋掉,如果要插入,是不可能的,因為后面的數據在硬盤上不會整體向后移動,所以就會出現當前這個情況,你要插入,卻變成把舊內容覆蓋掉。
所以修改文件,就不要在硬盤上修改,把內容全部讀到內存里,數據在內存里可以隨便增刪改查,修改之后,把內容全部寫回硬盤,把原來的數據全部覆蓋掉。
當然了,如果有些文件特別大,比如5G,那么一下吃掉這么大內存,非常浪費資源,所以更好的方法就是,如果不像占內存,那么就邊讀邊寫,也就是不修改源文件,但是可以打開丟文件的同時,生成一個新文件,邊從舊的里面讀,邊往新的里面寫,遇到需要修改的就改了再寫道新文件,這樣在內存里面一直只存一行內容,就不占內存了,但是也有一個缺點就是,雖然不占內存,但是占硬盤,每次修改,都要生成一份新文件,雖然改完后,可以把舊的覆蓋掉,但是在改的過程中,還是有兩份數據
3.6.1文件修改占硬盤
代碼:
f_name= 'file.txt' f_new_name = 'file_new.txt' old_str = '123' new_str = '123123123' f = open(f_name,'r',encoding='gbk') f_new = open(f_new_name,'w',encoding='gbk') for line in f: if old_str in line: line = line.replace(old_str,new_str) f_new.write(line) f.close() f_new.close()
上面的代碼會生成一個修改后的新文件,源文件不動,若想覆蓋源文件
代碼如下:
import os os.replace(file_new_name,file_name)
3.6.2文件修改占內存
這樣不需要占用兩個文件,只需要寫一個文件,直接r+就可以,但是會出現一個問題,就是要是以前文件內容多,現在少了,那會出錯,為了避免這種錯誤的發生,使用truncate()就ok
userinfo.txt的內容:
1:www.baidu.com 2:www.google.com 3:www.tencent.com 4:www.tianmao.com 5:www.jingdong.com
f.truncate([size]) 把文件裁成規定的大小,默認的是裁到當前文件操作標記的位置。 如果size比文件的大小還要大,依據系統的不同可能是不改變文件, 也可能是用0把文件補到相應的大小,也可能是以一些隨機的內容加上去。
file = open('userinfo.txt','r+',encoding='utf-8') print('文件名為:',file.name) line =file.readline() print('讀取第一行:%s' %line) #截斷剩下的字符串 file.truncate() #嘗試再次讀取數據 line =file.readline() print('讀取數據:%s' %line) file.close() # 結果: # 文件名為: userinfo.txt # 讀取第一行:1:www.baidu.com # # 讀取數據:2:www.google.com
file = open('userinfo.txt','r+',encoding='utf-8') print('文件名為:',file.name) line =file.readline() print('讀取第一行:%s' %line) #截斷10的字符串 file.truncate(10) #嘗試再次讀取數據 line =file.readline() print('讀取數據:%s' %line) file.close() 結果: 文件名為: userinfo.txt 讀取第一行:1:www.baid 讀取數據:
四,補充內容
4.1 各種系統操作
注意:雖然python中提供了各種拼接目錄的函數,但是,函數並不能保證字符編碼不出問題,很大可能導致程序錯誤。所以最好還是自己拼接。
python中對文件、文件夾(文件操作函數)的操作需要涉及到os模塊和shutil模塊。
得到當前工作目錄,即當前Python腳本工作的目錄路徑: os.getcwd() 返回指定目錄下的所有文件和目錄名:os.listdir() 函數用來刪除一個文件:os.remove() 刪除多個目錄:os.removedirs(r“c:\python”) 檢驗給出的路徑是否是一個文件:os.path.isfile() 檢驗給出的路徑是否是一個目錄:os.path.isdir() 判斷是否是絕對路徑:os.path.isabs() 檢查是否快捷方式os.path.islink ( filename ) 檢驗給出的路徑是否真地存:os.path.exists() 返回一個路徑的目錄名和文件名:os.path.split() eg os.path.split('/home/swaroop/byte/code/poem.txt') 結果:('/home/swaroop/byte/code', 'poem.txt') 分離擴展名:os.path.splitext() 獲取路徑名:os.path.dirname() 獲取文件名:os.path.basename() 運行shell命令: os.system() 讀取和設置環境變量:os.getenv() 與os.putenv() 給出當前平台使用的行終止符:os.linesep Windows使用'\r\n',Linux使用'\n'而Mac使用'\r' 指示你正在使用的平台:os.name 對於Windows,它是'nt',而對於Linux/Unix用戶,它是'posix' 重命名:os.rename(old, new) 創建多級目錄:os.makedirs(r“c:\python\test”) 創建單個目錄:os.mkdir(“test”) 獲取文件屬性:os.stat(file) 修改文件權限與時間戳:os.chmod(file) 終止當前進程:os.exit() 獲取文件大小:os.path.getsize(filename)
4.2 各種目錄操作
os.mkdir("file") 創建目錄 復制文件: shutil.copyfile("oldfile","newfile") oldfile和newfile都只能是文件 shutil.copy("oldfile","newfile") oldfile只能是文件夾,newfile可以是文件,也可以是目標目錄 復制文件夾: shutil.copytree("olddir","newdir") olddir和newdir都只能是目錄,且newdir必須不存在 重命名文件(目錄) os.rename("oldname","newname") 文件或目錄都是使用這條命令 移動文件(目錄) shutil.move("oldpos","newpos") 刪除文件 os.remove("file") 刪除目錄 os.rmdir("dir")只能刪除空目錄 shutil.rmtree("dir") 空目錄、有內容的目錄都可以刪 轉換目錄 os.chdir("path") 換路徑 ps: 文件操作時,常常配合正則表達式: img_dir = img_dir.replace('\\','/')
五,文件處理習題
文件操作分為讀,寫,修改
5.1 讀文件
舉例如下:
f = open(file=‘userinfo.txt',mode='r',encoding='utf-8') data = f.read() f.close()
上述操作語法解釋如下:
file=‘userinfo.txt' 表示文件路徑 mode='r' 表示只讀(可以修改為其他) encoding='utf-8' 表示將硬盤上的 0101010 按照utf-8的規則去“斷句”, 再將“斷句”后的每一段0101010轉換成unicode的 01010101,unicode對照表中有01010101和 字符的對應關系。 f.read() 表示讀取所有內容,內容是已經轉換完畢的字符串。 f.close() 表示關閉文件
再看一個例子:
f = open(file='userinfo.txt',mode='rb') data = f.read() f.close()
上述操作語法解釋:
file='userinfo.txt' 表示文件路徑 mode='rb' 表示只讀(可以修改為其他) f.read() 表示讀取所有內容,內容是硬盤上原來以某種編碼保存010101010,即:某種編碼格式的字節類型 f.close() 表示關閉文件
問:兩個例子的區別在哪?
答:在於示例2打開文件時並未指定encoding,這是為何?是因為直接以rb模式打開了文件 ,rb是指二進制模式,數據讀到內存里直接是bytes格式,如果想內容,還需要手動decode,因此在文件打開階段,不需要指定編碼
問:假設你不知道你要處理的文件是什么編碼可怎么辦?
import chardet f = open('log',mode='rb') data = f.read() f.close() result = chardet.detect(open('log',mode='rb').read()) print(result)
輸出:
{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
注意:
- 文件操作時,以 “r”或“rb” 模式打開,則只能讀,無法寫入;
- 硬盤上保存的文件都是某種編碼的0101010,打開時需要注意:
- rb,直接讀取文件保存時原生的0101010,在Python中用字節類型表示
- r和encoding,讀取硬盤的0101010,並按照encoding指定的編碼格式進行斷句,再將“斷句”后的每一段0101010轉換成unicode的 010101010101,在Python中用字符串類型表示
5.2文件操作的其他功能
def fileno(self, *args, **kwargs): # real signature unknown 返回文件句柄在內核中的索引值,以后做IO多路復用時可以用到 def flush(self, *args, **kwargs): # real signature unknown 把文件從內存buffer里強制刷新到硬盤 def readable(self, *args, **kwargs): # real signature unknown 判斷是否可讀 def readline(self, *args, **kwargs): # real signature unknown 只讀一行,遇到\r or \n為止 def seek(self, *args, **kwargs): # real signature unknown 把操作文件的光標移到指定位置 *注意seek的長度是按字節算的, 字符編碼存每個字符所占的字節長度不一樣。 如“路飛學城” 用gbk存是2個字節一個字,用utf-8就是3個字節,因此以gbk打開時,seek(4) 就把光標切換到了“飛”和“學”兩個字中間。 但如果是utf8,seek(4)會導致,拿到了飛這個字的一部分字節,打印的話會報錯,因為處理剩下的文本時發現用utf8處理不了了,因為編碼對不上了。少了一個字節 def seekable(self, *args, **kwargs): # real signature unknown 判斷文件是否可進行seek操作 def tell(self, *args, **kwargs): # real signature unknown 返回當前文件操作光標位置 def truncate(self, *args, **kwargs): # real signature unknown 按指定長度截斷文件 *指定長度的話,就從文件開頭開始截斷指定長度,不指定長度的話,就從當前位置到文件尾部的內容全去掉。 def writable(self, *args, **kwargs): # real signature unknown 判斷文件是否可寫
5.3 例題
讀文件找到第9個字符,華 ,找到第二行的 實,刪除最后一行 寫入文件
桃之夭夭,灼灼其華。之子於歸,宜其室家。
桃之夭夭,有蕡其實。之子於歸,宜其家室。
桃之夭夭,其葉蓁蓁。之子於歸,宜其家人。
# 2.讀文件找到第9個字符,華 ,找到第二行的 實,刪除最后一行 寫入文件 # 桃之夭夭,灼灼其華。之子於歸,宜其室家。 # 桃之夭夭,有蕡其實。之子於歸,宜其家室。 # 桃之夭夭,其葉蓁蓁。之子於歸,宜其家人。 f = open('poem.txt', 'r+', encoding='utf-8') # f.seek(3*8) # print(f.read(1)) # f.seek(3*28+2) # print(f.read(1)) data_list = f.readlines() print(data_list) data_list.pop() print(data_list) f.seek(0) f.truncate() f.write(''.join(data_list))
5.4 例題:讀取一個文件下的所有文件
import os def read_image(image_dir): photofile_list = os.listdir(image_dir) for i in range(len(photofile_list)): photo_path = os.path.join(image_dir,photofile_list[i]) print(photo_path)
5.5 例題:判定目錄是否存在,若不存在即創建
import os if not os.path.isdir(dir_name): os.makedir(dir_name)
os.mkdir()創建路徑中的最后一級目錄,而如果之前的目錄不存在並且也需要創建的話,就會報錯。
os.makedirs()創建多層目錄,如果中間目錄都不存在的話,會自動創建。
5.6 讀取文件的行數
import csv a=open("Train_A_wear.csv","r") b=len(a.readlines()) print(b)
5.7 批量重命名文件名
import os class BatchRename(): # 批量重命名文件夾中的圖片文件 def __init__(self): self.path_A = '../binary_variable/A' self.path_B = '../binary_variable/B' def rename(self): filelist_A = os.listdir(self.path_A) filelist_B = os.listdir(self.path_B) total_num_A = len(filelist_A) total_num_B = len(filelist_B) i = 1 for item in filelist_A: # 初始的圖片的格式為jpg格式的,或者png if item.endswith('.jpg') or item.endswith('png'): src = os.path.join(os.path.abspath(self.path_A), item) # 處理后的格式也為jpg格式的,當然這里也可以改成png格式 dst = os.path.join(os.path.abspath(self.path_A), str(i) + '.jpg') try: os.rename(src, dst) i = i + 1 except: continue for item in filelist_B: # 初始的圖片的格式為jpg格式的,或者png if item.endswith('.jpg') or item.endswith('png'): src = os.path.join(os.path.abspath(self.path_B), item) # 處理后的格式也為jpg格式的,當然這里也可以改成png格式 dst = os.path.join(os.path.abspath(self.path_B), str(i) + '.jpg') try: os.rename(src, dst) i = i + 1 except: continue if __name__ == '__main__': demo = BatchRename() demo.rename()