python3学习之路(10)文件操作

对文件的操作无非两种:读文件与写文件。且不管对文件进行读操作还是写操作前,都必须使用open函数打开文件,文件操作完成后,用close()函数关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,而操作系统同一时间能打开的文件数量有限。如读取一个文件内容:

#!/usr/bin/env python
a=open('/etc/passwd')                               #打开文件
b=a.read()                                          #将文件内容全部读入内存
print(b)
root:x:0:0:root:/root:/bin/bash
... ...
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash

a.close()                                           #最后关闭文件

open函数处理文件编码的问题

open函数默认处理UTF-8编码文件,要读取非UTF-8编码文件,需要给open函数传入encoding参数,如读取GBK编码的文件:

>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
>>> f.read()
'测试'
>>> print(f.encoding)       #查询文件的编码
gbk

遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函数还接收一个errors参数,表示若遇到编码错误后如何处理。最简单的方式是直接忽略:

>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')


## open函数之文件的打开模式

|模式|描述|

  • | :- | :-
    |r|以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。|
    |rb|以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。|
    |r+|打开一个文件用于读写。文件指针将会放在文件的开头。|
    |rb+|以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。|
    |w|打开一个文件只用于写入。若该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。若该文件不存在,创建新文件。|
    |wb|以二进制格式打开一个文件只用于写入。若该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。若该文件不存在,创建新文件。|
    |w+|打开一个文件用于读写。若该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。若该文件不存在,创建新文件。|
    |wb+|以二进制格式打开一个文件用于读写。若该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。若该文件不存在,创建新文件。|
    |a|打开一个文件用于追加。若该文件已存在,文件指针将会放在文件的结尾。即,新的内容将会被写入到已有内容之后。若该文件不存在,创建新文件进行写入。|
    |ab|以二进制格式打开一个文件用于追加。若该文件已存在,文件指针将会放在文件的结尾。即,新的内容将会被写入到已有内容之后。若该文件不存在,创建新文件进行写入。|
    |a+|打开一个文件用于读写。若该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。若该文件不存在,创建新文件用于读写。|
    |ab+|以二进制格式打开一个文件用于追加。若该文件已存在,文件指针将会放在文件的结尾。若该文件不存在,创建新文件用于读写。|
    |t|windows特有的所谓text mode(文本模式),区别在于会自动识别windows平台的换行符。|
    |rt|python在读取文本时会自动把\r\n转换成\n|
    |wt|Python写文件时会用\r\n来表示换行|
#不指定文件打开模式,默认以只读方式打开文件(r)。
>>> f=open('/etc/passwd')
>>> f.readable()            #检查文件是否可读,可读返回True,否则返回False
True
>>> f.writable()            #检查文件是否可写,可写返回True,否则返回False
False

#以二进制格式打开文件
>>> f=open('/etc/passwd','rb')
>>> print(f.read())
b'root:x:0:0:root:/root:/bin/bash\nbin:x:1:1:bin:/bin:/sbin/nologin\ndaemon:x:2:2:daemon:/sbin:/sbin/nologin\nsync:x:5:0:sync:/sbin:/bin/sync\nshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\nmail:x:8:12:mail:/var/spool/mail:/sbin/nologin\nftp:x:14:50:FTP User:/var/ftp:/sbin/nologin\nnobody:x:99:99:Nobody:/:/sbin/nologin\nvcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin\nsaslauth:x:499:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin\npostfix:x:89:89::/var/spool/postfix:/sbin/nologin\nsshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin\nopenvpn:x:498:498:OpenVPN:/etc/openvpn:/sbin/nologin\nnginx:x:497:497:nginx user:/var/cache/nginx:/sbin/nologin\napache:x:48:48:Apache:/var/www:/sbin/nologin\nmysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash\n'


## Python文件操作函数 file对象使用open函数来创建,下表列出了file对象常用的函数:

