20.python的文件处理

  我们日常在处理文件的时候一般都遵循这样的逻辑:打开文件,操作文件,保存关闭文件。

  但在python中,又分为以下几步:创建文件对象,对文件对象进行操作(读入,写入之类的),关闭文件。

  由于文件操作在python2.x和python3.x中区别还是比较大的,3.x可以接受更多的参数。

  所以在此说明:以下内容都是针对python2.x而言的,准确来说是python2.7。

  下面来逐一分析:

1.创建文件对象

  创建文件对象的方法有两种,第一张是使用工厂函数 file(name[, mode[, buffering]]) -> file object ,另一种是调用内置函数 open(name[, mode[, buffering]]) -> file object

  两者其实并不存在本质上的区别,实际上 open 也是调用 file 实现的,但是python官方建议我们使用 open 进行文件对象的创建,所以我们也顺应官方的建议,下面的演示都是基于 open 来进行。

  首先,当我们使用 open 创新一个文件对象时需要一些参数,其中 name 是必须的,它接受一个字符串,表示文件名,文件名可以采用绝对路径,也可以采用相对路径,而 mode 表示模式,它接受一个字符串表示要用什么模式来创建这个文件对象,而模式的不同也会对文件造成影响,下面是一个关于模式的总结表:

文件模式 操作
r 以只读方式打开,默认
rU 或 Ua 以读方式打开, 同时提供通用换行符支持 (PEP 278)
w 以写方式打开 (必要时清空)
a 以追加模式打开 (从 EOF 开始, 必要时创建新文件)
r+ 以读写模式打开
w+ 以读写模式打开 (参见 w )
a+ 以读写模式打开 (参见 a )
rb 以二进制读模式打开
wb 以二进制写模式打开 (参见 w )
ab 以二进制追加模式打开 (参见 a )
rb+ 以二进制读写模式打开 (参见 r+ )
wb+ 以二进制读写模式打开 (参见 w+ )
ab+ 以二进制读写模式打开 (参见 a+ )

  下面是关于这些模式的进一步解释:

  r : 顾名思义,同该模式创建的文件对象中的进行读取,而不能进行写入等操作,当要打开的文件不存在时抛出 IOError 异常。

  w:以只写的方式打开,该模式下,只能进行写入操作,而不能进行读取操作,如果打开的文件存在,则清空原文件再打开,若打开的文件不存在则创建该文件后再打开。这种模式相对危险,因为无论文件存不存存在,最后操作的都是一个空文件,此时写入会从头开始写,也就是文件指针在文件的开头处。

  a : 以追加的模式打开,该模式下不能进行文件的读取,但能进行写入操作。如果打开的文件存在,则打开此文件,将文件的指针移到文件的末尾,此时任何新写入的内容都会在文件的最末尾处。如果文件不存在,则新建一个文件,从头开始写。和 w 不同的是当打开的文件存在时不会清空原文件,只是进行追加写入。

  r+ :r 本身不能进行写入操作,而扩展成 r+ 之后,就能够进行写入了。r+ 打开的文件并不会清空文件,写入时其指针会在最开头,也就是我文件中的内容是 123456,我用 r+ 写入了 'abc' 字符串时,文件内容就变成了 abc456 了。同样的,文件不存在的时候还是会抛出异常。

  w+,a+:因为 w 和 a 都不具有读权限,所以在进行 + 号拓展后,都能进行读操作了,其他的行为了原来的一样。

  U :通用换行符支持,不同平台用来表示行结束的符号是不同的, 例如 , , 或者 。但是如果只写了一种处理换行符的方法,在其他平台就无法同样了,如果要为每一个平台都写一个方法就太麻烦了。所以python在 Python 2.3 引入了 UNS。当你使用 'U' 标志打开文件的时候, 所有的行分割符(或行结束符, 无论它原来是什么)通过 Python 的输入方法(例如:read() )返回时都会被替换为换行符 NEWLINE( )。('rU' 模式也支持 'rb' 选项) 。 这个特性还支持包含不同类型行结束符的文件,文件对象的 newlines 属性会记录它曾“看到的”文件的行结束符。

  b:二进制模式,在 linux 中默认都是用二进制打开的,所以这个选项对 linux 而言可有可无,但如果真的需要使用二进制模式,还是建议写上,增加跨平台能力。使用此模式进行写入操作时,不仅可以写入字符串,还可以写入 buffer 对象。

  

  buffering 表示缓用于指示访问文件所采用的缓冲方式。 其中 0 表示不缓冲, 1 表示只缓冲一行数据, 任何其它大于 1 的值代表使用给定值作为缓冲区大小。不提供该参数或者给定负值代表使用系统默认缓冲机制,既对任何类电报机( tty )设备使用行缓冲, 其它设备使用正常缓冲。一般情况下使用系统默认方式即可。

  

  最后要总结一点,创建了文件对象,并不意味着读取了文件内容,这与我们日常生活中的打开文件的定义是不同的。Python的打开文件是获取了文件的句柄,也就是文件的操作入口,而读取文件内容还需要将其读入到python的内存中,也就是所谓的输入。


