5、IO流

File

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

open(file, mode)

完整的语法格式:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
'''
file: 必需,文件路径(相对或者绝对路径)。
mode: 可选,文件打开模式
buffering: 设置缓冲
encoding: 一般使用utf8
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 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回

readline()

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

readlines()

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

with open('./1.txt','r+',encoding='utf8') as file:
    lines = file.readlines()
    for str in lines:
        print(str)

write(string)

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

with open('./1.txt','w',encoding='utf8') as file:
    num = file.write('你好呀')
    print('写入字符数:%i' % num)

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.mkdir('path'):创建一个目录

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

os.makedirs('path'):递归生成目录,如果目录全部存在,抛出错误

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

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

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

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

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

os.listdir('path'):返回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.path.walk(path, visit, arg) 遍历path,进入每个目录都调用visit函数,visit函数必须有3个参数(arg, dirname, names),dirname表示当前目录的目录名,names代表当前目录下的所有文件名,args则为walk的第三个参数
os.path.supports_unicode_filenames 设置是否支持unicode路径名

序列化

我们把变量从内存中变成可存储或传输的过程称之为序列化,在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}
'''

Json

如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下

JSON类型 Python类型
{} dict
[] list
"string" str
1234.56 int或float
true/false True/False
null None

Python内置的json模块提供了非常完善的Python对象到JSON格式的转换

dumps

dumps()方法返回一个str,内容就是标准的JSON

json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False, **kw)

obj:就是你要转化成json的对象。

sort_keys =True:是告诉编码器按照字典排序(a到z)输出。如果是字典类型的python对象,就把关键字按照字典排序。

indent:参数根据数据格式缩进显示,读起来更加清晰,None为不缩进,数值为缩进的空格数

separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了。

skipkeys:默认值是False,如果dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key 。

ensure_ascii:默认输出ASCLL码,如果把这个该成False,就可以输出中文。

check_circular:如果check_circular为false,则跳过对容器类型的循环引用检查,循环引用将导致溢出错误(或更糟的情况)。

allow_nan:如果allow_nan为假,则ValueError将序列化超出范围的浮点值(nan、inf、-inf),严格遵守JSON规范,而不是使用JavaScript等价值(nan、Infinity、-Infinity)。

default:default(obj)是一个函数,它应该返回一个可序列化的obj版本或引发类型错误。默认值只会引发类型错误。

import json

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

str = json.dumps(a)

print(str)

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

dump

dump可以将转换后的json字符串写入文件

import json

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

file = open('./person.txt','w')
json.dump(a,file)
file.close()

loads

可以将json转换为python对象

import json

a = json.loads('{"name": "lucy", "age": 18}')

print(a)
print(type(a))

'''
{'name': 'lucy', 'age': 18}
<class 'dict'>
'''

load

可以将json文件,转换为python对象

import json

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

a = json.load(file)

print(a)

file.close()

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

自定义类型-JSON

由于python和json之间的互相转换只限制于python内置基本数据类型,对于自定义的数据类型,如果使用dumps进行转换,会抛出TypeError;但是dumps函数有一个形参default,这个参数需要传一个函数,函数需要可以将该对象转为python数据类型,并返回,例如dict字典

import json

class Person(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

def person2json(per):
    "将person对象转为dict"
    return {
        'name':per.name,
        'age':per.age
    }

person = Person('lucy',18)

str = json.dumps(person,default=person2json)
print(str)

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

通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class,所以我们可以不用给每个对象都写一个转dict的函数

import json

class Person(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

person = Person('lucy',18)

str = json.dumps(person,default=lambda per : per.__dict__)
print(str)

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