|函数|描述|

  • | :- | :-
    |file.close()|关闭文件。关闭后文件不能再进行读写操作。|
    |file.flush()|刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。|
    |file.fileno()|返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。|
    |file.isatty()|若文件连接到一个终端设备返回 True,否则返回 False。|
    |file.next()|返回文件下一行。|
    |file.read([size])|从文件读取指定的字节数,若未给定或为负则读取所有。|
    |file.readline([size])|读取整行,包括 "\n" 字符。若指定了一个非负数的参数,则返回指定大小的字节数,包括 "\n" 字符|
    |file.readlines([sizeint])|读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。|
    |file.seek(offset[, whence])|设置文件当前位置。即文件指针或光标所在位置|
    |file.tell()|返回文件当前位置。即文件指针或光标所在位置|
    |file.truncate([size])|从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 Widnows 系统下的换行代表2个字符大小。截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果|
    |file.write(str)|将字符串写入文件,返回的是写入的字符长度。|
    |file.writelines(sequence)|向文件写入一个序列字符串列表,若需要换行则要自己加入每行的换行符。|
    |file.writable()|检查文件是否可写,可写返回True,否则返回False|
    |file.readable()|检查文件是否可读,可读返回True,否则返回False|
    |file.closed|检查文件是否已经关闭,是则返回True,否则返回False|
    |file.encoding|显示文件内容编码|

## open函数读模式,read、readline、readlines read---读取整个文件 readline---读取下一行 readlines---读取整个文件到一个迭代器以供我们遍历(读取到一个list中,以供使用,比较方便)、
#文件“file”中有内容
[root@erbiao ~]# cat file
line 1
line 2
line 3
line 4
line 5


read函数

用于从文件读取指定字节数,若未给定或为负则读取所有

#未指定字节数,默认显示文件所有内容

#!/usr/bin/env python
f=open('file','r')
print(f.read())
f.close()
#执行结果:
line 1
line 2
line 3
line 4
line 5


#读取指定字节数

#!/usr/bin/env python
f=open('file','r')
print(f.read(9))
f.close()
#执行结果:
line 1        #可见字符7个,空格1个,换行符1个,总共9个字符,共9个字节
li


readline函数

用于从文件读取整行,包括"\n"字符。若指定了一个非负数的参数,则返回指定大小的字节数,包括"\n"字符

#从file文件每次读取整一行,当已读取完文件内容仍读取时会返回空字符串
f=open('file','r')
print('第1行:',f.readline())
print('第2行:',f.readline())
print('第3行:',f.readline())
print('第4行:',f.readline())
print('第5行:',f.readline())
print('第6行:',f.readline())
print('第7行:',f.readline())
f.close()

#执行结果:
第1行: line 1

第2行: line 2

第3行: line 3

第4行: line 4

第5行: line 5

第6行: 
第7行:

#可以看到每次读取整一行后都会有一个换行,可以指定end参数去除换行符
f=open('file','r')
print('第1行:',f.readline(),end='')
print('第2行:',f.readline(),end='')
print('第3行:',f.readline(),end='')
print('第4行:',f.readline(),end='')
print('第5行:',f.readline(),end='')
print('第6行:',f.readline(),end='')
print('第7行:',f.readline(),end='')
f.close()

#执行结果:
第1行: line 1
第2行: line 2
第3行: line 3
第4行: line 4
第5行: line 5
第6行: 第7行:               #换行符被删除



#按照字节依次读取文件内容,当字节数少于当前读取行的总字节数时,拆分字符显示;当字节数多于当前读取行的总字节数,仅显示当前读取行,
>>> f=open('file','r')
>>> print(f.readline(3))        #字节数少于当前读取行
lin
>>> print(f.readline(3))
e 1
>>> print(f.readline(3))


>>> print(f.readline(3))
lin
>>> print(f.readline(3))
e 2
>>> print(f.readline(3))


>>> f=open('file','r')
>>> print(f.readline(100))      #字节数多于当前读取行
line 1

>>> print(f.readline(100))
line 2

>>> print(f.readline(100))
line 3

>>> print(f.readline(100))
line 4


readlines函数

用于读取所有行(直到结束符EOF)并返回列表,该列表可由Python的for...in...结构进行处理。若碰到结束符EOF则返回空字符串

