Day11 Python文件操作详解

文章详细介绍了Python中的os模块,包括环境变量、路径处理、文件夹操作以及子文件操作。接着讲解了os.path模块,用于路径判断和文件属性获取。还涵盖了文件的读写操作,如读取文件、以字节模式读取、写入文件等。此外,文章提到了文件拷贝、with语句的使用以及pickle库在序列化和反序列化中的作用。
摘要由CSDN通过智能技术生成

第八章 Python文件操作

8.1. os模块

8.1.1. os模块介绍

os是OperateSystem的简称,即操作系统。使用os模块,可以实现对操作系统的文件系统进行简单的操作。

在使用之前,需要先导入模块

import os

# 获取当前系统名称
print(os.name)
8.1.2. 环境变量相关
# 获取系统中所有的环境变量
print(os.environ)
# 获取指定的环境变量的值
print(os.environ.get("PATH"))
print(os.getenv("PATH"))
8.1.3. 路径相关
# 获取当前目录的相对路径表示法
print(os.curdir)  # .
# 获取当前目录的绝对路径
print(os.getcwd())
8.1.4. 文件夹操作相关
# 创建文件夹,如果目标已存在,会出异常FileExistsError
# 只支持创建一层文件夹,不支持多级文件夹的创建
os.mkdir("./test")

# 创建多级文件夹
os.mkdir

# 创建多级文件夹,如果目标已存在,会出异常FileExistsError
os.makedirs('./demo/subdemo')

# 移除文件夹,只能移除空文件夹,删除非空的文件夹会出异常
os.rmdir("./demo")

# 移除多级文件夹,只能删除空的文件夹
os.removedirs("./demo/subdemo")

# 移除文件
os.remove("./demo/subdemo")

# 重命名文件/文件夹,可以实现移动的效果
os.rename("./test", "./test2")
8.1.5. 子文件操作
# 获取指定的文件夹下,直接包含的子文件名和子文件夹的名
print(os.listdir("./"))

8.2. os.path模块

8.2.1. os.path模块的介绍

os.path其实是os模块的子模块,其实就是个子包。

在os模块中,我们可以对文件夹进行若干的操作,而os.path模块中包含的更多都是文件、文件夹属性获取的操作。

import os
8.2.2. 路径判断
# 1. 判断路径是否是真实存在的
print(os.path.exists('./test'))
print(os.path.exists('../test'))

# 2. 判断路径表达的是否为文件 [检验的后缀的情况]
print(os.path.isfile('./test/hello.txt'))
print(os.path.isfile('./test/hello'))

# 3. 判断路径表达的是否为文件夹
print(os.path.isdir('./test/hello.txt'))
print(os.path.isdir('./test'))  # True

# 4. 判断路径表达的是否为绝对路径
print(os.path.isabs('./test'))  # absolute  False
print(os.path.isabs(os.getcwd()))  # True

# 5. 获取相对路径对应的绝对路径
print(os.path.abspath('./test'))

# 6. 获取路径中的最后一级路径名
path = r'C:\Users\shawn\Documents\上课资料\语法基础\day18\代码\BaseProject\day18\test\hello.txt'
print(os.path.basename(path))

# 7. 获取路径中包含最后一个的文件夹路径
print(os.path.dirname(path))
# C:\Users\shawn\Documents\上课资料\语法基础\day18\代码\BaseProject\day18\test

# 8. 获取文件的后缀名 结果是一个二元组 如果是文件 第二个元素就是后缀名  文件夹的话就是空字符串【按照后缀切割  路径, 后缀名】
print(os.path.splitext(path))
# ('C:\\Users\\shawn\\Documents\\上课资料\\语法基础\\day18\\代码\\BaseProject\\day18\\test\\hello', '.txt')

print(os.path.splitext(r'C:\Users\shawn\Documents\上课资料\语法基础\day18\代码\BaseProject\day18\test'))
# ('C:\\Users\\shawn\\Documents\\上课资料\\语法基础\\day18\\代码\\BaseProject\\day18\\test', '')

