Python基础(十)--文件相关

目录

 

                                Python基础(十)--文件相关

1 读写文件

1.1 获取文件对象

1.2 文件读取

1.3 文件写入

1.4 文件定位

2 文件与路径的操作

2.1 os模块

2.2 os.path模块

2.3 shutil模块

2.4 glob模块

3 序列化

3.1 csv

3.2 json

3.3 pickle

4 上下文管理器

4.1 自定义上下文管理器

4.2 @contextmanager装饰器


                                Python基础(十)--文件相关

1 读写文件

1.1 获取文件对象

在操作系统中,一个text文档,一张图片,一首音乐,这些都是文件。文件会以其固有的格式保存在硬盘中。文件可以分为两种类型:①文本文件:文本文件由若干可识别的字符组成,并且不能包含非文本字符之外的内容(图片等),如,.txt,.bat都是文本文件,而.doc,.pdf则不是文本文件②二进制文件:由可识别的字符组成,如果我们用文本编辑器打开二进制文件,往往看到的都是一堆乱码。

底层的角度来说,一切都是二进制格式的,文本文件,也是二进制文件的一种,只是其内容,是我们能够识别的字符而已。

在Python中,可以通过open函数返回文件对象。文件对象是一个泛指,可以表示文件,也可以表示文件夹(路径)。格式为:open(file, mode='r') file指定文件的路径(相对路径或绝对路径),mode指定打开文件的模式,如下表。当读入文件或者写入文件时,会涉及到文件指针。文件指针指向的就是下一次要读取或写入的字符(或字节)位置,随着读取或写入的进行,文件指针也会随之而移动。

模式

说明

r(1)

读模式(默认模式),用来读取文件内容。文件指针在文件的开头。文件需要事先存在,否则会产生异常。

w(1)

写模式,用来向文件写入数据。文件指针在文件的开头。如果文件存在,则覆盖文件,否则会创建文件。

a(1)

追加模式,用来向文件追加数据。文件指针在文件的末尾。如果文件存在,不会覆盖文件(追加写入),否则会创建文件。

x(1)

写模式,用来向文件写入数据。文件指针在文件的开头。如果文件不存在,创建文件,否则产生错误。

t(2)

文本(字符串)模式(默认模式),以文本形式操作文件。

b(2)

二进制(字节)模式,以二进制形式操作文件。

U(3)

通用换行符模式(已不建议使用)。在该模式下,\n,\r\n或\r都会解析为换行符。不能与w、a、x或+模式同时使用(仅限于读取文件时使用)。

+(3)

读取或写入。

上表的(1)(2)(3)模式中,不同组的模式可同时使用(除了与U不兼容的模式),例如rt,wb+等。同一组的模式同时使用,如rw,tb等。

当文件不再使用时,我们需要对文件进行关闭,从而断开与外部文件的连接。断开连接可以调用文件对象的close方法。

# open函数,参数1:文件路径,参数2:操作文件的模式,返回文件对象
# 以r(读取)模式操作文件时,文件必须存在,否则会产生FileNotFoundError
fr = open("E:/test/test.txt","r")
# 以w(写入)或a(追加)模式操作文件时,文件不存在,不报错而是创建文件
fw = open("E:/test/test1.txt","w")
fa = open("E:/test/test2.txt","a")
# 以x(写入)模式操作文件时,文件必须存在,否则会产生FileNotFoundError
fx = open("E:/test/test3.txt","w")
# 访问文件结束后,需要断开与文件的连接,直接调用close并不好,可能在close方法前产生异常
# finally方式关闭文件
try:
    f = open("E:/test/test.txt", "r")
finally:
    f.close()
# with处打开文件,可以保证在with结束后一定能够有效的关闭,无序显示调用close方法
# with中可以使用as来保存文件对象(获得文件兑现高的引用)
with open("E:/test/test.txt") as f:
    pass

1.2 文件读取

读取文件的几种方式如下:

方法 描述
read(size=-1) 读取并返回文件内容。size指定读取的大小。如果是文本模式,以字符为单位,如果是二进制模式,以字节为单位。如果size省略,或者为负数,则返回文件的全部内容。如果文件已经没有内容可读取,返回空串(""或b"")
readlline() 返回文件的一行,保留末尾的换行符。如果没有内容可以读取,返回空串(""或b"")。
readllines() 返回一个列表,列表中的每个元素为文件的一行内容,每行保留末尾的换行符。

