python進(jìn)階:第四章(文件I/O高效處理)

問(wèn)題一:如何讀寫文本文件?

問(wèn)題內(nèi)容:
某文件編碼格式已知(如UTF-8,GBK,BIG5),在python2.X和python3.X中分別如何讀取該文件?

解決方案:
我們首先明確下2到3的一些語(yǔ)義變化:
在python2中字符串有兩種形式:str和Unicode。str表面上抽象的是字符串,實(shí)際上抽象的是連續(xù)的字節(jié),對(duì)應(yīng)python3中的bytes,只有這些字節(jié)存儲(chǔ)到物理設(shè)備上。而Unicode才是真正的字符串,對(duì)應(yīng)python3中的str。

python2.x:寫入文件前對(duì)Unicode編碼,讀入文件后對(duì)二進(jìn)制字符串解碼。
python3.x:open函數(shù)指定't'的文本模式,encoding指定編碼格式。

在python2下,我沒(méi)安裝python2,來(lái)看截圖吧:

Paste_Image.png

在python3中:

在python3中定義bytes需要前面加小 b ,而python2只要字節(jié)串。
In [1]: b'dfghjdfg'
Out[1]: b'dfghjdfg'
在python3中定義Unicode,前面不需要加小 u ,而python2中需要。
In [2]: '你好'
Out[2]: '你好'

文本文件的操作:

In [2]: f = open('py3.txt','wt',encoding='utf8')

In [3]: f.write('你好,我是王二小')
Out[3]: 8

In [4]: f.close()

In [5]: f = open('py3.txt','rt',encoding='utf8')

In [6]: s = f.read()

In [7]: print(s)
你好,我是王二小

文件寫入的時(shí)候是以字節(jié)寫入,不過(guò)編碼為了utf8,文件讀取的時(shí)候也是轉(zhuǎn)碼為utf8。

問(wèn)題二:如何處理二進(jìn)制文件

這段后續(xù)補(bǔ)充

In [9]: f = open('syst.wav','rb')

In [10]: info  = f.read(44)

In [11]: info
Out[11]: b'RIFF\xf4\x04\x92\x02WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00D\xac\x00\x00\x10\xb1\x02\x00\x04\x00\x10\x00data\xd0\x04\x92\x02'

In [12]: import struct

In [13]: struct.unpack?
Docstring:
unpack(fmt, buffer) -> (v1, v2, ...)

Return a tuple containing values unpacked according to the format string
fmt.  The buffer's size in bytes must be calcsize(fmt). See help(struct)
for more on format strings.
Type:      builtin_function_or_method

In [14]: struct.unpack('h','\x01\x02')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-4b76b634ada8> in <module>()
----> 1 struct.unpack('h','\x01\x02')

TypeError: a bytes-like object is required, not 'str'

In [15]: struct.unpack('h',info[22:24])
Out[15]: (2,)

In [16]: struct.unpack('i',info[24:28])
Out[16]: (44100,)

問(wèn)題三:如何設(shè)置文件緩沖

問(wèn)題內(nèi)容:
將文件內(nèi)容寫入到硬件設(shè)備時(shí),使用系統(tǒng)調(diào)用,這類I/O操作的時(shí)間很長(zhǎng)。為了減少I/O操作的次數(shù),文件通常使用緩沖區(qū)。(有足夠多的數(shù)據(jù)才進(jìn)行系統(tǒng)調(diào)用)文件的緩沖行為,分為全緩沖,行緩沖,無(wú)緩沖。如何沒(méi)有緩沖會(huì)造成資源的浪費(fèi)(未寫滿一個(gè)塊,依舊寫入,使用緩沖,只有當(dāng)寫滿之后,才會(huì)插入)
如何設(shè)置python中文件的緩沖行為?

解決方案:
全緩沖:open函數(shù)的buffering設(shè)置為大于1的整數(shù)n,n為緩沖區(qū)大小
行緩沖:open函數(shù)的buffering設(shè)置為1
無(wú)緩沖:open函數(shù)的buffering設(shè)置為0