# 9. 路径拼接
super_path = r'C:\\Users\\shawn\\Documents\\上课资料\\语法基础\\day18\\代码\\BaseProject\\day18\\test'
print(os.path.join(super_path, 'hello.txt'))
# C:\\Users\\shawn\\Documents\\上课资料\\语法基础\\day18\\代码\\BaseProject\\day18\\test\hello.txt
8.2.3. 文件属性获取
# 1. 获取文件的字节大小
print(os.path.getsize('./1.OS模块.py'))

# 2. 获取文件的创建时间
print(os.path.getctime('./1.OS模块.py'))
import datetime
print(datetime.datetime.fromtimestamp(1661916953.9453664))

# 3. 获取文件的修改时间
print(os.path.getmtime('./1.OS模块.py'))
print(datetime.datetime.fromtimestamp(1661917233.2336862))

# 4. 获取文件的访问时间
print(os.path.getatime('./1.OS模块.py'))
print(datetime.datetime.fromtimestamp(1661930617.1276166))
8.2.4. 练习
获取指定目录下的所有的文件
import os

def get_all_file(dir_path):
	file_names = os.listdir(dir_path)
	for fn in file_names:
		file_path = os.path.join(dir_path, fn)
		if os.path.isfile(file_path):
			print(file_path)
		elif os.path.isdir(file_path):
			get_all_file(file_path)

get_all_file('./test')


'''
./test
|- demo   		hello.txt
	 |- ./test/demo
	      |- 1.txt			dir
	    					 |- 2.txt
'''
import os


def print_all_files(path, level):
    """
    需求:获取一个路径下的所有的子文件
    :param path: 需要查询文件的路径
    :return:
    """
    prefix = "|" + "----|" * level
    print(prefix, end=os.path.basename(path))
    print()
    # 1. 获取到所有的子文件
    sub = os.listdir(path)
    # 2. 遍历每一个文件
    for f in sub:
        # 3. 拼接路径
        full_path = os.path.join(path, f)
        # 4. 判断
        if os.path.isfile(full_path):
            print(prefix, end=f)
            print()
        else:
            print_all_files(full_path, level + 1)


print_all_files("/Users/shawn/PycharmProjects/pythonProject", 0)
删除指定的文件夹
import os

def remove_path(dir_path):
	file_names = os.listdir(dir_path)
	for fn in file_names:
		file_path = os.path.join(dir_path, fn)
		if os.path.isfile(file_path):
			os.remove(file_path)
		elif os.path.isdir(file_path):
			remove_path(file_path)
	os.rmdir(dir_path)


remove_path('./test1')

8.3. 读写文件

使用**open()**方法打开一个文件,建立一个程序与文件的数据流。在打开文件的时候需要设置打开的模式,常见的打开模式如下:

打开模式描述文件指针异常
r只读模式打开,默认开头如果文件不存在,会出现异常
r+读写模式打开开头如果文件不存在,会出现异常
w只写模式打开,打开文件会清空原有内容开头/结尾
w+读写模式打开,打开文件会清空原有内容开头/结尾
a只写模式打开结尾
a+读写模式打开结尾
b二进制数据流,可以与上述的混合使用
8.3.1. 读取文件的基本操作
from collections.abc import Iterator, Iterable

# 读取文件中的内容

# 打开指定的文件,设置打开模式为r,表示读取;设置encoding字符集,使用utf8字符集打开文件;获取到一个数据流
read_stream = open("./1. OS模块.py", "r", encoding="utf8")

# 打开文件后,建立数据通道,每一行数据就已经存在于一个迭代器容器中。
print(isinstance(read_stream, Iterator))
print(isinstance(read_stream, Iterable))

# 遍历,获取到文件中的每一行数据
# for line_data in read_stream:
#     print(line_data, end="")

# 还可以将文件中的数据当作一个字符串全部读取
# file_data = read_stream.read()
# print(file_data)

