Py学习  »  Python

Python进阶之文件I/O高效处理技巧

编程派 • 6 年前 • 944 次点击  

作者:ansheng

来源:https://blog.ansheng.me/article/advanced-python-intensive-training-of-file-i-o-efficient-handling-skills

如何读写文本文件?

实际案例

某文本文件编码格式已直(如 UTF-8,GBK,BIG5),在 python2.x 和 python3.x 中分别如何读取这些文件?

解决方案

字符串的语义发生了变化:

python2python3
strbytes
unicodestr

python2.x 写入文件前对 unicode 编码,读入文件后对二进制字符串解码

  1. >>> f = open('py2.txt', 'w')

  2. >>> s = u'你好'

  3. >>> f.write(s.encode('gbk'))

  4. >>> f.close()

  5. >>> f = open('py2.txt', 'r')

  6. >>> t = f.read()

  7. >>> print t.decode('gbk')

  8. 你好

python3.x 中 open 函数指定 t 的文本模式, encoding 指定编码格式

  1. >>> f = open('py3.txt', 'wt', encoding='utf-8')

  2. >>> f.write('你好')

  3. 2

  4. >>> f.close()

  5. >>> f = open ('py3.txt', 'rt', encoding='utf-8')

  6. >>> s = f.read()

  7. >>> s

  8. '你好'

如何设置文件的缓冲

实际案例

将文件内容写入到硬盘设备时,使用系统调用,这类 I/O 操作的时间很长,为了减少 I/O 操作的次数,文件通常使用缓冲区(有足够多的数据才进行系统调用),文件的缓存行为,分为全缓冲、行缓存、无缓冲。

如何设置 Python 中文件对象的缓冲行文?

解决方案

全缓冲: open 函数的 buffering 设置为大于 1 的整数 n,n 为缓冲区大小

  1. >>> f = open('demo2.txt', 'w', buffering=2048)

  2. >>> f.write(' ' * 1024)

  3. >>> f.write(' ' * 1023)

  4. # 大于 2048 的时候就写入文件

  5. >>> f.write('-' * 2)

  6. >>> f.close()

行缓冲: open 函数的 buffering 设置为 1

  1. >>> f = open('demo3.txt', 'w', buffering=1)

  2. >>> f.write('abcd')

  3. >>> f.write('1234')

  4. # 只要加上 n 就写入文件中

  5. >>> f.write('n')

  6. >>> f.close()

无缓冲: open 函数的 buffering 设置为 0

  1. >>> f = open( 'demo4.txt', 'w', buffering=0)

  2. >>> f.write('a')

  3. >>> f.write('b')

  4. >>> f.close()

如何将文件映射到内存?

实际案例

  1. 在访问某些二进制文件时,希望能把文件映射到内存中,可以实现随机访问.(framebuffer 设备文件)

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

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

解决方案

使用标准库中的 mmap 模块的 mmap() 函数,它需要一个打开的文件描述符作为参数

创建如下文件

  1. [root@iZ28i253je0Z ~]# dd if=/dev/zero of=demo.bin bs=1024 count=1024

  2. 1024 0 records in

  3. 1024 0 records out

  4. 1048576 bytes (1.0 MB) copied, 0.00380084 s, 276 MB/s

  5. # 以十六进制格式查看文件内容

  6. [root@iZ28i253je0Z ~]# od -x demo.bin

  7. 0000000 0000 0000 0000 0000 0000 0000 0000 0000

  8. *

  9. 4000000

  10. >>> import mmap

  11. >>> import os

  12. >>> f = open('demo.bin','r b')

  13. # 获取文件描述符

  14. >>> f.fileno()

  15. 3

  16. >>> m = mmap.mmap(f.fileno(),0,access=mmap.ACCESS_WRITE)

  17. >>> type(m)

  18. <type 'mmap.mmap'>

  19. # 可以通过索引获取内容

  20. >>> m[0]

  21. 'x00'

  22. >>> m[10:20]

  23. 'x00x00x00x00x00x00x00x00x00x00'

  24. # 修改内容

  25. >>> m[0] = 'x88'

查看

  1. [root@iZ28i253je0Z ~]# od -x demo.bin

  2. 0000000 0088 0000 0000 0000 0000 0000 0000 0000

  3. 0000020 0000 0000 0000 0000 0000 0000 0000 0000

  4. *

  5. 4000000

修改切片

  1. >>> m[4:8] = 'xff' * 4

