Python中檔案I/O高效操作處理的技巧

pythontab發表於2017-02-04

如何讀寫文字檔案?

實際案例

某文字檔案編碼格式已直(如UTF-8,GBK,BIG5),在python2.x和python3.x中分別如何讀取這些檔案?

解決方案

注意區分python2和python3中的差別

字串的語義發生了變化:

python2python3
strbytes
unicodestr


python2.x 寫入檔案前對 unicode 編碼,讀入檔案後對二進位制字串解碼

>>> f = open('py2.txt', 'w')
>>> s = u'你好'
>>> f.write(s.encode('gbk'))
>>> f.close()
>>> f = open('py2.txt', 'r')
>>> t = f.read()
>>> print t.decode('gbk')

你好

python3.x 中 open 函式指定 t 的文字模式, encoding 指定編碼格式

>>> f = open('py3.txt', 'wt', encoding='utf-8')
>>> f.write('你好')
2
>>> f.close()
>>> f = open('py3.txt', 'rt', encoding='utf-8')
>>> s = f.read()
>>> s
'你好'

如何設定檔案的緩衝

實際案例

將檔案內容寫入到硬碟裝置時,使用系統呼叫,這類I/O操作的時間很長,為了減少I/O操作的次數,檔案通常使用緩衝區(有足夠多的資料才進行系統呼叫),檔案的快取行為,分為全緩衝、行快取、無緩衝。

如何設定Python中檔案物件的緩衝行文?

解決方案

全緩衝: open 函式的 buffering 設定為大於1的整數n,n為緩衝區大小

>>> f = open('demo2.txt', 'w', buffering=2048)
>>> f.write('+' * 1024)
>>> f.write('+' * 1023)
# 大於2048的時候就寫入檔案
>>> f.write('-' * 2)
>>> f.close()

行緩衝: open 函式的 buffering 設定為1

>>> f = open('demo3.txt', 'w', buffering=1)
>>> f.write('abcd')
>>> f.write('1234')
# 只要加上\n就寫入檔案中
>>> f.write('\n')
>>> f.close()

無緩衝: open 函式的 buffering 設定為0

>>> f = open('demo4.txt', 'w', buffering=0)
>>> f.write('a')
>>> f.write('b')
>>> f.close()

如何將檔案對映到記憶體?

實際案例

在訪問某些二進位制檔案時,希望能把檔案對映到記憶體中,可以實現隨機訪問.(framebuffer裝置檔案)

某些嵌入式裝置,暫存器唄編址到記憶體地址空間,我們可以對映 /dev/mem 某範圍,去訪問這些暫存器

如果多個程式對映到同一個檔案,還能實現程式通訊的目的

解決方案

使用標準庫中的 mmap 模組的 mmap() 函式,它需要一個開啟的檔案描述符作為引數

建立如下檔案

[root@pythontab.com ~]# dd if=/dev/zero of=demo.bin bs=1024 count=1024
1024+0 records in
1024+0 records out
1048576 bytes (1.0 MB) copied, 0.00380084 s, 276 MB/s
# 以十六進位制格式檢視檔案內容
[root@pythontab.com ~]# od -x demo.bin 
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
4000000
>>> import mmap
>>> import os
>>> f = open('demo.bin','r+b')
# 獲取檔案描述符
>>> f.fileno()
3
>>> m = mmap.mmap(f.fileno(),0,access=mmap.ACCESS_WRITE)
>>> type(m)
<type 'mmap.mmap'>
# 可以透過索引獲取內容
>>> m[0]
'\x00'
>>> m[10:20]
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# 修改內容
>>> m[0] = '\x88'

檢視

[root@pythontab.com ~]# od -x demo.bin 
0000000 0088 0000 0000 0000 0000 0000 0000 0000
0000020 0000 0000 0000 0000 0000 0000 0000 0000
*
4000000

修改切片

>>> m[4:8] = '\xff' * 4

檢視

[root@pythontab.com ~]# od -x demo.bin 
0000000 0088 0000 ffff ffff 0000 0000 0000 0000
0000020 0000 0000 0000 0000 0000 0000 0000 0000
*
4000000
>>> m = mmap.mmap(f.fileno(),mmap.PAGESIZE * 8,access=mmap.ACCESS_WRITE,offset=mmap.PAGESIZE * 4) 
>>> m[:0x1000] = '\xaa' * 0x1000