2.输入

  所谓的输入,就是将文件的内容读入的python中,有下面几种方法。

1. read([size]) -> read at most size bytes, returned as a string. 

  如果 size(单位为字节) 是负数或者没有给,就一直读到EOF,也就是文件结束。返回一个保括所有内容的字符串(包括换行符)。注意,当在非阻塞模式下,数据就算低于要求也可能会返回,即使没有尺寸参数。

  这个方法是一次性读入,也就是加载1gb的文件就会直接占用1gb的内存,所以不适合读取大的文件。

f = open('test.txt')
a = f.read()
print a
print repr(a)

  文件内容:

  代码输出:

  注意这里的换行符。文件中使用了3个回车,也就有3个换行符。

  我们在python的编码中使用过repr()函数来获取一个字符串的编码,这里我们使用这个函数,看看打开的文件都是什么编码。

  首先,我的编码声明为: # coding= utf-8 

  

  编码声明为: # coding= gbk 

   

  发现无论编码声明是什么,返回的字符串都是一样的。说明编码声明并不影响从文件读取到的字符串的编码。

  那么,我们用 utf-8 编码写一个文件:

#! /usr/bin/env python
# coding= utf-8

f = open('test1.txt','w')
f.write('第一行
第二行
第三行
')
f.close()

    再进行一下读取:

  和上面的一样。

  再用gbk写一个文件:

#! /usr/bin/env python
# coding= gbk

f = open('test2.txt','w')
f.write('第一行
第二行
第三行
')
f.close()

  再读取一下:

  结果不同了。

  说明一个问题:在文件对象中读取获得的字符串,其编码和python的编码声明无关,只与文件本身保存时使用的编码有关。

  python的编码声明只影响在python创建的字符串,所以我根据这个特性,在python创建相应编码的字符串,再保存到文件中,所有文件内字符的编码才会不一样,希望大家不要绕晕了。

  讲到这里,就可以看python中是如何进行文件的编码转换的了。

  编码的转换其实是针对于字符串的,所有用到的也是字符串中的内置方法:

 S.decode([encoding[,errors]]) -> object 

  该方法返回解码后的字符串。

  encoding -- 要使用的编码,如"UTF-8"(默认)。errors -- 设置不同错误的处理方案。默认为 'strict',意为编码错误引起一个UnicodeError。 其他可能得值有 'ignore', 'replace', 'xmlcharrefreplace', 'backslashreplace' 以及通过 codecs.register_error() 注册的任何值。

  代码示例:

f = open('test2.txt','r')
a = f.read()
b = a.decode(encoding='gbk')
print b
print repr(b)

  可以看出,进行解码以后得到的是Unicode字符串。

  接下来我们看看如何编码。

 S.encode([encoding[,errors]]) -> object 

  该方法返回编码后的字符串。

  encoding -- 要使用的编码,如"UTF-8"(默认)。errors -- 设置不同错误的处理方案。默认为 'strict',意为编码错误引起一个UnicodeError。 其他可能得值有 'ignore', 'replace', 'xmlcharrefreplace', 'backslashreplace' 以及通过 codecs.register_error() 注册的任何值。

  代码示例:

f = open('test2.txt','r')
a = f.read()
b = a.decode(encoding='gbk')
c = b.encode(encoding='utf-8')
print c
print repr(c)

  得到的就是 utf-8 编码的字符串了,也就是完成了gbk-->Unicode-->utf-8的转换了。此时可以看看我在python的编码中的内容,就明白了Unicode作为桥梁的含义了。

f = open('test2.txt','r')
a = f.read()
f.close()
b = a.decode(encoding='gbk')
c = b.encode(encoding='utf-8')
f = open('test2.txt','w')
f.write(c)
f.close()

  使用以上的代码就可以完成文件的编码转换了,当然还有一定的优化空间,这里仅作为逻辑演示。

  接下来继续进行输入方法的说明。

2. readline([size]) -> next line from the file, as a string. 

  一次读取一行,保留换行符,返回一个字符串对象。size(默认为 -1, 代表读至行结束符)为非负数时,用来限制最大读取的字节数,当字节数设置少于真正一行的字节时,会返回不完整的一行,超过时不影响。当为EOF也就是文件结束时,返回空字符串。配合for循环使用可以进行逐行遍历。由于返回的也是字符串,所以也可以进行对返回对象使用字符串的方法,例如编解码等。

f = open('test2.txt', 'r')
for x in range(2):
    print f.readline()

  由于print关键字会默认添加换行符,而文件中已经又换行符了,所以会出现两个换行符,也就出现了输出这样的隔行显示。可以加入一个逗号取消print这种默认行为。

f = open('test2.txt', 'r')
for x in range(2):
    print f.readline(),

  另外,还有注意一个问题,不用想下面这里循环:

f = open('test2.txt', 'r')
for x in f.readline():  #相当于读取一行,对每个字节进行遍历
    print x     #此时打印的就是一个字节,utf-8中,显示中文需要3个字节,输出的就是乱码了
    print repr(x)   #看一下是否是字节

  每次读取一行,内存消耗自然就低了点,但是很难知道文件有多少行,所以也不是一个理想的遍历方式。

3.  readlines([size]) -> list of strings, each a line from the file. 

  方法并不像其它两个输入方法一样返回一个字符串, 它会读取所有(剩余的)行,然后把它们作为一个字符串列表返回。 它的可选参数 size 代表返回的大字节大小。如果它大于 0 , 那么返回的所有行应该大约有 size 字节(可能稍微大于这个数字, 因为需要凑齐缓冲区大小。比如说缓冲区的大小只能为 4K 的倍数,如果 size 为 15k,则后返回的可能是 16k)。

  因为返回的是一个包含所有行的列表,所以可以直接对其进行遍历,注意和上面的区别。

f = open('test2.txt', 'r')
for x in f.readlines():
    print x,

  但是,它也是一次性读取了整个文件,所以内存占用也是非常大的。

  此时,有一个更高效的 xreadlines 方法,参数也是一样的,只不过其本质是一个生成器,也就是每次调用都返回一行,迭代的时候是逐行读取,效率更高。

  但是,这并不是最好的方法,最好的方法是直接迭代文件对象:

f = open('test2.txt', 'r')
for x in f:
    print x,

  这是自python2.2中引入的迭代器和文件迭代后的新特性,文件对象成为了它们自己的迭代器,这意味着用户不必调用 read*() 方法就可以在 for 循环中迭代文件的每一行。 另外我们也可以使用迭代器的 next 方法, file.next() 可以用来读取文件的下一行。和其它迭代器一样, Python 也会在所有行迭代完成后引发 StopIteration 异常。但 for 循环会自动调用 next 方法和处理迭代完成后引发的异常,所以直接迭代文件对象成为了最佳的用法。

  另个废弃的方法是 readinto() ,它读取给定数目的字节到一个可写的缓冲器对象,和废弃的 buffer() 内建函数返回的对象是同个类型。(由于 buffer() 已经不再支持,所以 readinto() 被废弃。)


3.输出

1. write(str) -> None. Write string str to file. 

  向文件中写入字符串,这里不再演示了。

2. writelines(sequence_of_strings) -> None. Write the strings to the file. 

  接受一个字符串列表作为参数,将它们写入文件。 行结束符并不会被自动加入,所以如果需要的话,你必须在调用 writelines()前给每行结尾加上行结束符。

  注意这里并没有 "writeline()" 方法, 因为它等价于使用以行结束符结尾的单行字符串调用 write() 方法.


4.文件内指针的移动

  文件内的指针相对与我们平常所见的光标,它表示我们各种操作的位置。例如我们用 r+ 打开文件并写入东西时,光标就在文件的开头,所以出现开头举例的会覆盖原有字符的现象,而 a 模式打开后光标在文件的最后,所以在 a 模式下写入内容会在文件的末尾追加。

1. seek(offset[, whence]) -> None. Move to new file position. 

  在文件中移动光标的位置 从 whence ( 0 代表文件起始; 1 代表当前位置,当前位置由打开模式决定,或者之前有移动过光标; 2 代表文件末尾)偏移 off 字节 ,off为正时向右移动,为负时向左移动。当光标在文件开头时,只能为正;在文件末尾时,只能为负。虽然部分文件支持光标超出末尾,但还是不超过的好。

2. tell() -> current file position, an integer (may be a long integer). 

  返回一个整数(有可能是长整数),表示当前光标所在的位置。

3. truncate([size]) -> None. Truncate the file to at most size bytes. 

  截取文件到大 size 字节, 默认为当前文件位置(size=file.tell())。所谓的截取就是光标前面的保留,后面的全部去掉。


5.关闭保存

1. close() -> None or (perhaps) an integer. Close the file. 

  关闭文件。如果文件不关闭,则对文件的各种操作都保存在缓冲区中,关闭文件才能把缓冲区里的内容写入磁盘中。

2. flush() -> None. Flush the internal I/O buffer. 

  在不关闭文件的前提下,将缓冲区里的内容保存到磁盘中。


6.其他方法

1. isatty() -> true or false. 

   判断 file 是否是一个类 tty 设备

2. x.next() -> the next value, or raise StopIteration 

  返回文件的下一行(类似于 file.readline() ), 在没有其它行时引发 StopIteration 异常。


7.file对象相关属性

  file.closed         True 表示文件已经被关闭, 否则为 False

  file.encoding     文件所使用的编码 - 当 Unicode 字符串被写入数据时, 它们将自动使 用 file.encoding 转换为字节字符串; 若 file.encoding 为 None 时使用系统默认编码

  file.mode          文件打开时使用的访问模式

  file.name          文件名

  file.newlines     未读取到行分隔符时为 None , 只有一种行分隔符时为一个字符串, 当文件有多种类型的行结束符时,则为一个包含所有当前所遇到的行结束符的列表。

  file.softspace   为 0 表示在输出一数据后,要加上一个空格符,1 表示不加。这个属性一般程序员用不着,由程序内部使用。


  关于文件操作就先总结到这,有什么错误或补充的以后会进行修正。

原文地址:https://www.cnblogs.com/scolia/p/5557839.html