文件操作
这一篇介绍操作文件、文件指针、读写压缩文件、 序列与反序列化
一、操作文件
python 中使用open函数操作文件,三步走
- 打开文件, 得到文件对象
- 通过文件对象提供的方法读写文件
- 关闭文件对象(句柄)
open用法
Help on built-in function open in module io: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None) Open file and return a stream. Raise IOError upon failure. file is either a text or byte string giving the name (and the path if the file isn't in the current working directory) of the file to be opened or an integer file descriptor of the file to be wrapped. (If a file descriptor is given, it is closed when the returned I/O object is closed, unless closefd is set to False.) mode is an optional string that specifies the mode in which the file is opened. It defaults to 'r' which means open for reading in text mode. Other common values are 'w' for writing (truncating the file if it already exists), 'x' for creating and writing to a new file, and 'a' for appending (which on some Unix systems, means that all writes append to the end of the file regardless of the current seek position). In text mode, if encoding is not specified the encoding used is platform dependent: locale.getpreferredencoding(False) is called to get the current locale encoding. (For reading and writing raw bytes use binary mode and leave encoding unspecified.) The available modes are: ========= =============================================================== Character Meaning --------- --------------------------------------------------------------- 'r' open for reading (default) 'w' open for writing, truncating the file first 'x' create a new file and open it for writing 'a' open for writing, appending to the end of the file if it exists 'b' binary mode 't' text mode (default) '+' open a disk file for updating (reading and writing) 'U' universal newline mode (deprecated) ========= =============================================================== The default mode is 'rt' (open for reading text). For binary random access, the mode 'w+b' opens and truncates the file to 0 bytes, while 'r+b' opens the file without truncation. The 'x' mode implies 'w' and raises an `FileExistsError` if the file already exists. Python distinguishes between files opened in binary and text modes, even when the underlying operating system doesn't. Files opened in binary mode (appending 'b' to the mode argument) return contents as bytes objects without any decoding. In text mode (the default, or when 't' is appended to the mode argument), the contents of the file are returned as strings, the bytes having been first decoded using a platform-dependent encoding or using the specified encoding if given. 'U' mode is deprecated and will raise an exception in future versions of Python. It has no effect in Python 3. Use newline to control universal newlines mode. buffering is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line buffering (only usable in text mode), and an integer > 1 to indicate the size of a fixed-size chunk buffer. When no buffering argument is given, the default buffering policy works as follows: * Binary files are buffered in fixed-size chunks; the size of the buffer is chosen using a heuristic trying to determine the underlying device's "block size" and falling back on `io.DEFAULT_BUFFER_SIZE`. On many systems, the buffer will typically be 4096 or 8192 bytes long. * "Interactive" text files (files for which isatty() returns True) use line buffering. Other text files use the policy described above for binary files. encoding is the name of the encoding used to decode or encode the file. This should only be used in text mode. The default encoding is platform dependent, but any encoding supported by Python can be passed. See the codecs module for the list of supported encodings. errors is an optional string that specifies how encoding errors are to be handled---this argument should not be used in binary mode. Pass 'strict' to raise a ValueError exception if there is an encoding error (the default of None has the same effect), or pass 'ignore' to ignore errors. (Note that ignoring encoding errors can lead to data loss.) See the documentation for codecs.register or run 'help(codecs.Codec)' for a list of the permitted encoding error strings. newline controls how universal newlines works (it only applies to text mode). It can be None, '', '\n', '\r', and '\r\n'. It works as follows: * On input, if newline is None, universal newlines mode is enabled. Lines in the input can end in '\n', '\r', or '\r\n', and these are translated into '\n' before being returned to the caller. If it is '', universal newline mode is enabled, but line endings are returned to the caller untranslated. If it has any of the other legal values, input lines are only terminated by the given string, and the line ending is returned to the caller untranslated. * On output, if newline is None, any '\n' characters written are translated to the system default line separator, os.linesep. If newline is '' or '\n', no translation takes place. If newline is any of the other legal values, any '\n' characters written are translated to the given string. If closefd is False, the underlying file descriptor will be kept open when the file is closed. This does not work when a file name is given and must be True in that case. A custom opener can be used by passing a callable as *opener*. The underlying file descriptor for the file object is then obtained by calling *opener* with (*file*, *flags*). *opener* must return an open file descriptor (passing os.open as *opener* results in functionality similar to passing None). open() returns a file object whose type depends on the mode, and through which the standard file operations such as reading and writing are performed. When open() is used to open a file in a text mode ('w', 'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open a file in a binary mode, the returned class varies: in read binary mode, it returns a BufferedReader; in write binary and append binary modes, it returns a BufferedWriter, and in read/write mode, it returns a BufferedRandom. It is also possible to use a string or bytearray as a file for both reading and writing. For strings StringIO can be used like a file opened in a text mode, and for bytes a BytesIO can be used like a file opened in a binary mode
主要会用到 file、mode、encoding参数,更多请 help(open)
fille 要打开的文件名,不存在抛异常
encoding 打开文件的编码,例如uft8
mode 打开文件的模式,如下模式
r # 只读模式(默认) a # 追加模式,从文件的末尾开始写(追加),只能写不能读. w # 写入模式,先清空文件内容后写 r+ # 读写模式,写的时候从文件的头部开始写 a+ # 追加模式,写的时候从文件的尾部追加写 (相当于linux的>>) w+ # 写读模式,先清空文件再写 (相当于linux的>) rb # 以二进制方式读, 其它同r (读取文件效率会高一点) wb # 以二进制方式写, 其它同w rt # 以文本读方式打开, (默认) wt # 以文本写方式打开, 如果文件不存在则创建(只能写) rb+ # 以二进制读方式打开, 可以读写 wb+ # 以二进制写方式打开, 可以读写 xt # 比wt多了个功能, 只有当文件不存在才能写
操作文件的方法
打开文件后得到是文件对象,通过对象内置的方式操作文件,有如下方法
read(size=-1) # 从文件流(stream)中最多读取n个字符,默认-1表示一次性读取所有 readline() # 读取一行, 每执行一次就读取一行直到没有 readlines(hint=-1) # 从文件流(strem)中返回一个包含行的的列表 tell() # 返回当前文件流(stream)的position(位置) seek(cookie, whence=0) # 改变文件流(stream)的position(位置),操作成功返回新的指针位置,否则返回-1 seekable() # 返回对象是否支持随机访问 write(text) # 将字符串写入stream(流) writelines(lines) # 将一个序列写入文件流(stream) flush() # 刷新写入缓冲区(如果可用) truncate(pos=None) # 将文件截断为字节大小, 仅支持'r+'、'rb+'、'w'、'wb'、'wb+'模式 close() # 刷新并关闭IO对象, 如果文件已经关闭则此方法无效
练习
read 方法 >>> fp = open('/tmp/passwd') >>> fp.read() # 所有的内容返回一个字符串 'root:x:0:0:root:/root:/bin/bash\nbin:x:1:1:bin:/bin:/sbin/nologin\ndaemon:x:2:2:daemon:/sbin:/sbin/nologin\nadm:x:3:4:adm:/var/adm:/sbin/nologin\nlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin\nsync:x:5:0:sync:/sbin:/bin/sync\nshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\nhalt:x:7:0:halt:/sbin:/sbin/halt\nmail:x:8:12:mail:/var/spool/mail:/sbin/nologin\nuucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin\noperator:x:11:0:operator:/root:/sbin/nologin\ngames:x:12:100:games:/usr/games:/sbin/nologin\ngopher:x:13:30:gopher:/var/gopher:/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\nntp:x:38:38::/etc/ntp:/sbin/nologin\nvirtual:x:500:500::/home/ftpsite:/sbin/nologin\ntcpdump:x:72:72::/:/sbin/nologin\ntest:x:501:501::/home/test:/bin/bash\nnginx:x:498:499:Nginx web server:/var/lib/nginx:/sbin/nologin\nabc:x:502:502::/home/abc:/bin/bash\ntomcat:x:8080:8080:Tomcat Server:/home/tomcat:/bin/bash\ndubbo:x:8081:8081:dubbo service:/home/dubbo:/bin/bash\n' >>> fp.read(8) # 可以读取指定读取几个字节数 '' >>> fp.close() # 关闭文件句柄 readline 方法 >>> fp = open('/tmp/passwd') # 打开文件 >>> fp.readline() # 读取一行 'root:x:0:0:root:/root:/bin/bash\n' >>> fp.readline() # 再读取一行 'bin:x:1:1:bin:/bin:/sbin/nologin\n' >>> fp.close() # 关闭文件句柄 readlines 方法 >>> fp = open('/tmp/passwd') # 打开文件 >>> fp.readlines() # 返回一列表,每一行是列表中的元素 ['daemon:x:2:2:daemon:/sbin:/sbin/nologin\n', 'adm:x:3:4:adm:/var/adm:/sbin/nologin\n', 'lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin\n', 'sync:x:5:0:sync:/sbin:/bin/sync\n', 'shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\n', 'halt:x:7:0:halt:/sbin:/sbin/halt\n', 'mail:x:8:12:mail:/var/spool/mail:/sbin/nologin\n', 'uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin\n', 'operator:x:11:0:operator:/root:/sbin/nologin\n', 'games:x:12:100:games:/usr/games:/sbin/nologin\n', 'gopher:x:13:30:gopher:/var/gopher:/sbin/nologin\n', 'ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin\n', 'nobody:x:99:99:Nobody:/:/sbin/nologin\n', 'vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin\n', 'saslauth:x:499:76:"Saslauthd user":/var/empty/saslauth:/sbin/nologin\n', 'postfix:x:89:89::/var/spool/postfix:/sbin/nologin\n', 'sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin\n', 'ntp:x:38:38::/etc/ntp:/sbin/nologin\n', 'virtual:x:500:500::/home/ftpsite:/sbin/nologin\n', 'tcpdump:x:72:72::/:/sbin/nologin\n', 'test:x:501:501::/home/test:/bin/bash\n', 'nginx:x:498:499:Nginx web server:/var/lib/nginx:/sbin/nologin\n', 'abc:x:502:502::/home/abc:/bin/bash\n', 'tomcat:x:8080:8080:Tomcat Server:/home/tomcat:/bin/bash\n', 'dubbo:x:8081:8081:dubbo service:/home/dubbo:/bin/bash\n'] >>> fp.close() # 关闭文件句柄 write、writelines 方法 >>> fp = open('/tmp/passwd', 'a') # 追加写 >>> fp.write("one\n") # 追加一行 3 >>> fp.write("two\n") # 在追加一行 3 >>> li = ['a\n', 'b\n', 'c\n'] >>> fp.writelines(li) # 将一个序列写入到文件(列表中的每个元素是一行,每行写入到文件) >>> fp.close() # 关闭文件句柄 tell、seek 方法 >>> fp = open('/tmp/passwd') >>> fp.tell() # 当前文件指针位置 0 >>> fp.readline() # 读取一行 'root:x:0:0:root:/root:/bin/bash\n' >>> fp.tell() # 当前文件指针位置 32 >>> fp.seek(0, 1) # 文件指针位置移到当前 32 >>> fp.tell() # 当前文件指针位置 32 >>> fp.seek(0,2) # 文件指针设移到末尾 1228 >>> fp.tell() # 当前文件指针位置 1228 >>> fp.read() # 读取一次, 没有内容,因为指针到末尾没有数据 '' >>> fp.close() # 关闭文件句柄
遍历文件
文件对象实际是一个iterator(迭代器)对象
>>> from collections import Iterator >>> f = open('/etc/passwd') >>> isinstance(f, Iterator) # 文件对象是Iterator类型的对象 True
既然是可迭代对象,那么就可以通过for遍历
for line in f: print(line, end='') # 注: print通常会多加一个换行,将参数end=''规避
文件指针
文件以某种模式打开后得到的是个file对象,对象内部会记住当前的position(位置),用于定位从哪个position开始读或写,这个position称为文件的指针位置(是个整数).
你可以把指针位置理解为光标的位置。
文件的指针位置与打开的模式有关,除了a和a+两个模式以外,其它模式一开始文件的指针指向的是文件的头部, 即postion=0,因为a和a+两种模式是追加方式,文件的指针指向的是文件的末尾
你可以通过对象的tell、seek方法查看、移动指针位置,用于控制从某个位置开始读或写
tell 方法获取文件当前的指针位置
>>> fp = open('install.log') # 打开文件对象fp >>> fp.tell() # 获取当前对象指针位置,一开始在头部, 即postion=0 0 >>> fp.readline() # 读取一行 'Installing libgcc-4.4.7-4.el6.x86_64\n' >>> fp.tell() # 此时对象的指针位置,即postion=37 37 >>> fp.read(9) # 再读取9个字节 'warning: ' >>> fp.tell() # 读取后的指针位置,即postion=36 46 >>> fp.close() # 关闭文件对象
seek 方法移动文件指针位置
在文件的读写的过程中,需要从另外一个位置进行操作,可以使用seek,seek的用法:
seek(cookie, whence=0)
cookie: 偏移量
whence: 相对位置
0 表示从文件开头(默认)
1 表示从当前的位置 (感觉用处不大)
2 表示文件末尾位置
注: 在py3中如果whence的值不是0,那么cookie的值必须为0, 否则异常.
例子1: 往后偏移7个字节
>>> fp = open('install.log') # 打开文件对象fp
>>> fp.tell() # 获取当前对象的指针位置,一开始在头部, 即postion=0
0
>>> fp.seek(7) # 文件指针往后偏移7个字节
7
>>> fp.tell() # 移动后的文件指针位置,即postion=7
7
>>> fp.readline() # 读取一行,从第7个字节开始读
'ing libgcc-4.4.7-4.el6.x86_64\n'
>>> fp.tell() # 读取后的指针位置postion=37
37
>>> fp.close() # 关闭文件
例子2: 末尾往前面偏移3个字节
先通过seek(0, 2)到文件末尾
再通过tell()方法拿到文件末尾的指针位置, 然后减去3, 得到新的指针位置,命名成end
然后在seek(end),那么指针位置就往后偏向end个字节, 等价于末尾往前面偏向3个字节
>>> fp = open('install.log') # 打开文件对象fp >>> fp.seek(0, 2) # 通过seek移动到文件末尾,返回也是个指针位置 9072 >>> fp.tell() # 获取当前的指针位置 9072 >>> end = fp.tell() - 3 # 减去3 >>> fp.seek(end) # 重新设置文件指针到end个字节,相当于末尾往前面偏移3个字节 9069 >>> fp.tell() # 获取当前的指针位置 9069 >>> fp.read() # 读取剩余的内容 '***'
但是不能下面的操作, 因为whence不是0时,cookie必须是0
>>> fp = open('install.log') >>> fp.tell(-3, whence=2) # 不支持的操作,这样会出现异常 Traceback (most recent call last): File "<stdin>", line 1, in <module> io.UnsupportedOperation: can't do nonzero end-relative seeks
二、使用with...open
使用open函数操作文件完成后每次都要close释放文件资源,有可能会忘记。 所以建议是使用with...open方式来操作文件,with语句是一种上下文管理机制,可以在文件操作完成后自动释放文件资源.
用法
with open('文件名') as 别名: "操作文件动作"
只需要在open前面使用with,后面跟一个as语句命名成一个名字。 它的意思是open打开文件后得到的文件对象并命名成一个名字,然后通过这个名字就可以操作文件了,最后会自释放文件资源,相当于帮你执行了close。注:操作文件的动作要放到with语句块中
例子
with open('/etc/passwd') as fp: # open打开文件得到对象并命名成fp,就可以使用fp的内置方法来操作文件 for line in fp: print(line)
等价于
try: f = open('/etc/passwd') for line in f: print(line) finally: f.close()
with嵌套
在py2.7以上版本还可以同时操作多个文件对象,下面的例子读取一个文件的内容进行修改写入到另一个文件中
with open('file_1') as f: with open('file_2', 'w') as w: for line in f: line = line.replace('root', 'ROOT') # 小写替换成大写 w.write(line)
注: 如果操作很大的文件需要考虑内存的问题
更多操作文件练习与例子,请参考
三、处理压缩文件
内置的zipfile、gzip、bz2库用来读写压缩文件。zipfile模块处理zip格式的文件、gzip模块处理gz格式的文件、bz2模块处理bz2格式的文件
gzip格式文件
读
import gzip with gzip.open('accees_log.gz', 'rt') as f: # 'accees_log.gz' 表示文件, 'rt' 表示模式 for line in f: print(line)
写
import gzip contnet = 'Lots of content here' with gzip.open('accees_log.gz', 'wt', compresslevel=4) as f: # 创建accees_log.gz压缩文件, compresslevel参数是压缩的级别 f.write(content)
更多参考gziip库
bz2格式文件
读
import bz2 with bz2.open('filename.gz', 'rt') as f: for line in f: print(line)
写
import bz2 with bz2.open('filename.gz', 'wt', compresslevel=4) as f: # 可选的指定compresslevel关键字参数, 定义压缩的级别 f.write('文件的内容')
更多参考bz2库
zip格式文件
import zipfile
# 添加三个.py文件到压缩myzips.zip压缩文件中 z = zipfile.ZipFile('myzips.zip', 'w', zipfile.ZIP_DEFLATED) z.write('pynote.py') z.write('record.py') z.write('sh.py') z.printdir() # 打印zip内文件的信息 zipfile.is_zipfile('myzip.zip') # 判断一个文是否为zip文件 zipfile.ZipFile('myzip.zip').namelist() # 列出zip文件中的文件 z.extract('pynote.py', 'E:\\') # 解压单个文件pynote.py到指定的E:\\目录 z.extractall('E:\\') # 解压所有文件到指定的E:\\目录 z.close() # 关闭资源
更多参考zipfile库
四、序列化与反序列化
序列化, 将内存中的数据(对象)变成可存储或传输的过程称为序列化, 序列化后的对象可以保存变量或文件中,也可以通过网络传输给其它机器,保存到文件中叫持久化.
反序列化, 是 序列化的反向操作,将存储里面的数据(对象)加载到内存
序列化的作用和场景: 保存当时对象的状态。 场景: 打游戏的时候按暂停,保存暂停的状态(数据),恢复的时候从暂停那会恢复,而不是从从头开始
Python中有个两个序列化模块:json和pickle,主要会用到它们的四个方法:
- dump 对象序列化持久存储到文件中
- dumps 对象序列化
- load 将文件中对象反序列化到内存中
- loads 对象反序列化
注: dump对应的是load, dumps对应的是loads
pickle
# 原始数据 li = [1,2,3]
序列化到变量
import pickle
new_li = pickle.dumps(li) # 将li这个list序列化, 存储在new_li变量中 print(new_li) # b'\x80\x03]q\x00(K\x01K\x02K\x03e.' 序列化后的数据是bytes类型
反序列化读取
two_li = pickle.loads(new_li) # 将new_li反序列化,还原出列表 print(two_li) # [1, 2, 3]
将内存中对象序列化存储到文件
with open('data.pk', 'wb') as fp: # 将li这个列表对象序列化后存储到data.pk文件中, 必须使用二进制模式打开文件 pickle.dump(li, fp)
从文件中反序列化对象到内存
with open('data.pk', 'rb') as fp: # 从data.pk文件中反序列化对象到内存中 ret_list = pickle.load(fp) print(ret_list) # [1, 2, 3] // 列表被还原了
json
# 原始数据 dic = {'k1':'v1', 'k2':'v2'}
序列化到变量
import json
new_dic = json.dumps(dic) # 将dic这个dict序列化, 存储在new_dic变量中 print(new_dic) # {"k1": "v1", "k2": "v2"} print(type(new_dic)) # 序列化后的类型str
反序列化读取
two_dic = json.loads(new_dic) # 将new_dic反序列化, 还原出字典 print(two_dic) # {'k1': 'v1', 'k2': 'v2'} // 字典被还原出来了 print(type(two_dic))
将内存中的对象序列化存储到文件
with open('data.dbs', 'w') as fp: # 将dic这个对象序列化存储到data.dbs文件中 json.dump(dic, fp)
从文件中反序列化对象到内存
with open('data.dbs', 'r') as fp: # 从data.dbs文件中反序列化将对象加载到内存中 ret_dic = json.load(fp) print(ret_dic) # {'k1': 'v1', 'k2': 'v2'}
总结
- 应该使用with..open方式操作文件, 操作完成后一定要close掉,否则占用资源, 'rb'模式读取效率可能会比较高
- 文件对象实际是一个iterator(迭代器), 那么可以通过for遍历
- 理解和熟练使用操作文件的常用方法、文件指针 还有seek操作
- 处理压缩文件用到的三个模块 zipfile、gzip、bz2
- 理解序列和反序列化的概念,会用到pickle,json两个模块
- 当要操作很大的文件时候需要考虑内存的问题,比如readlines方法占用内存比较大
- 当遍历一个很大的文件时候, 如果数据处理完了就不用在读取后面的数据
浙公网安备 33010602011771号