關(guān)于行緩沖:

In [29]: f = open('demo3.txt','w',buffering=1) 

In [30]: f.write('abcd') 
Out[30]: 4

In [31]: f.write('1234') 
Out[31]: 4

In [32]: f.write('\n') 
Out[32]: 1

我們使用tail 監(jiān)控輸入,只有我們輸入了 '\n'之后,數(shù)據(jù)才真正的寫入。
tail  -f  demo3.txt  
abcd1234

關(guān)于無(wú)緩沖:

In [36]: f = open('demo4.txt','w',buffering=0) 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-36-7b816614dabf> in <module>()
----> 1 f = open('demo4.txt','w',buffering=0)

ValueError: can't have unbuffered text I/O

In [37]: f = open('demo4.txt','wb',buffering=0)

python3中文本文件不能設(shè)置buffering = 0,而二進(jìn)制文件可以。

問(wèn)題四:如何將文件映射到內(nèi)存?

問(wèn)題內(nèi)容:
1,在訪問(wèn)某些二進(jìn)制文件時(shí)(通常使用read()和write()方法的時(shí)候,都是以流的形式讀寫,一個(gè)字節(jié)一個(gè)字節(jié)的順序進(jìn)行),希望能把文件映射到內(nèi)存中,(像數(shù)組一樣訪問(wèn))可以實(shí)現(xiàn)隨機(jī)訪問(wèn)。(framebuffer設(shè)備文件)

2,某些嵌入式設(shè)備,寄存器被編址到內(nèi)存地址空間,我們可以映射/dev/mem某范圍,去訪問(wèn)這些寄存器。

3,如果多個(gè)進(jìn)程映射同一個(gè)文件,還能實(shí)現(xiàn)進(jìn)程通信的目的。

解決方案:
使用標(biāo)準(zhǔn)庫(kù)中mmap()函數(shù),它需要一個(gè)打開的文件描述符作為參數(shù)。

首先我們創(chuàng)建大小為1M,全部?jī)?nèi)容為0的二進(jìn)制文件。

dd  if=/dev/zero  of=demo.bin bs=1024 count=1024 

我們查看文件內(nèi)容:

od -x demo.bin 
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
4000000

我們查看下mmap函數(shù)的使用:

In [1]: import mmap 

In [2]: mmap.mmap 
Out[2]: mmap.mmap

In [3]: mmap.mmap?
Init signature: mmap.mmap(self, /, *args, **kwargs)
Docstring:     
Windows: mmap(fileno, length[, tagname[, access[, offset]]])

Maps length bytes from the file specified by the file handle fileno,
and returns a mmap object.  If length is larger than the current size
of the file, the file is extended to contain length bytes.  If length
is 0, the maximum length of the map is the current size of the file,
except that if the file is empty Windows raises an exception (you cannot
create an empty mapping on Windows).

Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])

Maps length bytes from the file specified by the file descriptor fileno,
and returns a mmap object.  If length is 0, the maximum length of the map
will be the current size of the file when mmap is called.
flags specifies the nature of the mapping. MAP_PRIVATE creates a
private copy-on-write mapping, so changes to the contents of the mmap
object will be private to this process, and MAP_SHARED creates a mapping
that's shared with all other processes mapping the same areas of the file.
The default value is MAP_SHARED.

To map anonymous memory, pass -1 as the fileno (both versions).
File:           /usr/local/lib/python3.5/lib-dynload/mmap.cpython-35m-x86_64-linux-gnu.so
Type:           type

函數(shù)的第一個(gè)參數(shù)為文件的描述符,由系統(tǒng)調(diào)用的open()函數(shù)產(chǎn)生。


In [4]: import os

In [5]: os.open?
Signature: os.open(path, flags, mode=511, *, dir_fd=None)
Docstring:
Open a file for low level IO.  Returns a file descriptor (integer).

If dir_fd is not None, it should be a file descriptor open to a directory,
  and path should be relative; path will then be relative to that directory.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.
