5、IO流

File

Python内置的open()函数,传入文件名和标示符,open()将会返回一个File对象,完整的语法格式如下:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
'''
file: 必需,文件路径(相对或者绝对路径)。
mode: 可选,文件打开模式
buffering: 设置缓冲
encoding: 默认使用操作系统的字符集编码,所以为了统一,必须设置为utf-8
errors: 报错级别
newline: 区分换行符
closefd: 传入的file参数类型
opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。
'''
模式 描述
t 文本模式 (默认)。
x 模式,新建一个文件,如果该文件已存在则会报错。
b 二进制模式。
+ 打开一个文件进行更新(可读可写)。
U 通用换行模式(Python 3 不支持)。
r 只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

默认为文本模式,如果要以二进制模式打开,加上 b

# 使用try-except-finally,不管有没有出错,都会关闭文件
try:
    # 以制度r的方式打开文件1.txt,如果文件不存在,程序会抛出IOError
    file = open('./1.txt','r')
    # 调用read()方法可以一次读取文件的全部内容
    a = file.read()
except IOError as e:
    print(e)
finally:
    # 关闭文件,文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源
    file.close()
print(a)

每次都写try-except-finally过于繁琐,所以可以使用with,自动帮我们调用close()方法

with open('./1.txt','r+',encoding='utf8') as file:
    file.write('你好呀')
    a = file.read()
print(a)

字符编码

要读取非UTF-8编码的文本文件,需要给open()函数传入encoding参数,例如,读取GBK编码的文件

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

File对象api

read(size)

调用read(size)方法,每次最多读取size个字符的内容,当 size 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。

with open('1.txt', mode='r', encoding='utf-8') as f:
    content = f.read()
    for line in content.splitlines(): # 使用splitlines将内容按照换行符进行分割
        print(line)

readline()

readline() 会从文件中读取单独的一行。换行符为 '\n'。f.readline() 如果返回一个空字符串, 说明已经已经读取到最后一行

with open('1.txt', mode='r', encoding='utf-8') as f:
    while True:
        line = f.readline()
        if not line:
            break
        print(line.strip())  # 使用 strip 去除每行末尾的换行符

readlines()

readlines() 将返回该文件中包含的所有行,一个字符串组成的列表(list)

with open('1.txt', mode='r', encoding='utf-8') as f:
    lines = f.readlines()
    for line in lines:
        print(line.strip())

write(string)

f.write(string) 将 string 写入到文件中, 然后返回写入的字符数

with open('1.txt',mode='a',encoding='utf-8') as f:
    for i in range(10):
        f.write(f'this is line {i}\n')

tell()

f.tell() 返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。

seek()

如果要改变文件指针当前的位置, 可以使用 f.seek(offset, from_what) 函数

offset:文件指针移动的字符数,整数为往结尾方向移动,负数为往开头方向移动

from_what:如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾

StringIO和BytesIO

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

StringIO

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

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

from io import StringIO
f = StringIO()
f.write('hello')
f.write(' ')
f.write('world!')
print(f.getvalue())

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

要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取:

from io import StringIO
f = StringIO('Hello!\nHi!\nGoodbye!')
while True:
	s = f.readline()
	if s == '':
		break
	print(s.strip())

BytesIO

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

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

from io import BytesIO
f = BytesIO()
f.write('中文'.encode('utf-8'))
#如果使用getvalue,是将所有内容读出来
print(f.getvalue())

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

和StringIO类似,可以用一个bytes初始化BytesIO,然后,像读文件一样读取:

from io import BytesIO
f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
#如果使用read,需要先将文件指针定位到开头
f.seek(0)
f.read()

操作系统接口模块(OS)

如果我们要操作文件、目录,可以在命令行下面输入操作系统提供的各种命令来完成。比如dircp等命令

如果要在Python程序中执行这些目录和文件的操作怎么办?其实操作系统提供的命令只是简单地调用了操作系统提供的接口函数,Python内置的os模块也可以直接调用操作系统提供的接口函数。

系统

os.name,获取操作系统类型

import os
print(os.name)
#如果是posix,说明系统是Linux、Unix或Mac OS X,如果是nt,就是Windows系统

os.uname(),获取详细的系统信息,在Windows上不提供,也就是说,os模块的某些函数是跟操作系统相关的

os.environ,获取系统的环境变量

import os
print(os.environ)
'''
environ({'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\92988\\AppData\\Roaming',
'PATH': 'D:\\python\\python3.6.8\\Scripts\\;D:\\python\\python3.6.8\\;%JAVA_HOME%\\bin'})
'''

os.environ.get('key'):获取指定环境变量,可以写为os.getenv('key')

import os
print(os.environ.get('JAVA_HOME'))
print(os.getenv('JAVA_HOME'))
'''
D:\java\jdk1.8.0_301
'''

os.sep:获取当前系统的路径分隔符

命令

os.system('command'):执行系统命令,并返回是否执行成功,0则表示执行成功,为其他值则表示执行不成功

os.popen('command'):执行系统命令,返回命令输出内容

除了这两个命令,python2.4开始提供新的模块subprocess来处理系统命令

import subprocess
#stdout = subprocess.PIPE,stderr = subprocess.STDOUT这两个参数主要是为了限制控制台输出
cmd = subprocess.Popen('java',stdout = subprocess.PIPE,stderr = subprocess.STDOUT)
#由于在windows环境下使用,所以编码为gbk
print(cmd.stdout.read().decode('gbk'))

目录和路径操作

os.getcwd():查看当前所在的工作目录

os.chdir(path):改变当前工作目录到另外的路径

os.mkdir('path'):创建一个目录