# 读取指定数量的字符
# data = read_stream.read(30)
# print(data)

# 读取一行数据
print(read_stream.readline(), end="")
print(read_stream.readline(), end="")
print(read_stream.readline(), end="")
print(read_stream.readline(), end="")

# 读取指定行的数据
# 参数传递的是一个字符的数量,返回的是这些字符所在的行数据!
print(read_stream.readlines(34), end="")

read_stream.close()
8.3.2. 循环读取文件
'''
内存是2G
文件内容是4G

内容是迭代器  读出来之后迭代器中就没有了
分批读取 肯定是将读的操作进行重复  重复到什么情况下不用再重复了 [read的内容是空字符串]

分批读取 怎么分批 一般是按照计算单位的换算来走的  通常是1024的倍数
'''
read_stream = open('./os的操作_08.py', 'r', encoding='utf-8')
while True:
	data = read_stream.read(1024)
	print(data)
	if data == '':
		break
8.3.3. 以字节模式读取
read_stream = open('./os的操作_08.py', 'rb')
# for ele in read_stream:
# 	print(ele)

# data = read_stream.read(18)  # 字节数
# print(data)
# print(data.decode('utf-8'))
print(read_stream.readlines())
read_stream.close()

# import os
# os.remove('./os的操作_08.py')
8.3.4. 写模式的基本操作
# 和文件之间建立通道
# w: 写模式
# a: 向后追加,如果没有设置追加模式,则默认会用新的内容覆盖掉之前的内容
write_stream = open("./相思.txt", "w", encoding="utf8")

# 写操作,一次写
write_stream.write("红豆生南国,春来发几支?\n")
# 同时写多个
write_stream.writelines(["愿君多采撷, ", "此物最相思\n"])

write_stream.close()
8.3.4. 以字节模式写入
# 和文件之间建立通道
# w: 写模式
# a: 向后追加,如果没有设置追加模式,则默认会用新的内容覆盖掉之前的内容
# b: binary,字节模式
write_stream = open("./相思.txt", "wb")

# 写操作,一次写
write_stream.write("红豆生南国,春来发几支?\n".encode("utf8"))
# 同时写多个
write_stream.writelines(map(lambda ele: ele.encode("utf8"), ["愿君多采撷, ", "此物最相思\n"]))

write_stream.close()

8.4. 拷贝文件

# 其实拷贝文件的思路非常简单,就是读取源文件,将读取到的内容原封不动的写入到目标文件即可
def copy_file(src_file, dst_file):
    """
    拷贝文件
    :param src_file: 源文件
    :param dst_file: 目标文件
    :return:
    """
    # 1. 以字节模式打开源文件,创建数据流
    read_stream = open(src_file, "rb")
    # 2. 以字节模式打开目标文件,创建数据流
    write_stream = open(dst_file, "wb")
    # 3. 循环读取源文件的内容,往目标文件写
    while len(read_data := read_stream.read(1024)) != 0:
        write_stream.write(read_data)
        write_stream.flush()


copy_file("./1. OS模块.py", "./1. OS模块.py.copy")

8.5. with语句

8.5.1. 为什么要使用with

无论是在读取文件,还是在写文件的时候,我们需要先使用open语句,建立一个程序与文件的连接,获取到数据流。然后对文件进行读、写的操作。操作之后需要将这个连接断掉,释放资源。

# 1. 建立程序与文件的连接
read_data = open("./test.py", "r", encoding="utf8")
# 2. 读取数据
read_data.readline()
# 3. 释放资源
read_data.close()

在实际的操作过程中,有可能会出现资源没有释放,大概两种情况会导致:

  • 自己忘了写close了
  • 读取数据过程中出现异常,导致后面的close没有执行

第一个我们可以通过更加细心来解决,而第二个我们可以通过try语句来解决:

# 1. 建立程序与文件的连接
read_data = open("./test.py", "r", encoding="utf8")
try:
    # 2. 读取数据
    read_data.readline()