文件对象也是迭代器:如果文件过大,这会占据大量的内容空间。此时readlines不是一个好的选择。对于文件对象,其本身也是一个迭代器类型,我们可以使用for循环的方式对文件对象进行迭代,从而节省内存。

# 文件读写操作
with open("E:/test/test.txt","rt") as f:
    # t模式以字符(文本)形式操作文件,b模式以字节(二进制)形式操作文件
    # read读取文件的数据,参数指定读取的单位,如果参数缺失,或者为负值表示读取所有
    # print(f.read(1))
    # print(f.read())
    pass
# 如果使用了t模式,要求操作文件的编码方式与目标文件的编码方式一致
# 如果没有显示指定编码方式,则使用平台的编码方式,可以使用encoding指定操作文件的编码方式
with open("E:/test/test.txt","rt",encoding="UTF-8") as f:
    print(f.read())
    # 读取文件的一行
    while True:
        line = f.readline()
        if line:
            print(line,end=" ")
        else:
            break
    # 返回一个列表,列表中的元素为每一行的内容,保留换行符
    # lines = f.readlines()
    # for line in lines:
    #     print(line,end=" ")
# 文件是迭代器类类型,每个元素就是文件的一行
from collections.abc import Iterator
with open("E:/test/test.txt","rt",encoding="UTF-8") as f:
    print(isinstance(f,Iterator))
    for line in f:
        print(line,end="")

1.3 文件写入

向文件写入的几种方式如下:

方法 描述
write(content) 将参数写入到文件中,返回写入的长度。如果是文本模式,以字符为单位,如果是二进制模式,以字节为单位
writelines(lines) 参数lines为列表类型,将列表中所有元素写入文件中
# 文件写入
with open("E:/test/a.txt","wt") as f:
    f.write("写入")
    # 向文件写入列表
    f.writelines(["这是\n","列表\n"])
# b模式写入
with open("E:/test/a.txt","wb") as f:
    f.write(b"abc")
# 文件复制
with open("E:/test/b.txt","rb") as f1,open("E:/test/a.txt","rb") as f2:
    for line in f1:
        f2.write(line)

1.4 文件定位

通过文件对象的tell()和seek()方法可以获取或设置文件指针的位置

tell():返回文件指针的位置,即下一个要读取或写入的字符(字节)位置。以字节为单位。

seek(offset, whence): 改变文件的指针。offset指定新的索引位置偏移量。whence指定偏移量的参照位置:0:从文件头计算;1:从当前位置计算;2:从文件末尾计算

在文本模式下,如果whence的值不为0,则仅当offset的值为0时才是有效的。

# 文件定位
with open("E:/test/a.txt","rt",encoding="UTF-8") as f:
    # 返回当前文件指针的位置,以字节为单位
    print(f.tell())
    f.read(2)
    print(f.tell())
    # 改变文件指针的位置,参数1:偏移量,参数2:参照物
    # 在t模式下,如果第二个参数不为0,则只有第一个参数为0才支持,b模式无此限制
    f.seek(3,0)
    print(f.tell())

2 文件与路径的操作

2.1 os模块

os模块提供了很多操作目录与文件的功能,常用方法如下:

方法名 描述
mkdir(path) 创建path指定的目录。如果path所在的父目录不存在,或者path目录已经存在,都会产生异常
makedirs(path,exist_ok=False) 创建path指定的目录。如果path所在的父目录不存在,则会连同父目录一同创建。如果path目录已经存在,当exist_ok值为False,会产生异常,如果exist_ok值为True,则不会产生异常(默认值为False)
rmdir(path) 删除path指定的空目录,但不会删除父目录。如果path目录不存在,或者该目录不为空,将会产生异常
removedirs(path) 删除path指定的空目录。如果父目录也为空,则会连同父目录一同删除(一直到根目录为止)。如果path不存在,或者该目录不为空,将会产生异常
remove(path) 删除path指定的文件。如果path不是一个文件,或者文件不存在,将会产生异常
getcwd() 返回当前的工作目录,即以脚本运行文件所在的目录
chdir(path) 改变当前的工作目录,工作目录由path指定
rename(src,dst) 重命名一个文件或目录。src指定源文件的路径,dst指定重命名后的文件路径。src与dest要求为同一目录
renames(old,new) 与rename相似,但是old与new指定的目录可以不同(此时类似于移动文件)。在方法执行时,会删除old路径中左侧所有的非空目录,并根据需要,创建new路径中不存在的目录。在Windows系统,old与new必须在同一盘符中
listdir(path) 获取path指定目录下所有的文件与子目录名(包括隐藏文件),以列表形式返回。列表元素的顺序是没有任何保证的。如果path为空,则默认为当前目录
system() 在shell中执行command指定的命令
sep 返回特定操作系统使用的分隔符
name 返回操作系统的名称。在Windows上返回nt,在Linux上返回posix
environ 返回操作系统的环境变量
linesep 返回操作系统使用的换行符
pathsep 返回操作系统环境变量的分隔符
curdir 返回当前目录(.)
pardir 返回上级目录(..)