>>> f=open('file','r')
>>> la=f.readlines()
>>> type(la)
<class 'list'>
>>> print(la)
['line 1\n', 'line 2\n', 'line 3\n', 'line 4\n', 'line 5\n', 'line 6\n', 'line 7\n', 'line 8\n', 'line 9\n']



## open函数写模式,write、writelines 文件的写模式和读模式一样,唯一区别是调用open函数时,传入的是'w'或'wb'。 使用写模式时,若open函数指定的文件不存在,则创建一个,若存在则先把原文件内容清空再写入新东西。因此若不想清空文件内容而是追加内容,请使用追加模式'a'和'ab'

在文件关闭前或缓冲区刷新前,字符串内容存储在缓冲区中,这时你在文件中是看不到写入的内容的。
若文件打开模式带 b,那写入文件内容时,str(参数)要用encode方法转为bytes形式

write函数

f=open('write.txt','w',encoding='utf8')     #使用写模式打开文件
f.write('This is a new world!')             #写入内容
f=open('write.txt','r')                     
print (f.read())                            #此时读取的内容是缓冲区中的内容,并非直接来源于文件
f.close()                                   #明确关闭文件后,缓冲区数据会刷新到文件


writelines函数

writelines()方法和readlines()方法对应,也是针对列表的操作。它只能接收一个字符串列表作为参数,将他们写入到文件中,换行符不会自动的加入,因此,需要显式的加入换行符

f=open('write.txt','w',encoding='utf8')
f.writelines(['this','is','new','world'])                   #写入一个字符串列表
f.writelines(['\nthis\n','is\n','a\n','new\n','world\n'])       #添加了特殊字符格式化字符串
f=open('write.txt','r')
print (f.read())
f.close()

#执行结果:
thisisnewworldthis
is
a
new
world


#仅支持字符串写入
f=open('write.txt','w',encoding='utf8')
f.writelines([1,2,3,4,5])                   #写入一个数字类型
f=open('write.txt','r')
print (f.read())
f.close()

#执行结果:
Traceback (most recent call last):
  File "D:/PyProject/test/test.py", line 2, in <module>
    f.writelines([1,2,3,4,5])                   #写入一个数字类型
TypeError: write() argument must be str, not int



## open函数追加模式 打开一个文件用于追加。若该文件已存在,文件指针将会放在文件的结尾。即新的内容将会被写入到已有内容之后。若该文件不存在,创建新文件进行写入。 ``` f=open('file.txt','a',encoding='utf-8') #a模式 f.write('\nnew line') f=open('file.txt','r',encoding='utf-8') print (f.read()) f.close()

## 文件读写模式

!/usr/bin/env python

f=open('write.txt','r+') #以读写模式打开文件
print(f.read())
f.write('\n no,this is a new line!!!') #写入新数据
print(f.read())
f.close()

执行结果:由于文件内容还未刷新到磁盘,因此第一个read函数读到未修改的完整内容,第二个read永远读不到文件内容,除非强制刷新文件内容到文件

This is a new world,
,yes it is.

(此处有空行)

## 文件二进制处理模式
为什么要用二进制的读写?
因为图片视频不是字符串方式能显示的,所以只能用b的方式来。另外二进制数据可以跨平台

可以有:rb,wb,ab,rb+,wb+,ab+使用方法。
#### 读二进制文件数据,rb

首先“file”中有内容“我是1个粉刷匠,粉刷本领强”

f=open('file','rb') #在使用二进制处理模式时,不能指定编码,因为二进制编码方式是原生的
print(f.read())
f.close()

执行结果:

b'\xe6\x88\x91\xe6\x98\xaf1\xe4\xb8\xaa\xe7\xb2\x89\xe5\x88\xb7\xe5\x8c\xa0\xef\xbc\x8c\xe7\xb2\x89\xe5\x88\xb7\xe6\x9c\xac\xe9\xa2\x86\xe5\xbc\xba'

上述为二进制数据,对其进行解码,就可看到文件原内容

