第四章 文件和目录工具

文件工具:

通过内建库函数和标准库来对文件进行操纵;

内建函数open是用来在计算机底层系统下访问文件的工具,它是python的固有部分,被调用返回一个新的与文件相连的对象

通过内建模块os 可以操作较底层的基于描述符的文件,复制,移除,移动和手机文件(os和shutil),借助字典的键将数据和对象存储于文件中(dbm和shelve) 以及访问sql数据库(sqllit第三方插件)


python3.x中的文件对象模型

  • 文本文件含有unicode文本(内容始终是一个str字符串----字符构成的序列)

  • 二进制文件包含原有的8位字节码(内容始终是字节字符串----小整数构成的序列)


使用内建文件对象

输出文件:

f=open('file','w')    以写方式打开文本文件

f.write('string\n')    写入文件

f.writelines(['str1\n','str2\n'])    写入行字符列表到文件

f.close()    关闭文件

open('file.txt','w').write('str\n')    创建临时文件对象并写入数据,马上被使用不需要保存对象的引用,数据传输完成后问击案对象马上就被回收并在进程中自动关闭


确保文件关闭:异常处理和上下文管理

万能的处理模式:

myfile=open('file','w')

     try:

        ...process myfile...

    finally:

         myfile.close()

这是使用最广泛的方法,确保发生异常中止时 关闭文件

文件上下文管理器(这个语句依赖与文件上下文):

with open(filename) as myfile:

    subprocess...

这种语句无论是否发生异常都不要手动去关闭文件。

大多数情况下上面的两种语句很少用的到,因为标准cpython在回收时自动关闭文件。


输入文件

file=open('file.txt','r')    打开输入文件对象,默认值为'r'

file.read()     一次性读取整个文件,返回一个字符串(包含文本所有字符(或字节))

file.read(n)     读取接下来的n个字符

file.readline()    读取下一个\n之前的内容并返回一个字符串 

file.readlines()    读取整个文件并返回一个行字符串列表

file.seek()    指定文件内容的偏移位置


 使用文件迭代器读取行

f=open(filename)

for line in f:

   ...do...

更妙的是:

for line in open(data.txt):

   ..something.......

循环中把文件作为临时文件对象打开,在循环结束后的垃圾回收时自动关闭。而且这个文件行迭代器不是一次性把整个文件加载到行列表,因此在处理大文件时较为节省空间。你也可以通过手动方式调用迭代器 它是一个__next__方法(由内建函数next运行)与每次调用readline()方法类似,只是read方法在文件末尾EOF返回一个空字符,而迭代器则抛出一个异常来结束迭代


有趣的是,在所有跌打情境下,迭代器都自动得以使用,这些情景包括list构造调用,列表解析表达式,map调用,以及in成员关系检查

>>> open('txt').readlines()                    #总是读取多行
['hello word\n', 'bye lixing\n']               
>>> list(open('txt'))                        #强制逐行迭代
['hello word\n', 'bye lixing\n']
>>> lines=[line.rstrip() for line in open('txt')]  #解析
>>> lines
['hello word', 'bye lixing']
>>> lines=[line.upper() for line in open('txt')]   #任意操作
>>> lines
['HELLO WORD\n', 'BYE LIXING\n']
>>> list(map(str.split,open('txt')))             #应用函数
[['hello', 'word'], ['bye', 'lixing']]
>>> line='hello word\n'                        #判断文件是否含有该行
>>> line in open('txt')
True


其他打开选项

open开文件用三个参数:

  • 文件名 

                文件名可以包含一个显示的目录路径,没有显示路径,就采用当前工作的目录。在win下打开文件时因为要使用                       r'c:\dr\txt'模式来消除转义 . 当前目录,..父目录

  • 打开模式

             open函数也接受其他模式,r+ w+ a+表示读取写入和追加的模式,b二进制模式 t文本模式   rb以二进制读取模式 wb以二进制写 ab以二进制追加, rb+ wb+ ab+把二进制输入 输出 结合起来

  • 缓存策略

    0表示无缓冲 1表示缓冲


二进制和文本文件

文件与程序之间传输的数据,即使是二进制数据,在脚本中还是表示为python字符串。对于二进制模式文件,文件内容则表示为字节字符串。文件实例:

>>> open('data.txt','w').writelines(['hello word\n','bye file word\n','the life of brian\n'])
>>> open('data.txt','r').read()
'hello word\nbye file word\nthe life of brian\n'
>>> open('data.txt','rb').read()
b'hello word\nbye file word\nthe life of brian\n'
>>> for line in open('data.txt','rb'):     
...  print(line)\
... 
... 
b'hello word\n'
b'bye file word\n'
b'the life of brian\n'
>>>

产生上述结果的原因是python3.x把文本模式文件当做unicode来处理,并自动地在输入时对文件进行解码,在输出时进行编码。

谨记:必须在二进制模式下打开真正的二进制数据,因为二进制数据作为unicode文本时是不会发生解码编码等操作的。二进制模式不仅使unicode编码转换无法进行,还阻止了文本模式文件默认换行符的自动转换。


文本文件的unicode编码

如下包含一个unicode字符的字符串:

>>> data='sp\xe4m'
>>> data
'späm'
>>> 0xe4
228
>>> bin(0xe4)
'0b11100100'
>>> chr(0xe4)
'ä'
>>> data.encode('latin1')
b'sp\xe4m'
>>> data.encode('utf8')
b'sp\xc3\xa4m'
>>> data.encode('acsii')
Traceback (most recent call last):                 
  File "<stdin>", line 1, in <module>
LookupError: unknown encoding: acsii

编码结果反映了其存储于文件中原始二进制形式。不过通常不用手动编码,因为文本文件在数据传输的时候会自动编码。