2.2 os.path模块

os.path模块提供了关于路径操作的相关功能,常用方法如下:

方法名 描述
abspath(path) 返回path的绝对路径
basename(path) 返回path的最后一个部分。即path中操作系统分隔符(/或\等)最后一次出现位置后面的内容。如果path以操作系统分隔符结尾,则返回空字符串
commonpath(paths) 参数paths为路径的序列,返回最长的公共子路径
dirname(path) 返回path的目录部分
exists(path) 判断路径是否存在,存在返回True,否则返回False
getatime(path) 返回文件或目录的最后访问时间
getmtime(path) 返回文件或目录的最后修改时间
getsize(path) 返回文件的大小,以字节为单位
isabs(path) 判断path是否为绝对路径,是返回True,否则返回False
isdir(path) 判断path是否为存在的目录,是返回True,否则返回False
isfile(path) 判断path是否为存在的文件,是返回True,否则返回False
join(path,*paths) 连接所有的path,以当前操作系统的分隔符分隔,并返回。空path(除了最后一个)将会丢弃。如果最后一个path为空,则以分隔符作为结尾。如果其中的一个path为绝对路径,则绝对路径之前的路径都会丢弃,从绝对路径处开始连接
split(path) 将path分割成一个元组,元组含有两个元素,第2个元素为path的最后一个部分,第一个元素为剩余之前的部分。(dirname与basename)

 

2.3 shutil模块

shutil模块提供了高级操作文件的方式。通过该模块提供的功能,可以快捷方便的对文件或目录执行复制,移动等操作。常用方法如下:

方法名 描述
copy(src,dst) 复制文件,返回复制后的文件路径。src指定要复制的文件,如果dst是一个存在的目录,则将文件复制到该目录中,文件名与src指定的文件名一致,否则,将src复制到dst指定的路径中,文件名为dst中指定的文件名
copy2(src,dst) 与copy函数类似,但是copy函数不会保留文件的元信息,例如创建时间,最后修改时间等。copy2函数会尽可能保留文件的元信息
copytree(src,dst) 复制一个目录,目录中的文件与子目录也会递归实现复制,返回复制后的目录路径。src指定要复制的目录,dst指定复制后的目标目录,如果dst已经存在,则会产生异常
rmtree(path) 删除path指定的目录,目录中的子目录与文件也会一并删除
move(src,path) 将文件或目录移动到另外一个位置,src指定文件或目录的路径,当src为目录时,会将该目录下所有的文件与子目录一同移动(递归)。dst指定移动的目标文件或目录

使用copytree(src,dst)复制目录时,可以结合ignore_patterns函数对目录下的文件与子目录进行排除。如:shutil.copytree("abc", "def", ignore=shutil.ignore_patterns("*.txt"))ignore参数指定忽略的文件或目录,这样,所有名称以txt结尾的文件或目录将不会复制到目标目录中。

2.4 glob模块

glob模块提供列举目录下文件的方法,支持通配符*,?与[ ]。常用方法如下:

glob(pathname, *, recursive=False)

返回所有匹配pathname的文件与目录名称构成的列表,列表中元素的顺序是没有规律的。如果recursive值为True,则可以使用**匹配所有文件与目录,包括所有子目录中的文件与目录(递归)。

pathname中可以指定通配符:①*:匹配任意0个或多个字符②?:匹配任意1个字符③[]:匹配[]内的任意一个字符,支持使用“-”区间的表示。例如[0-9]则匹配0到9任何一个字符,[a-f]则匹配a-f之间的任何一个字符

iglob(pathname, *, recursive=False)与glob功能相同,只是返回一个迭代器而不是列表。

 

3 序列化

3.1 csv

CSV(Comma Separated Values),是一种存文本格式的文件,文件中各个元素值通常使用逗号(,)进行分隔,但这不是必须的,扩展名为.csv。可以使用csv模块来操作csv类型的文件。