f=open('file','rb')
print(f.read().decode('utf-8'))
f.close()

执行结果:

我是1个粉刷匠,粉刷本领强

#### 写二进制数据,wb

当使用二进制的写入模式时,写入的数据也必须是二进制的,否则会报错

f=open('test','wb')
f.write('二表')
f.close()

执行结果

Traceback (most recent call last):
File "D:/PyProject/test/test.py", line 2, in
f.write('二表')
TypeError: a bytes-like object is required, not 'str' #提示要求写入的数据是二进制数据

写入二进制数据的正确方式

f=open('test','wb')
f.write(bytes('二表',encoding='utf-8')) #首先指定字字符串的编码(encoding='utf-8'),然后使用bytes转换为二进制数据
f.close()

f=open('test','wb')
f.write('二表'.encode()) #首先指定字字符串的编码(encoding='utf-8'),然后使用bytes转换为二进制数据
f.close()

<br />
## 文件内光标的移动
read(3):

  1. 文件打开方式为文本模式时,代表读取3个字符

  2. 文件打开方式为b模式时,代表读取3个字节

其余的文件内光标移动都是以字节为单位如seek,tell,truncate

注意:

  1. seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的,且在b模式下,换行符为\r\n两个字符
    seek()方法用于移动文件读取指针(或光标)到指定位置。
    语法:fileObject.seek(offset[, from_what])
          offset:开始的偏移量,也就是代表需要移动偏移的字节数
          from_what:若是0表示开头, 若是1表示当前位置,2表示文件的结尾,例如:
                seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
                seek(x,1) : 表示从当前位置往后移动x个字符
                seek(-x,2):表示从文件的结尾往前移动x个字符
    >>> f = open('/tmp/foo.txt', 'rb+')
    >>> f.write(b'0123456789abcdef')
    16
    >>> f.seek(5)     # 移动到文件的第六个字节
    5
    >>> f.read(1)
    b'5'
    >>> f.seek(-3, 2) # 移动到文件的倒数第三字节
    13
    >>> f.read(1)
    b'd'     

练习:基于seek实现tail -f功能

import time
with open('test.txt','rb') as f:
f.seek(0,2)
while True:
line=f.readline()
if line:
print(line.decode('utf-8'))
else:
time.sleep(0.2)

  2. truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果
    f=open('test','a+',encoding='utf-8')
    f.truncate(11)

<br />
## with语句
有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄
若不用with语句:

file = open("/tmp/foo.txt")
data = file.read()
file.close()

这里有两个问题。一是可能忘记关闭文件句柄;二是文件读取数据发生异常,没有进行任何处理。下面是处理异常的加强版本:

file = open("/tmp/foo.txt")
try:
data = file.read()
finally:
file.close()

虽然这段代码运行良好,但是太冗长了。这时候就是with一展身手的时候了。除了有更优雅的语法,with还可以很好的处理上下文环境产生的异常。下面是with版本的代码:

with open("/tmp/foo.txt") as file:
data = file.read()

with语句也可同时操作多个文件:

with open('r1','w',encoding='gbk') as f1,\ #可用\加上一个回车键换行
open('w2','w') as f2:
f1.write('this is a write operation for f1')
f2.write('this is a write operation for f2')

with open('r1','r',encoding='gbk') as f1,open('w2','r') as f2:
print(f1.read())
print(f2.read())

但切记,若使用with语句多次操作一个文件,第一次次打开的文件模式是该文件的最终打开模式:

with open('r1','w',encoding='gbk') as f1,
open('w2','w') as f2:
f1.write('this is a write operation for f1')
f2.write('this is a write operation for f2')

with open('r1','w',encoding='gbk') as f1,
open('r1','r') as f2:
print(f2.writable())
f1.write('new line')
print(f1.readable())
print(f1.read())

执行结果:

False
False
Traceback (most recent call last):
File "D:/PyProject/test/test.py", line 11, in
print(f1.read())
io.UnsupportedOperation: not readable

文件内容

new line

posted @ 2018-08-22 16:39  二表  阅读(122)  评论(0)    收藏  举报