看下面代码:

>>> open('data.txt','w',encoding='latin1').write(data)
4
>>> open('data.txt','r',encoding='latin1').read()
'späm'
>>> open('data.txt','rb').read()
b'sp\xe4m'

如果我们在二进制模式下打开文件,是不会发生编码转换的,为了了解文件内容和其他编码之间的区别,再次保存这些同样的字符串:

>>> open('data.txt','w',encoding='utf8').write(data)
4
>>> open('data.txt','r',encoding='utf8').read()
'späm'
>>> open('data.txt','rb').read()
b'sp\xc3\xa4m'

这一次虽然原始文件内容有所不同,但文本模式的自动解码使得字符串在脚本读取返回之前相等。其实编码只与文件中的字符串有关,前面说过对于文本文件python3.x都是当成unicode来处理,字符串一旦载入内存就成了简单的unicode字符序列(即‘码点’).转换的步骤正是我们希望对文本而非二进制文件进行的操作。因为二进制模式跳过了转换。

再次提醒:二进制模式打开文件不会 进行任何转换!!!!!!!!!!!!!!!!


用struct模块解析打包的二进制数据

该模块用于打包和解压二进制数据的调用,能够用你想用的任何一种字节序来进行组合和分解。

如下代码:

>>> import struct
>>> data=struct.pack('>i4shf',2,'spam'.encode('utf8'),4,1.234)
>>> data
b'\x00\x00\x00\x02spam\x00\x04?\x9d\xf3\xb6'
>>> open('data.bin','wb').write(data)
14
>>> open('data.bin','rb').read()
b'\x00\x00\x00\x02spam\x00\x04?\x9d\xf3\xb6'
>>> struct.unpack('>i4shf',open('data.bin','rb').read())
(2, b'spam', 4, 1.2339999675750732)

python在这里用十六进制转义序列\xn的形式展示大部分打包后的二进制数据的字节,因为这些字节都是不可打印字符串。解析刚生成的这种数据,从文件中读取并以同样的格式字符串将其传入struct模块--将得到一个元组,它包含从字符串解析出来的数值以及转换为python对象的数值。-----详细了解struct就看python库手册吧


随机访问文件

二进制文件中经常可见随机访问处理操作。文件的open模式字符串添加+号后可读也可写。这种模式通常与文件对象的seek方法联合使用,以便支持随机读取和写入访问。

Python的Seek方法也可以接受可选的第二种参数,这个参数有三种值:

0:表示绝对文件位置(默认值)

1:表示寻找 的是基于当前所在的相对位置

2:表示寻求基于文件结尾的相对位置


在wb+模式下创建一个文件,并写入一些数据;此模式允许我们进行读取和写入,如果文档存在,此模式会初始化文档并将其清空。在写入一些数据之后,我们找到文件的起始位置,读取内容。代码如下:

>>> recods=[bytes([char]*8) for char in b'spam']
>>> recods
[b'ssssssss', b'pppppppp', b'aaaaaaaa', b'mmmmmmmm']
>>> file=open('random.bin','wb+')
>>> for rec in recods:
...   size=file.write(rec)
... 
>>> file.flush()
>>> pos=file.seek(0)
>>> print(file.read())
b'ssssssssppppppppaaaaaaaammmmmmmm'

现在再在rb+模式下重新打开文件(该模式也允许读和写,但初始不清除文档) 代码如下:

 
>>> file=open('random.bin','rb+')
>>> print(file.read())
b'ssssssssppppppppaaaaaaaammmmmmmm'
>>> recod=b'X'*8
>>> file.seek(0)
0
>>> file.write(recod)            #更新第一条记录
8
>>> file.seek(len(recod)*2)      #更新第三条记录
16
>>> file.write(b'Y'*8)
8
>>> file.seek(8)
8
>>> file.read(len(recod))          #抓取第二条记录
b'pppppppp'
>>> file.read(len(recod))          #抓取下一条记录
b'YYYYYYYY'
>>> file.seek(0)
0
>>> file.read()                       #读取整个文件
b'XXXXXXXXppppppppYYYYYYYYmmmmmmmm'
>>> exit()
[root@TrackerServer ~]# cat random.bin            #shell命令行查看文本
XXXXXXXXppppppppYYYYYYYYmmmmmmmm


如果使用寻找功能,一般来说不应该使用文本模式,换行符 unicode都可以进行任意转换,二者都使绝对寻找偏移变量难以使用。如下代码:

>>> data='sp\xe4m'
>>> data,len(data)
('späm', 4)
>>> data.encode('utf8'), len(data.encode('utf8'))
(b'sp\xc3\xa4m', 5)
>>> f=open('test',mode='w+',encoding='utf8')
>>> f.write(data)
4
>>> f.flush()
>>> f.seek(0);f.read(1)
0
's'
>>> f.seek(2);f.read(1)
2
'ä'
>>> data[3]
'm'
>>> f.seek(3);f.read(1)
3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/python3.5/lib/python3.5/codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa4 in position 0: invalid start byte


OS模块的底层文件工具

os模块包含了一个文件处理函数的附加集合:

os.open(path,flags,mode)

    打开文件并返回其描述符‘

os.read(descripter,N)

    最多读取N个字节并返回一个字节字符串

os.write(descripter,string)

    把字节字符串string中的字节写入文件

os.lseek(descripter,position,how)

    在文件中移动至positiong


严格地说os调用通过文件描述符来处理文件,基于描述符的文件以原始字节形式进行处理。除了缓冲等额外性能基于描述的文件一般都能对应上二进制模式文件对象,与文件对象相比,它更底层更复杂。







你可能感兴趣的:(第四章 文件和目录工具)