finally:
    # 3. 释放资源
    read_data.close()

问题的确解决了,但是这样的代码看着就非常麻烦了。那么能不能简化这个过程呢?with就出现了,我们可以将上述的语句修改如下:

with open("./test.py", "r", encoding="utf8") as read_data:
    read_data.readline()
8.5.2. with语句是什么

with的基本语法是:

with 对象获取方式 as 对象:
    对象的操作

可以在with后面获取到一个对象,然后在with的代码段中使用这个对象。但是这个对象并不是随便都可以的,需要实现两个魔术方法:

  • _enter_: 通过with获取对象的时候调用,返回一个对象
  • _exit_: 代码段执行结束,或者异常结束的时候会执行
class Demo:
    def __enter__(self):
        print("Demo Enter")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Demo Exit")
        print(f"{exc_type = }")
        print(f"{exc_val = }")
        print(f"{exc_tb = }")

    def test_exception(self):
        print(10 / 0)


with Demo() as d:
    d.test_exception()
    print("aaa")
8.5.3. 文件操作中的with

在进行文件读写的时候,open返回的是_io.TextIOWrapper对象,而在这个对象中已经实现了__enter__和__exit__,并且在__exit__中完成了对数据流的关闭操作。因此,我们在进行文件操作的时候,就可以直接简化如下:

# 使用with操作文件,完成文件拷贝
def copy_file(src, dst):
    with open(src, "rb") as read_data, open(dst, "wb") as write_data:
        while len(data := read_data.read(1024)) != 0:
            write_data.write(data)
            write_data.flush()


copy_file("./相思.txt", "./相思2.txt")

8.6. pickle库

程序在运行的过程中,会在内存中存储很多的数据。这些数据会随着程序的结束而被释放掉。那如果我们希望下一次程序启动起来之后,依然能够使用到这些内存中的数据该怎么办呢?我们就需要将内存中的数据以文件的形式保存下来,下一次程序启动的时候,可以直接读取这个文件中的数据,加载到内存中继续使用。

在这里需要涉及到两个概念:

  • **序列化:**就是将内存中的对象,转成字节序列的形式。然后可以将这些字节序列保存到文件中,或者传递给网络中的其他的机器。
  • **反序列化:**就是通过一个字节序列,将其中的数据读取到内存中。

而pickle库,就是来实现序列化和反序列化的操作的!

import pickle


# pickle库是一个用来将内存中的数据转成字节序列,存储到文件中的库,通常以.pkl为后缀
# 序列化:将内存中的数据,转成字节序列
# 反序列化:将字节序列的数据解析成内存中的数据

# 序列化:dump
# 反序列化:load

with open("./file/data.pkl", "wb") as write_stream:
    pickle.dump(10, write_stream)
    pickle.dump("10", write_stream)
    pickle.dump(b"10", write_stream)
    pickle.dump([10, 20], write_stream)
    pickle.dump({"key": "value"}, write_stream)


with open("./file/data.pkl", "rb") as read_stream:
    value1 = pickle.load(read_stream)
    value2 = pickle.load(read_stream)
    value3 = pickle.load(read_stream)
    value4 = pickle.load(read_stream)
    value5 = pickle.load(read_stream)
    print(type(value1), value1)
    print(type(value2), value2)
    print(type(value3), value3)
    print(type(value4), value4)
    print(type(value5), value5)

注意事项:

在使用dump和load进行序列化和反序列化的时候,需要保证dump的次数和load的次数、顺序是相同的。

使用dump在序列化对象的时候,操作的个数是不确定的,会导致load的次数也就不确定了。

  • 如果load次数少了,会导致数据没有完全读取。
  • 如果load次数多了,会出现异常

那么如何解决这样的问题呢?

最简单也是最常见的解决方式就是:将需要序列化的所有的对象存储到一个数据容器中,将这个数据容器序列化就可以了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据东哥(Aidon)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值