Python基础之文件处理

一 文件读写

读写文件是最常见的IO操作。现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符或文件句柄),然后通过操作系统提供的接口对这个文件对象进行读写操作。

1.1 文件操作

以读文件的模式打开一个文件对象,使用python内置open()函数,传入文件路径、模式、模式及其他参数:

f = open('mt.py','r',encoding='utf8')
data = f.read()
print(type(data))     # <class 'str'>
f.close()

模式“r”表示读,为默认方式,这样我们就成功的打开了一个文件。如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示;最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的。

过程分析:

1、由应用程序向操作系统发系统调用open()

2、操作系统打开该文件,并返回一个文件句柄给应用程序

3、应用程序将文件句柄赋值给变量f,然后进行读取、关闭操作

模式种类:

1、打开文件的模式有(默认为文本模式):

  • r   :只读模式【默认模式,文件必须存在,不存在则抛出异常】
  • w :只写模式【不可读;不存在则创建;存在则清空内容】
  • a  :追加写模式【不可读;不存在则创建;存在则只追加内容】

2、对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式),相对应的,也有三种方式:

  • rb
  • wb
  • ab

注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码

f = open('mt.py','rb')
data = f.read()
print(type(data))     # <class 'bytes'>
print(data.decode('utf8'))
f.close()

3、"+" 表示可以同时读写某个文件(r+、w+、a+)

注意点:

由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来实现。

try:
    f = open('mt.py', 'r', encoding='utf8')
    print(f.read())
finally:
    if f:
        f.close()

为了方便,python引入了with语句:

with open("mt.py", "r") as f:

这和try....finally...一致,同时则不需要自行进行close操作,避免因没有执行close出错。

操作文件的方法

read():一次性读取文件的全部内容,光标移动到文件末尾;

read(size):可指定每次最多读取size个字节的内容;

readline():每次读取一行内容,光标移动到第二行首部;

readlines():一次性读取所有内容并按行返回list;

write('1111\n222\n'):针对文本模式的写,需要自己写换行符; write('1111\n222\n'.encode('utf-8')) :针对b模式的写,需要自己写换行符

flush() :立刻将文件内容从内存刷到硬盘

补充:假如不知道需要处理的文件是什么编码怎么处理?

import chardet


result = chardet.detect(open("test.txt",'rb').read())
print(result)  # {'confidence': 0.99, 'encoding': 'GB2312', 'language': 'Chinese'}

1.2 file-like Object

像open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。file-like Object不要求从特定类继承,只要写个read()方法就行。

StringIO就是在内存中创建的file-like Object,常用作临时缓冲。

1.3 文件内光标移动(seek)

read(3):

  • 文件打开方式为文本模式时,代表读取3个字符
  • 文件打开方式为b模式时,代表读取3个字节
  • 其余的文件内光标移动都是以字节为单位如seek,tell,truncate

注意:

  • seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的;
  • truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+、a、a+等模式下测试效果

1.4 文件的修改

文件的数据是存放于硬盘上的,因而只存在覆盖、不存在修改这么一说,我们平时看到的修改文件,都是模拟出来的效果,具体的说有两种实现方式:

方式一:将硬盘存放的该文件的内容全部加载到内存,在内存中是可以修改的,修改完毕后,再由内存覆盖到硬盘(word,vim,nodpad++等编辑器)

import os
with open('mt.py') as read_f,open('mt.py.swap','w') as write_f:
    data = read_f.read()                        # 全部读入内存,如果文件很大,会很卡
    data = data.replace('world','joe1991')      # 在内存中完成修改
    write_f.write(data)                         # 一次性写入新文件
os.remove('mt.py')                              # 删除老文件
os.rename('mt.py.swap','mt.py')                 # 将文件名进行更改

方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖源文件

import os
with open('mt.py') as read_f,open('mt.py.swap','w') as write_f:
    for line in read_f:
        line = line.replace('world','joe1991')
        write_f.write(line)
os.remove('mt.py')
os.rename('mt.py.swap','mt.py')

这种方式避免了文件太大,一次性读取太卡的问题。

二、StringIO和BytesIO

2.1 StringIO

很多时候,数据读写不一定是文件,也可以在内存中读写。

StringIO顾名思义就是在内存中读写str。

要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:

>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world')
5
>>> f.getvalue()
'hello world'

getvalue()方法用于获得写入后的str。

2.2 BytesIO

StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。

BytesIO实现了在内存中读写bytes,我们创建一个BytesIO,然后写入一些bytes:

>>> from io import BytesIO
>>> b = BytesIO()
>>> b.write('中国'.encode('utf-8'))
6
>>> b.getvalue()
b'\xe4\xb8\xad\xe5\x9b\xbd'
>>> b.getvalue().decode('utf-8')
'中国'

请注意,写入的不是str,而是经过UTF-8编码的bytes

总结:StringIO和BytesIO是在内存中操作str和bytes的方法,使得和读写文件具有一致的接口。

posted @ 2018-05-31 22:16  Joe1991  阅读(104)  评论(0)    收藏  举报