python进阶:第四章(文件I/O高效处理)

问题一:如何读写文本文件?

问题内容:
某文件编码格式已知(如UTF-8,GBK,BIG5),在python2.X和python3.X中分别如何读取该文件?

解决方案:
我们首先明确下2到3的一些语义变化:
在python2中字符串有两种形式:str和Unicode。str表面上抽象的是字符串,实际上抽象的是连续的字节,对应python3中的bytes,只有这些字节存储到物理设备上。而Unicode才是真正的字符串,对应python3中的str。

python2.x:写入文件前对Unicode编码,读入文件后对二进制字符串解码。
python3.x:open函数指定't'的文本模式,encoding指定编码格式。

在python2下,我没安装python2,来看截图吧:

python进阶:第四章(文件I/O高效处理)_第1张图片
Paste_Image.png

在python3中:

在python3中定义bytes需要前面加小 b ,而python2只要字节串。
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)
你好,我是王二小

文件写入的时候是以字节写入,不过编码为了utf8,文件读取的时候也是转码为utf8。

问题二:如何处理二进制文件

这段后续补充

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)
 in ()
----> 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,)

问题三:如何设置文件缓冲

问题内容:
将文件内容写入到硬件设备时,使用系统调用,这类I/O操作的时间很长。为了减少I/O操作的次数,文件通常使用缓冲区。(有足够多的数据才进行系统调用)文件的缓冲行为,分为全缓冲,行缓冲,无缓冲。如何没有缓冲会造成资源的浪费(未写满一个块,依旧写入,使用缓冲,只有当写满之后,才会插入)
如何设置python中文件的缓冲行为?

解决方案:
全缓冲:open函数的buffering设置为大于1的整数n,n为缓冲区大小
行缓冲:open函数的buffering设置为1
无缓冲:open函数的buffering设置为0

关于行缓冲:

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 监控输入,只有我们输入了 '\n'之后,数据才真正的写入。
tail  -f  demo3.txt  
abcd1234

关于无缓冲:

In [36]: f = open('demo4.txt','w',buffering=0) 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
 in ()
----> 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中文本文件不能设置buffering = 0,而二进制文件可以。

问题四:如何将文件映射到内存?

问题内容:
1,在访问某些二进制文件时(通常使用read()和write()方法的时候,都是以流的形式读写,一个字节一个字节的顺序进行),希望能把文件映射到内存中,(像数组一样访问)可以实现随机访问。(framebuffer设备文件)

2,某些嵌入式设备,寄存器被编址到内存地址空间,我们可以映射/dev/mem某范围,去访问这些寄存器。

3,如果多个进程映射同一个文件,还能实现进程通信的目的。

解决方案:
使用标准库中mmap()函数,它需要一个打开的文件描述符作为参数。

首先我们创建大小为1M,全部内容为0的二进制文件。

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

我们查看文件内容:

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

我们查看下mmap函数的使用:

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

函数的第一个参数为文件的描述符,由系统调用的open()函数产生。


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()函数和fileno()函数产生文件描述符:

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

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

函数的第二个参数是指映射大小,当为0的时候为全部映射。第三个参数为权限。

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

以上为权限的列表。

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'

返回一个mmap对象,把内存映射为一个数组,可以进行切片等操作。

问题五:如何访问文件的状态?

问题内容:
1,文件的类型(普通文件,目录,符号连接,设备文件。。。)
2,文件的访问权限
3,文件的最后的访问/修改/节点状态更改时间
4,普通文件的大小

解决方法:
方法一:使用标准库os模块下的三个系统调用stat,fstat,lstat获取文件状态。
方法二:标准库中os.path下一些函数,使用起来更加简洁

问题六:如何使用临时文件?

问题内容:
某项目,我们从传感器采集数据,每收集1G数据后,做数据分析,最终只保存分析结果。这样很大的临时数据如果常驻内存,将消耗大量内存资源,我们可以使用临时文件存储这些临时数据(外部存储)。
临时文件不用命名,且关闭后会自动被删除。

解决方案:
可以使用标准库中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

默认是以二进制打开文件的

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()创建的文件,不能在文件系统中查找到,只能通过文件对象 f 访问。

使用NamedTemporaryFile()创建的临时文件则可以在文件系统中查找得到。

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 "", 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'

我们发现两次的name属性值不同,是因为每次关闭之后,前面的文件会被删除掉

我们可以指定Signature: NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True)
函数参数里面的 delete= 为False。则关闭后之前的文件不会被删除。

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

我们可以看到之前创建的tmpgfps1wco并没有在文件关闭之后删除。
使用NamedTemporaryFile()创建的文件可以被多个进程访问,而被TemporaryFile()创建的文件只能被当前进程访问。

你可能感兴趣的:(python进阶:第四章(文件I/O高效处理))