Type:      builtin_function_or_method

在這里我們使用python的open()函數(shù)和fileno()函數(shù)產(chǎn)生文件描述符:

In [7]: f = open("demo.bin",'r+b')

In [8]: f.fileno()
Out[8]: 11

函數(shù)的第二個(gè)參數(shù)是指映射大小,當(dāng)為0的時(shí)候?yàn)槿坑成洹5谌齻€(gè)參數(shù)為權(quán)限。

In [9]: mmap.mmap(f.fileno(),0,access=mmap.
                  mmap.ACCESS_COPY           mmap.MAP_ANONYMOUS         mmap.PAGESIZE              
                  mmap.ACCESS_READ           mmap.MAP_DENYWRITE         mmap.PROT_EXEC             
                  mmap.ACCESS_WRITE          mmap.MAP_EXECUTABLE        mmap.PROT_READ             
                  mmap.ALLOCATIONGRANULARITY mmap.MAP_PRIVATE           mmap.PROT_WRITE            
                  mmap.error                 mmap.MAP_SHARED                                       
                  mmap.MAP_ANON              mmap.mmap

以上為權(quán)限的列表。

In [9]: m = mmap.mmap(f.fileno(),0,access=mmap.ACCESS_WRITE)

In [10]: type(m) 
Out[10]: mmap.mmap

In [11]: m[0] 
Out[11]: 0

In [12]: m[10:20] 
Out[12]: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

返回一個(gè)mmap對(duì)象,把內(nèi)存映射為一個(gè)數(shù)組,可以進(jìn)行切片等操作。

問(wèn)題五:如何訪問(wèn)文件的狀態(tài)?

問(wèn)題內(nèi)容:
1,文件的類型(普通文件,目錄,符號(hào)連接,設(shè)備文件。。。)
2,文件的訪問(wèn)權(quán)限
3,文件的最后的訪問(wèn)/修改/節(jié)點(diǎn)狀態(tài)更改時(shí)間
4,普通文件的大小

解決方法:
方法一:使用標(biāo)準(zhǔn)庫(kù)os模塊下的三個(gè)系統(tǒng)調(diào)用stat,fstat,lstat獲取文件狀態(tài)。
方法二:標(biāo)準(zhǔn)庫(kù)中os.path下一些函數(shù),使用起來(lái)更加簡(jiǎn)潔

問(wèn)題六:如何使用臨時(shí)文件?

問(wèn)題內(nèi)容:
某項(xiàng)目,我們從傳感器采集數(shù)據(jù),每收集1G數(shù)據(jù)后,做數(shù)據(jù)分析,最終只保存分析結(jié)果。這樣很大的臨時(shí)數(shù)據(jù)如果常駐內(nèi)存,將消耗大量?jī)?nèi)存資源,我們可以使用臨時(shí)文件存儲(chǔ)這些臨時(shí)數(shù)據(jù)(外部存儲(chǔ))。
臨時(shí)文件不用命名,且關(guān)閉后會(huì)自動(dòng)被刪除。

解決方案:
可以使用標(biāo)準(zhǔn)庫(kù)中tempfile下的temporaryFile,NamedTemporaryFile。

In [1]: from tempfile import TemporaryFile ,NamedTemporaryFile

In [2]: TemporaryFile?
Signature: TemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None)
Docstring:
Create and return a temporary file.
Arguments:
'prefix', 'suffix', 'dir' -- as for mkstemp.
'mode' -- the mode argument to io.open (default "w+b").
'buffering' -- the buffer size argument to io.open (default -1).
'encoding' -- the encoding argument to io.open (default None)
'newline' -- the newline argument to io.open (default None)
The file is created as mkstemp() would do it.

Returns an object with a file-like interface.  The file has no
name, and will cease to exist when it is closed.
File:      /usr/local/lib/python3.5/tempfile.py
Type:      function

默認(rèn)是以二進(jìn)制打開文件的

In [3]: f = TemporaryFile('w+t') 