# scv模块
import csv
# 写入csv文件,在写入的时候最好将newline设置为"",不然会产生空行
with open("E:/test/a.csv","wt",newline="") as f:
    # writer函数返回一个写入器对象,能向参数指定的文件中写入数据
    writer = csv.writer(f)
    writer.writerow(["张三","18","男"])
    writer.writerow(["李四","20","男"])
    # 一次写入多行记录的话提供一个二维列表,每个一维列表就是一条记录
    writer.writerows([["王五","25","男"],["赵六","30","男"]])
# 读取csv文件
with open("E:/test/a.csv","rt") as f:
    # reader函数返回一个读取器对象(是可迭代对象),能够读取csv文件中的数据内容
    reader = csv.reader(f)
    for lines in reader:
        print(lines)

3.2 json

(1)什么是json

JSON(JavaScript Object Notation),是一种轻量级的数据交换格式。json采用的是一组键与值的映射,键与值之间使用“:”进行分隔,而键值对之间使用“,”进行分隔。json文件中的类型可以是:

类型 描述
对象类型 使用{}表示
数组类型 使用[]表示
字符串类型 使用双引号表示
布尔类型 true或false
数值类型 整数与浮点数

格式如下:

{
    "desc": "json",
    "data": {
        "content": ["content1", "content2", "content3"],
        "annotation": "注释"
    }
}

(2)dump与load处理程序,dumps与loads序列化与反序列化

可以通过json类型的数据进行对象的序列化与反序列化。

序列化:将对象类型转换成字符串的形式。

反序列化:将序列化的字符串恢复为对象类型。

通过序列化与反序列化,我们就可以方便的对复杂的对象进行存储与恢复(因为文件读写只支持字符串类型),或者通过网络进行传输,将对象共享给远程的其他程序使用。

# json文件提供不同项目之间的数据交换
import json
data = {
    "desc": "json",
    "data": {
        "content": ["content1", "content2", "content3"],
        "annotation": "注释"
    }
}
with open("E:/test/a.json","wt") as f:
    # 向文件中写入json格式数据,参数1:要写入的数据,参数2:文件对象
    # json写入数据时,默认只显示ascii字符集的字符,非ascii字符集的字符需要转义
    # 指定ensure_ascii=False可以显示,非ascii字符集的字符
    json.dump(data,f,ensure_ascii=False)
with open("E:/test/a.json","rt") as f:
    # 读取json数据,恢复成一个字典
    data = json.load(f)
    print(type(data))
    print(data)
# 通过序列化与反序列化,可以实现不同项目之间的数据交换
# 序列化
d = json.dumps(data,ensure_ascii=False)
print(type(d))
print(d)
# 反序列化
d2 = json.loads(d)
print(type(d2))
print(d2)

Python中的数据类型与json格式的数据类型并非完全相符,因此,在进行转换的时候,可能会进行一些映射处理,如下(json -> Python):

布尔类型(true与false)映射Python中布尔类型(True与False)。

空值类型(null)映射为None。

整数与浮点类型映射为整数(int)与(float)类型。

字符串类型映射为字符串(str)类型。

数组类型([])映射为列表(list)类型。

对象类型(object)映射为字典(dict)类型

# json文件提供不同项目之间的数据交换
import json
data = {
    "布尔类型":False,
    "空值类型":None,
    "整数与浮":2,
    "浮点类型":2.2,
    "字符串类型":"a",
    "数组类型":[1,2,3],
    "对象类型":{"a":1,"b":2}
}
d = json.dumps(data,ensure_ascii=False)
print(d)
d2 = json.loads(d)
print(d2)

(3)自定义类型序列化

json在序列化时,不能序列化我们自定义的类型。如果我们需要自定义的类型也能够序列化,可以定义一个编码类,该类继承json.JSONEncoder,实现类中的default方法,指定序列化的方式,同时,在调用序列化方法时(dump或dumps),使用cls参数指定我们定义的编码类。

# 自定义类型序列化
import json
# 自定义的编码类继承json.JSONEncoder类型
class StudentEncoder(json.JSONEncoder):
    # 实现default方法,给出序列化形式,参数o为序列化对象
    def default(self, o):
        if isinstance(o,Student):
            # 将Student转换成可序列化对象
            # return {"name":o.name,"age":o.age}
            # 返回所有属性构成字典
            return o.__dict__
        else:
            # 不是Student类型,调用父类的default翻翻产生错误信息
            return super().default(0)
class Student:
    def __init__(self,name,age):
        self.name = name
        self.age =age
s = Student("refuel",18)
print(s.__dict__)
# cls参数指定序列化需要用到的编码类
jsonstr = json.dumps(s,cls=StudentEncoder)
print(jsonstr)

 