import os
os.mkdir('d:/test')

os.makedirs('path'):递归生成目录,如果目录全部存在,抛出错误,exist_ok=True 指定了,如果某个要创建的目录已经存在,也不报错

os.rmdir('path'):删除一个目录,如果目录非空,则抛出一个OSError

os.removedirs('path'):递归删除一个目录

os.remove('path'):删除一个文件,如果路径是一个目录,会抛出OSError

os.rename('old','new'):重命名文件或目录

os.renames('old','new'):递归重命名文件或目录

os.listdir('path'):返回path指定的目录包含的文件或文件夹的名字的列表

import os
# renames可以递归对路径中的所有目录重命名
os.renames('d:/test/test','d:/test3/test4')

路径(os.path)

该模块主要用于获取文件或目录的属性

# 查看当前目录的绝对路径
p1 = os.path.abspath('.')
# 在某个目录下创建一个新目录,返回新目录的完整路径
p2 = os.path.join('/Users/michael', 'testdir')
方法 说明
os.path.abspath(path) 返回绝对路径
os.path.basename(path) 返回文件名
os.path.commonprefix(list) 返回list(多个路径)中,所有path共有的最长的路径
os.path.dirname(path) 返回文件路径
os.path.exists(path) 路径存在则返回True,路径损坏返回False
os.path.lexists 路径存在则返回True,路径损坏也返回True
os.path.expanduser(path) 把path中包含的"~"和"~user"转换成用户目录
os.path.expandvars(path) 根据环境变量的值替换path中包含的"$name"和"${name}"
os.path.getatime(path) 返回最近访问时间(浮点型秒数)
os.path.getmtime(path) 返回最近文件修改时间
os.path.getctime(path) 返回文件 path 创建时间
os.path.getsize(path) 返回文件大小,如果文件不存在就返回错误
os.path.isabs(path) 判断是否为绝对路径
os.path.isfile(path) 判断路径是否为文件
os.path.isdir(path) 判断路径是否为目录
os.path.islink(path) 判断路径是否为链接
os.path.ismount(path) 判断路径是否为挂载点
os.path.join(path1[, path2[, ...]]) 把目录和文件名合成一个路径
os.path.normcase(path) 转换path的大小写和斜杠
os.path.normpath(path) 规范path字符串形式
os.path.realpath(path) 返回path的真实路径
os.path.relpath(path[, start]) 从start开始计算相对路径
os.path.samefile(path1, path2) 判断目录或文件是否相同
os.path.sameopenfile(fp1, fp2) 判断fp1和fp2是否指向同一文件
os.path.samestat(stat1, stat2) 判断stat tuple stat1和stat2是否指向同一个文件
os.path.split(path) 把路径分割成 dirname 和 basename,返回一个元组
os.path.splitdrive(path) 一般用在 windows 下,返回驱动器名和路径组成的元组
os.path.splitext(path) 分割路径中的文件名与拓展名
os.path.splitunc(path) 把路径分割为加载点与文件
os.walk(path) for (dirpath, dirnames, filenames) in os.walk(targetDir);dirpath 代表当前遍历到的目录名; dirnames 是列表对象,存放当前dirpath中的所有子目录名;filenames 是列表对象,存放当前dirpath中的所有文件名
os.path.supports_unicode_filenames 设置是否支持unicode路径名

遍历目录

os.walk(path)方法会深入遍历 path 以及 path 下所有子目录。

for (dirpath,dirnames,filenames) in os.walk('./'):
    print(f'当前遍历的目录是:{dirpath}')
    print(f'{dirpath}下存在的子目录有:{dirnames}')
    print(f'{dirpath}下存在的文件有:{filenames}')

shutil

shutil 模块里面有很多目录文件操作的函数

拷贝文件

注意,如果拷贝前,e:/first.py 已经存在,则会被拷贝覆盖,所以使用该函数一定要小心。

from shutil import copyfile

# 拷贝 d:/tools/first.py 到 e:/first.py
copyfile('d:/tools/first.py', 'e:/first.py')

拷贝目录

如果我们要拷贝一个目录里面所有的内容(包括子目录和文件)到另外一个目录中,可以使用 shutil的copytree函数。

注意拷贝前, 目标目录必须不存在 ,否则会报错。

from shutil import copytree

# 拷贝 d:/tools/aaa 目录中所有的内容 到 e:/bbb 中
copytree('d:/tools/aaa', 'e:/new/bbb')

序列化

我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

Python提供了pickle模块来实现序列化。

dumps

dumps可以将对象序列化为一个bytes

import pickle

#声明一个dict字典
a = {'name':'lucy','age':18}
#将对象序列化为bytes
result = pickle.dumps(a)

print(result)
'''
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x04\x00\x00\x00lucyq\x02X\x03\x00\x00\x00ageq\x03K\x12u.'
'''

dump

dump可以将对象序列化,并且写入一个文件

import pickle

#声明一个dict字典
a = {'name':'lucy','age':18}
#声明需要写入的File对象,以二进制模式打开并写入
file = open('./person.txt','wb')
#将对象序列化,并写入文件
pickle.dump(a,file)
file.close()

loads

loads可以将读到到bytes反序列化为对象

import pickle

a = pickle.loads(b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x04\x00\x00\x00lucyq\x02X\x03\x00\x00\x00ageq\x03K\x12u.')

print(a)

'''
{'name': 'lucy', 'age': 18}
'''

load

load可以将文件中的数据反序列化为python对象

import pickle

file = open('./person.txt','rb')

a = pickle.load(file)

print(a)

file.close()

'''
{'name': 'lucy', 'age': 18}
'''