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,来看截图吧:

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

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

问题内容:
将文件内容写入到硬件设备时,使用系统调用,这类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)
<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中文本文件不能设置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 "<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'

我们发现两次的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()创建的文件只能被当前进程访问。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • 定义类并创建实例 在Python中,类通过 class 关键字定义。以 Person 为例,定义一个Person类...
    绩重KF阅读 3,924评论 0 13
  • 4.1 字符串的语义 在python2 和 python3中,对字符串的语义是有着很大的区别的。 python2s...
    Lemon_Home阅读 543评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,600评论 18 139
  • 我有一辆单车 后车座空空荡荡 后车轮吱吱嘎嘎 不停转动着寂寞 当我可以接你放学 便安了一个后车凳 车凳全是爱做的海...
    伦小让阅读 106评论 4 1
  • 看着自己在屏幕上敲下的文字,删了又写,写了又删。仿佛回到课堂上的无命题作文,给你足够的自由尽情发挥,却反而不知如何...
    文大人频道阅读 211评论 0 0