3.3 pickle

pickle模块也能序列化类型。在序列化自定义类型上,pickle可以比json模块更加方便(不需要定义类似的编码器类)。pickle与json在序列化上的区别如下:

序列化

json

pickle

序列化格式

文本格式,可进行正常查看。

二进制格式,不方便查看。

序列化类型的支持

支持一部分内建的类型,如果需要序列化自定义类型,需要编写编码类。

支持非常广泛的类型,包括自定义类型,不需要编写编码类。

适用广泛性

适用广泛,对于序列化的内容可以用于Python语言之外的程序中进行反序列化。

适用受限,只能用于Python程序中,其他语言的程序无法反序列化。

# pickle模块
import pickle
class Student:
    def __init__(self,name,age):
        self.name = name
        self.age =age
s = Student("refuel",18)
# pickle序列化
with open("E:/test/a.pickle","wb") as f:
    pickle.dump(s,f)
# pickle反序列化
with open("E:/test/a.pickle","rb") as f:
    s2 = pickle.load(f)
print(s2.name)
print(s2.age)

4 上下文管理器

4.1 自定义上下文管理器

with语句跟随的表达式会返回一个上下文管理器,该上下文管理器中定义相关方法,在with开始执行与退出时会调用,也就是说,上下文管理器为with提供一个执行环境。

__enter__(self):with语句体开始执行时,会调用该方法。可以在该方法中执行初始化操作,返回值会赋值给with语句中as后面的变量

__exit__(self, exc_type, exc_val, exc_tb):with语句体执行结束后,会调用该方法。我们在__enter__方法中执行的初始化,就可以在该方法中执行相关的清理,如,文件的关闭,线程锁的释等。实现finally语句同样的功能。exc_type:产生异常的类型;exc_val:产生异常类型的对象;exc_tb:traceback类型的对象,包含了异常产生位置的堆栈调用信息。如果在with语句体中没有产生异常,相关参数为None。

对于with,也可以关联两个上下文管理器,如:

with Manager1() as m1, Manager2 as m2:

    语句

这相当于:

with Manager1() as m1:

    with Manager2() as m2:

        语句
class MyManager:
    def __enter__(self):
        print("进入with语句体")
        # return self
        return "__enter__返回值赋值给as后变量"
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("离线with语句体")
        print(exc_type)
        print(exc_val)
        print(exc_tb)
with MyManager() as f:
    # print(f)
    raise Exception("with中存在异常")

4.2 @contextmanager装饰器

contextlib模块中,定义了@contextmanager装饰器,该装饰器可以用来修饰一个生成器,从而将生成器变成一个上下文管理器,从而可以省略编写完整的上下文管理器类。

在@contextmanager修饰的生成器中,yield之前的语句会在进入with语句体时执行(相当于__enter__方法),而yield之后的语句会在离开with语句体时执行(相当于__eixt__方法)。

with后的表达式会返回生成器对象(假设为gen_obj),进入with语句体时,内部会调用next函数,用来激活生成器对象,进而执行生成器的函数体next(gen_obj)

从而令生成器对象执行。yield产生的值则会赋值给with语句as后的变量(相当于__enter__方法的返回值)。

当with语句体结束时,如果with语句体没有产生异常,则继续调用next,令生成器从之前yield暂停的位置处继续执行(这相当于实现__exit__方法)。如果with语句体产生异常,该异常会在生成器函数体yield的位置抛出。而如果生成器函数体没有处理该异常,将会导致yield之后的语句不会得到执行,这相当于是没有成功的执行__exit__方法。

# @contextManager修饰一个生成器,装饰成上下文管理器
from contextlib import contextmanager
# yield之前的语句会在进入with环境时执行(相当于__enter__方法)
# yield产生的值赋值给as后面的变量(相当与__enter__方法的返回值)
# 离开with环境,执行yield之后的语句(相当于是__exit__方法)
@contextmanager
def f():
    print("进入with环境")
    # 如果with语句体中产生异常,该异常会传播到yield位置处,后面的语句不得到执行,
    # 所以要使用try-finally操作.根据情况捕获,相当于实现了__exit__方法的返回值
    try:
        yield "产生的值赋值给as后面的变量"
    except:
        pass
    finally:
        print("离开with环境")
with f() as f:
    # print(f)
    raise Exception("异常")

发布了76 篇原创文章 · 获赞 9 · 访问量 5471

猜你喜欢

转载自blog.csdn.net/weixin_43786255/article/details/103110166