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

  作者: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 中分别如何读取这些文件?

  解决方案

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

python2 python3
str bytes
unicode str

  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. >>>printt.decode('gbk')

  8. 你好

  python3.xopen函数指定 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=1024count=1024

  2. 10240records in

  3. 10240records out

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

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

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

  7. 000000000000000000000000000000000000000

  8. *

  9. 4000000

  10. >>>importmmap

  11. >>>importos

  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. 000000000880000000000000000000000000000

  3. 000002000000000000000000000000000000000

  4. *

  5. 4000000

  修改切片

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

  查看

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

  2. 000000000880000ffff ffff 0000000000000000

  3. 000002000000000000000000000000000000000

  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. 000000000880000ffff ffff 0000000000000000

  3. 000002000000000000000000000000000000000

  4. *

  5. 0040000aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa

  6. *

  7. 005000000000000000000000000000000000000

  8. *

  9. 4000000

如何访问文件的状态?

  实际案例

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

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

  2. 文件的访问权限

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

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

解决方案

  当前目录有如下文件

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

  2. total 4

  3. drwxr-xr-x 2root root 4096Sep1611:35dirs

  4. -rw-r--r--1root root 0Sep1611:35files

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

  系统调用

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

  1. >>>importos

  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. >>>importstat

  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. >>>importtime

  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. >>>fromtempfile importTemporaryFile,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'

声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
推荐阅读