檢視

[root@pythontab.com ~]# od -x demo.bin 
0000000 0088 0000 ffff ffff 0000 0000 0000 0000
0000020 0000 0000 0000 0000 0000 0000 0000 0000
*
0040000 aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa
*
0050000 0000 0000 0000 0000 0000 0000 0000 0000
*
4000000

如何訪問檔案的狀態?

實際案例

在某些專案中,我們需要獲得檔案狀態,例如:

檔案的型別(普通檔案、目錄、符號連結、裝置檔案…)

檔案的訪問許可權

檔案的最後的訪問/修改/節點狀態更改時間

普通檔案的大小

…..

解決方案

當前目錄有如下檔案

[root@pythontab.com 2017]# ll
total 4
drwxr-xr-x 2 root root 4096 Sep 16 11:35 dirs
-rw-r--r-- 1 root root 0 Sep 16 11:35 files
lrwxrwxrwx 1 root root 37 Sep 16 11:36 lockfile -> /tmp/qtsingleapp-aegisG-46d2-lockfile

系統呼叫

標準庫中的os模組下的三個系統呼叫 stat 、 fstat 、 lstat 獲取檔案狀態

>>> import os
>>> s = os.stat('files')
>>> s
posix.stat_result(st_mode=33188, st_ino=267646, st_dev=51713L, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1486197100, st_mtime=1486197100, st_ctime=1486197100)
>>> s.st_mode
33188
>>> import stat
# stat有很多S_IS..方法來判斷檔案的型別
>>> stat.S_ISDIR(s.st_mode)
False
# 普通檔案
>>> stat.S_ISREG(s.st_mode)
True

獲取檔案的訪問許可權,只要大於0就為真

>>> s.st_mode & stat.S_IRUSR
256
>>> s.st_mode & stat.S_IXGRP
0
>>> s.st_mode & stat.S_IXOTH
0

獲取檔案的修改時間

# 訪問時間
>>> s.st_atime
1486197100.3384446
# 修改時間
>>> s.st_mtime
1486197100.3384446
# 狀態更新時間
>>> s.st_ctime
1486197100.3384446

將獲取到的時間戳進行轉換

>>> import time
>>> time.localtime(s.st_atime)
time.struct_time(tm_year=2016, tm_mon=9, tm_mday=16, tm_hour=11, tm_min=35, tm_sec=47, tm_wday=4, tm_yday=260, tm_isdst=0)

獲取普通檔案的大小

>>> s.st_size
0

快捷函式

標準庫中 os.path 下的一些函式,使用起來更加簡潔

檔案型別判斷

>>> os.path.isdir('dirs') 
True
>>> os.path.islink('lockfile')
True
>>> os.path.isfile('files') 
True

檔案三個時間

>>> os.path.getatime('files')
1486197100.3384445
>>> os.path.getmtime('files')
1486197100.3384445
>>> os.path.getctime('files')
1486197100.3384445

獲取檔案大小

>>> os.path.getsize('files') 
0

如何使用臨時檔案?

實際案例

某專案中,我們從感測器採集資料,每收集到1G資料後,做資料分析,最終只儲存分析結果,這樣很大的臨時資料如果常駐記憶體,將消耗大量記憶體資源,我們可以使用臨時檔案儲存這些臨時資料(外部儲存)

臨時檔案不用命名,且關閉後會自動被刪除

解決方案

使用標準庫中的 tempfile 下的 TemporaryFile, NamedTemporaryFile

>>> from tempfile import TemporaryFile, NamedTemporaryFile
# 訪問的時候只能透過物件f來進行訪問
>>> f = TemporaryFile()
>>> f.write('abcdef' * 100000)
# 訪問臨時資料
>>> f.seek(0)
>>> f.read(100)
'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd'
>>> ntf = NamedTemporaryFile()
# 如果要讓每次建立NamedTemporaryFile()物件時不刪除檔案,可以設定NamedTemporaryFile(delete=False)
>>> ntf.name
# 返回當前臨時檔案在檔案系統中的路徑
'/tmp/tmppNvNA6'


相關文章