In [4]: f.write('abcdef' * 10000)
Out[4]: 60000

In [5]: f.seek(0) 
Out[5]: 0

In [6]: f.read(100) 
Out[6]: 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd'

使用TemporaryFile()創(chuàng)建的文件,不能在文件系統(tǒng)中查找到,只能通過(guò)文件對(duì)象 f 訪問(wèn)。

使用NamedTemporaryFile()創(chuàng)建的臨時(shí)文件則可以在文件系統(tǒng)中查找得到。

In [7]: NamedTemporaryFile?
Signature: NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True)
Docstring:
Create and return a temporary file.
Arguments:
'prefix', 'suffix', 'dir' -- as for mkstemp.
'mode' -- the mode argument to io.open (default "w+b").
'buffering' -- the buffer size argument to io.open (default -1).
'encoding' -- the encoding argument to io.open (default None)
'newline' -- the newline argument to io.open (default None)
'delete' -- whether the file is deleted on close (default True).
The file is created as mkstemp() would do it.

Returns an object with a file-like interface; the name of the file
is accessible as its 'name' attribute.  The file will be automatically
deleted when it is closed unless the 'delete' argument is set to False.
File:      /usr/local/lib/python3.5/tempfile.py
Type:      function

In [8]: ntf = NamedTemporaryFile("w+t") 

In [9]: ntf.
  File "<ipython-input-9-adee75e8b522>", line 1
    ntf.
        ^
SyntaxError: invalid syntax
In [10]: ntf.
              ntf.close  ntf.file   
              ntf.delete ntf.name 

In [10]: ntf.name
Out[10]: '/tmp/tmp7ggfe3br'
name屬性就是文件路徑

In [11]: ntf = NamedTemporaryFile("w+t") 

In [12]: ntf.name
Out[12]: '/tmp/tmp9gbuotxm'

我們發(fā)現(xiàn)兩次的name屬性值不同,是因?yàn)槊看侮P(guān)閉之后,前面的文件會(huì)被刪除掉

我們可以指定Signature: NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True)
函數(shù)參數(shù)里面的 delete= 為False。則關(guān)閉后之前的文件不會(huì)被刪除。

In [13]: ntf = NamedTemporaryFile("w+t", delete=False) 

In [14]: ntf.name 
Out[14]: '/tmp/tmpgfps1wco'

In [15]: ntf = NamedTemporaryFile("w+t", delete=False) 

In [16]: ntf.name 
Out[16]: '/tmp/tmpbm6a_0ze'

tmpbm6a_0ze
tmpgfps1wco

我們可以看到之前創(chuàng)建的tmpgfps1wco并沒(méi)有在文件關(guān)閉之后刪除。
使用NamedTemporaryFile()創(chuàng)建的文件可以被多個(gè)進(jìn)程訪問(wèn),而被TemporaryFile()創(chuàng)建的文件只能被當(dāng)前進(jìn)程訪問(wèn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 定義類并創(chuàng)建實(shí)例 在Python中,類通過(guò) class 關(guān)鍵字定義。以 Person 為例,定義一個(gè)Person類...
    績(jī)重KF閱讀 3,976評(píng)論 0 13
  • 4.1 字符串的語(yǔ)義 在python2 和 python3中,對(duì)字符串的語(yǔ)義是有著很大的區(qū)別的。 python2s...
    Lemon_Home閱讀 564評(píng)論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,991評(píng)論 19 139
  • 我有一輛單車 后車座空空蕩蕩 后車輪吱吱嘎嘎 不停轉(zhuǎn)動(dòng)著寂寞 當(dāng)我可以接你放學(xué) 便安了一個(gè)后車凳 車凳全是愛做的海...
    倫小讓閱讀 113評(píng)論 4 1
  • 看著自己在屏幕上敲下的文字,刪了又寫,寫了又刪。仿佛回到課堂上的無(wú)命題作文,給你足夠的自由盡情發(fā)揮,卻反而不知如何...
    文大人頻道閱讀 217評(píng)論 0 0