查看

  1. [root@iZ28i253je0Z ~]# od -x demo.bin

  2. 0000000 0088 0000 ffff ffff 0000 0000 0000 0000

  3. 0000020 0000 0000 0000 0000 0000 0000 0000 0000

  4. *

  5. 4000000

  6. >>> m = mmap.mmap(f.fileno(),mmap.PAGESIZE * 8,access=mmap.ACCESS_WRITE,offset=mmap.PAGESIZE * 4)  

  7. >>> m[:0x1000] = 'xaa' * 0x1000

查看

  1. [root@iZ28i253je0Z ~]# od -x demo.bin

  2. 0000000 0088 0000 ffff ffff 0000 0000 0000 0000

  3. 0000020 0000 0000 0000 0000 0000 0000 0000 0000

  4. *

  5. 0040000 aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa

  6. *

  7. 0050000 0000 0000 0000 0000 0000 0000 0000 0000

  8. *

  9. 4000000

如何访问文件的状态?

实际案例

在某些项目中,我们需要获得文件状态,例如:

  1. 文件的类型(普通文件、目录、符号链接、设备文件…)

  2. 文件的访问权限

  3. 文件的最后的访问/修改/节点状态更改时间

  4. 普通文件的大小 …..

解决方案

当前目录有如下文件

  1. [root@iZ28i253je0Z 2016-09-16]# ll

  2. total 4

  3. drwxr-xr-x 2 root root 4096 Sep 16 11:35 dirs

  4. -rw-r--r-- 1 root root    0 Sep 16 11:35 files

  5. lrwxrwxrwx 1 root root   37 Sep 16 11:36 lockfile -> /tmp/qtsingleapp-aegisG-46d2-lockfile

系统调用

标准库中的 os 模块下的三个系统调用 stat、 fstat、 lstat 获取文件状态

  1. >>> import os

  2. >>> s = os.stat('files')

  3. >>> s

  4. 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=1473996947, st_mtime=1473996947, st_ctime=1473996947)

  5. >>> s.st_mode

  6. 33188

  7. >>> import stat

  8. # stat 有很多 S_IS..方法来判断文件的类型

  9. >>> stat.S_ISDIR(s.st_mode)

  10. False

  11. # 普通文件

  12. >>> stat.S_ISREG(s.st_mode)

  13. True

获取文件的访问权限,只要大于 0 就为真

  1. >>> s.st_mode & stat.S_IRUSR

  2. 256

  3. >>> s.st_mode & stat.S_IXGRP

  4. 0

  5. >>> s.st_mode & stat.S_IXOTH

  6. 0

获取文件的修改时间

  1. # 访问时间

  2. >>> s.st_atime

  3. 1473996947.3384445

  4. # 修改时间

  5. >>> s.st_mtime

  6. 1473996947.3384445

  7. # 状态更新时间

  8. >>> s.st_ctime

  9. 1473996947.3384445

将获取到的时间戳进行转换

  1. >>> import time

  2. >>> time.localtime(s.st_atime)

  3. 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)

获取普通文件的大小

  1. >>> s.st_size

  2. 0

快捷函数

标准库中 os.path 下的一些函数,使用起来更加简洁

文件类型判断

  1. >>> os.path.isdir('dirs')

  2. True

  3. >>> os.path.islink('lockfile')

  4. True

  5. >>> os.path.isfile('files')  

  6. True

文件三个时间

  1. >>> os.path.getatime('files')

  2. 1473996947.3384445

  3. >>> os.path.getmtime('files')

  4. 1473996947.3384445

  5. >>> os.path.getctime('files')

  6. 1473996947.3384445

获取文件大小

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

  2. 0

如何使用临时文件?

实际案例

某项目中,我们从传感器采集数据,每收集到 1G 数据后,做数据分析,最终只保存分析结果,这样很大的临时数据如果常驻内存,将消耗大量内存资源,我们可以使用临时文件存储这些临时数据(外部存储)

临时文件不用命名,且关闭后会自动被删除

解决方案

使用标准库中的 tempfile 下的 TemporaryFile, NamedTemporaryFile

  1. >>> from tempfile import TemporaryFile, NamedTemporaryFile

  2. # 访问的时候只能通过对象 f 来进行访问

  3. >>> f = TemporaryFile()

  4. >>> f.write('abcdef' * 100000)

  5. # 访问临时数据

  6. >>> f.seek(0)

  7. >>> f.read(100)

  8. 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd'

  9. >>> ntf = NamedTemporaryFile()

  10. # 如果要让每次创建 NamedTemporaryFile() 对象时不删除文件,可以设置 NamedTemporaryFile(delete=False)

  11. >>> ntf.name

  12. # 返回当前临时文件在文件系统中的路径

  13. '/tmp/tmppNvBu2'


题图:pexels,CC0 授权。

点击阅读原文,查看更多 Python 教程和资源。


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/QBzenKezl0
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/3010
 
944 次点击