文件操作
文件操作概述
概述:
我们所熟知的操作系统, 例如: Windows, MacOS, Linux 都是文件操作系统, 它们都是通过 文件 来管理数据的.
文件的基本操作步骤:
1. 打开文件. 2. 读取数据 或者 写入数据 或者 追加数据. 3. 关闭文件.
文件操作涉及到的函数:
open(文件路径, 模式, 码表)
参1:文件路径
可以写绝对路径, 也可以写相对路径. 绝对路径: 固定的, 写死的路径, 以盘符开头. d:/data/1.txt 相对路径: 一般是相对于当前项目的路径来讲的. 例如:
1.txt 这是一个相对路径, 它对应的绝对路径为: 当前项目的路径 + 1.txt, 具体如下: D:\workspace\pythonProject\day07\1.txt
参2: 模式
r: 只读, 默认模式, 可以读取 字符. '你好' rb: 只读, 以二进制形式读取数据, 例如: 图片, 音频, 视频, 文本... w: 只写(覆盖写入), 可以操作 字符. wb: 只写(覆盖写入), 操作的是 字节. a: 追加写入, 可以操作 字符. ab: 追加写入, 操作的是 字节.
上述的模式中, r, w, a 都是可以操作字符的, 即: 可以结合码表 encoding 一起使用.
上述的模式中, rb, wb, ab都是操作二进制的, 所以不能结合 encoding参数 一起使用.
码表 就是描述 字符 和 整数之间的关系的, 例如: ASCII, GBK, utf-8...
在gbk码表中, 1个中文占2个字节, 在utf-8码表中, 1个中文占3个字节.数字, 字母, 特殊符号无论在什么码表中, 都只占 1个字节.
参3: 码表
码表 = 字符 + 其对应的int类型的整数, 例如: 'a' => 97, 'A' => 65, '0' => 48
读或者写
read()
readline()
readlines()
write()
writelines()
close()关闭
close() 关闭文件, 释放资源.
文件的基本操作
文件读
所用函数
read(num)
一次读取num个字节的数据, 不写num, 则一次性读取全部数据. 文件不存在就报错.
readline()
一次读取一行数据, 并封装成字符串, 然后返回.
readlines()
一次性读取所有的行, 并把每行数据封装成1个字符串, 然后整体放到1个列表中, 即: [第一行数据, 第二行数据...]
演示读(read())
# 1. 打开文件. # 相对路径. f = open('1.txt', 'r') # 绝对路径 # 写法1: r'' 取消\的特殊含义 f = open(r'D:\workspace\ai_30_basic_bj\pythonProject\day07\1.txt', 'r') # 写法2: \\ => \ f = open('D:\\workspace\\ai_30_basic_bj\\pythonProject\\day07\\1.txt', 'r') # 写法3: 直接写 1个 / f = open('D:/workspace/ai_30_basic_bj/pythonProject/day07/1.txt', 'r') # 如果数据源文件不存在, 就会报错. # f = open('2.txt', 'r') # ./代表当前项目的路径, 可以省略不写. f = open('./data/2.txt', 'r') f = open('data/2.txt', 'r') # 2. 读取文件. data = f.read() # 不写长度, 一次性读取所有的内容. data = f.read(2) # 写长度, 则一次读取 2个字节. data = f.read(3) # 写长度, 则一次读取 3个字节. data = f.read(10) # 写长度, 则一次读取 10个字节, 如果文件内容不足10个字节, 则读取全部内容. print(data) # 文件数据已经读取完毕, 如果继续读取, 则: 返回 '' data2 = f.read(3) print(data2) # 3. 关闭文件. f.close()
演示按行读取
# 1. 打开文件. f = open('1.txt', 'r') # 2. 读取文件数据. # 方式1: readline(), 一次读取一行, 分解版写法. line1 = f.readline() # 'abc\n' line2 = f.readline() # 'def\n' line3 = f.readline() # 'g\n' line4 = f.readline() # '' print(f'line1: {line1}', end='') print(f'line2: {line2}', end='') print(f'line3: {line3}', end='') print(f'line4: {line4}', end='') # 方式2: readline(), 一次读取一行, 合并版写法. while True: # 一次读取一行数据. line = f.readline() # 判断读取到的数据是否为空, 如果为空, 说明文件内容读取完毕, 结束循环即可. # if len(line) == 0: # if not line: # 0, None, '' => False, 其它 => True # if bool(line) == False: # 0, None, '' => False, 其它 => True if line == '': break # 走到这里, 说明读取到数据了. print(f'line: {line}', end='') # 方式3: readlines(): 一次读取所有行, 每行封装成字符串, 然后整体封装成列表. lines = f.readlines() print(lines) # ['abc\n', 'def\n', 'g\n'] print(type(lines)) # <class 'list'> # 3. 关闭文件. f.close()
数据写入文件
涉及到的函数
写数据到文件中, 涉及到的函数如下:
write(数据)
一次往文件中写入指定的内容, 不能是列表, 元组等多个值...
writelines(容器类型)
一次往文件中写入多个值, 必须是: 容器类型.
文件写特性
从打开 到 关闭, 这是一个完整的动作, 其内部是不会不断覆盖的.
以后我们用 read(), readline() + write() 这种方式较多.而 readlines() + writelines() 这种方式相对较少, 它比较适合 小文件的操作.
细节
中文在gbk码表(针对于国内)中占 2 个字节, 在utf-8码表(针对于国际, 也叫: 万国码, 统一码)中占 3个字节.
英文字母, 数字, 特殊符号无论在什么码表中, 都只占 1个字节.
码表 = 字符 + 字符对应的整数, 码表就是描述 字符 及其 对应的整数之间的 关系的.
演示写(write())
# 1. 打开文件. f = open('1.txt', 'r') # 默认的码表是: gbk码表. f = open('1.txt', 'r', encoding='gbk') # 效果同上. # 码表名可以写 utf-8 或者 utf8, 建议写: utf-8 f = open('1.txt', 'r', encoding='utf8') # rb: 以 二进制形式 读取文件, 报错, 二进制方式(无论读写), 不能结合 码表(encoding参数)一起用. f = open('1.txt', 'rb', encoding='utf8') # 正确的 二进制 写法. f = open('1.txt', 'rb') # 2. 读取文件数据. lines = f.readlines() print(lines) # 3. 关闭文件. f.close() # 解析二进制数据. s1 = b'\xe5\xa5\xbd\xe5\xa5\xbd\xe5\xad\xa6\xe4\xb9\xa0,\r\n' s2 = s1.decode(encoding='utf-8') print(s2)
演示写多行
字符写入
# 场景1: 字符方式 覆盖写入, 或者追加写入, 即: w, a 模式演示. # 1. 打开文件. # 只读模式, 如果文件不存在, 就报错. f = open('2.txt', 'r') # 覆盖写入模式 或者 追加写入模式, 如果目的地文件不存在(前提: 父目录存在), 则会自动创建. f = open('2.txt', 'w') f = open('2.txt', 'a') # 父目录存在 f = open('./data/1.txt', 'w') # 覆盖写入. f = open('./data/1.txt', 'a') # 追加写入. f = open('./data/1.txt', 'w', encoding='utf-8') # 覆盖写入, 支持中文. # 父目录不存在的情况. f = open('aa/bb/cc/2.txt', 'w') # FileNotFoundError, 文件不存在. # 2. 往文件中写数据. f.write('abc\n') f.write('def\n') f.write('g\n') f.write('好好学习!\n') # 如果要写中文, 记得加上 encoding='utf-8' # 3. 关闭文件. f.close()
字节写入
# 场景2: 字节方式 覆盖写入, 或者追加写入, 即: wb, ab 模式演示. # 1. 打开文件. f = open('./data/1.txt', 'wb') # 覆盖写入, 二进制形式 f = open('./data/1.txt', 'ab') # 追加写入, 二进制形式 # 2. 往文件中写数据. f.write(b'abc\n') f.write(b'def\n') f.write(b'g\n') f.write(b'\xe5\xa5\xbd\xe5\xa5\xbd\xe5\xad\xa6\xe4\xb9\xa0!\n') # 如果要写中文, 记得加上 encoding='utf-8' # 3. 关闭文件. f.close() # 场景3: writelines() 一次写多行. f = open('data/1.txt', 'w', encoding='utf-8') # writelines() 要的是容器类型. f.writelines(['故人西辞富士康, \n', '为学技术到蓝翔!\n', '蓝翔毕业包分配,\n', '居然还是富士康!\n']) f.writelines(('故人西辞富士康, \n', '为学技术到蓝翔!\n', '蓝翔毕业包分配,\n', '居然还是富士康!\n')) f.writelines('abc') # 如果操作字典, 则只写入: 键. f.writelines({'name': '张三', 'age': 23, 'gender': '男'}) # 报错: writelines()接收的是: 容器类型. f.writelines(10) f.close()
码表转换
概述
编码: 把我们看得懂的数据 => 我们看不懂的数据, 这个动作叫: 编码.解码: 把我们看不懂的数据 => 我们看得懂的数据, 这个动作叫: 解码.
细节
-
中文 => gbk(2个字节), UTF-8(3个字节), 数字,字母,特殊符号 => 无论什么码表, 都只占 1个 字节.
-
只要出现乱码问题了, 原因只有1个: 编解码不一致.
-
编解码涉及到的函数:encode(): 编码.decode(): 解码.
-
二进制的特殊写法, b'内容', 只针对于 数字, 字母, 特殊符号有效, 针对于 中文 无效.
代码演示
编码
# 1. 演示 编码. # s1 = 'abcXYZ123!@#' s1 = 'aX1!你好' # 具体的编码动作. bys1 = s1.encode() # 默认码表(utf-8). bys => bytes, 字节列表. bys2 = s1.encode(encoding='gbk') # 指定码表 => gbk bys3 = s1.encode(encoding='utf-8') # 指定码表 => utf-8 print(f'bys1: {bys1}') # b'aX1!\xe4\xbd\xa0\xe5\xa5\xbd' print(f'bys2: {bys2}') # b'aX1!\xc4\xe3\xba\xc3' print(f'bys3: {bys3}') # b'aX1!\xe4\xbd\xa0\xe5\xa5\xbd' # 打印类型 print(type(bys1)) # <class 'bytes'> print(type(bys2)) # <class 'bytes'> print(type(bys3)) # <class 'bytes'> print('-' * 30)
解码
# 2. 演示 解码 s2 = bys1.decode() # 默认码表(utf-8). s3 = bys1.decode(encoding='gbk') # 指定gbk码表 s4 = bys1.decode(encoding='utf-8') # 指定utf-8码表 print(f's2: {s2}') print(f's3: {s3}') # 乱码, 因为: 编解码不一致. print(f's4: {s4}') print(type(s2)) print(type(s3)) print(type(s4)) print('-' * 30) # 3. 演示 二进制的特殊写法. bys4 = b'abcXYZ123!@#' # bys4 = b'abcXYZ123!@#你好' # 报错, b'内容' 只针对于 数字, 字母, 特殊符号有效, 针对于 中文 无效. print(f'bys4: {bys4}') # b'abc' print(type(bys4)) # <class 'bytes'>
文件拷贝与备份
拷贝
需求1: 把 data目录下的1.txt => data目录下的2.txt文件中.
# 1. 封装数据源文件, 获取: 文件对象. src_f = open('data/1.txt', 'r', encoding='utf-8') # 2. 封装目的地文件, 获取: 文件对象. dest_f = open('data/2.txt', 'w', encoding='utf-8') # 3. 具体的拷贝动作. # 思路1: 一次读取所有的文件数据, 然后一次性写到目的地文件中. data = src_f.read() dest_f.write(data) # 思路2: 一次读取所有的行, 然后一次性写到目的地文件中. lines = src_f.readlines() dest_f.writelines(lines) # 思路3: 循环读取, 一次读取指定数量的数据, 然后写出到目的地文件中. while True: # 一次读取指定数量的数据, 一般是: 1024 的整数倍. # bit(比特位), byte, kb, mb, gb, tb, pb, eb, zb, yb, bb, nb, db data = src_f.read(8192) # 一次读取 8KB # 判断读取到的数据是否为空, 如果为空, 说明文件读完了, 结束循环即可. if not data: # '' => False break # 走这里, 说明读取到数据了, 就写到目的地文件中. dest_f.write(data) # 4. 释放资源. src_f.close() dest_f.close()
需求2: 把 data目录下的a.jpg => data目录下的b.jpg文件中.
# 1. 封装数据源文件, 获取: 文件对象. src_f = open('data/a.jpg', 'rb') # src_f = open('data/1.txt', 'rb') # 2. 封装目的地文件, 获取: 文件对象. dest_f = open('data/b.jpg', 'wb') # dest_f = open('data/2.txt', 'wb') # 3. 具体的拷贝动作. # 思路3: 循环读取, 一次读取指定数量的数据, 然后写出到目的地文件中. while True: # 一次读取指定数量的数据, 一般是: 1024 的整数倍. # bit(比特位), byte, kb, mb, gb, tb, pb, eb, zb, yb, bb, nb, db data = src_f.read(8192) # 一次读取 8KB # 判断读取到的数据是否为空, 如果为空, 说明文件读完了, 结束循环即可. if not data: # '' => False break # 走这里, 说明读取到数据了, 就写到目的地文件中. dest_f.write(data) # 4. 释放资源. src_f.close() dest_f.close()
备份
需求: 提示用户录入当前目录下任意文件名, 完成该文件的备份功能.例如: 用户录入: 1.txt 备份文件名: 1[备份].txt
分解版
# 方式1: 分解版写法. # 1. 提示用户录入文件名. file_name = input("请输入文件名: ") # 2. 判断用户录入的文件名是否合法. idx = file_name.rfind(".") # 找.的最后一次出现的位置. if idx <= 0: # abc => -1, .txt => 0 print('文件名不合法, 程序结束!') else: # 3. 走到这里, 说明文件名合法(但是文件存不存在还需你额外校验, 函数还没学, 暂不校验), 就拼接: 目的地文件名. dest_file_name = file_name[:idx] + '[备份]' + file_name[idx:] # print(dest_file_name) # 4. 具体的拷贝动作. src_f = open(file_name, 'rb') dest_f = open(dest_file_name, 'wb') while True: data = src_f.read(8192) if not data: break # 走这里, 说明文件内容读取完毕. # 走这里, 说明有文件, 就写出 dest_f.write(data) # 5. 释放资源 src_f.close() dest_f.close() # 6. 提示用户. print('拷贝成功!')
函数版
# 方式2: 函数版. 抽取函数的快捷键 => ctrl + alt + m def copy_file(src_file_name, dest_file_name): """ 自定义函数, 完成: 文件备份 :param src_file_name: 数据源文件名 :param dest_file_name: 目的地文件名 :return: 无 """ # 1. 封装数据源 和 目的地文件. src_f = open(src_file_name, 'rb') dest_f = open(dest_file_name, 'wb') # 2. 具体的拷贝动作 while True: data = src_f.read(8192) if not data: break # 走这里, 说明文件内容读取完毕. # 走这里, 说明有文件, 就写出 dest_f.write(data) # 3. 释放资源 src_f.close() dest_f.close() # 测试上述的代码 if __name__ == '__main__': # 1. 提示用户录入文件名. file_name = input("请输入文件名: ") # 2. 判断用户录入的文件名是否合法. idx = file_name.rfind(".") # 找.的最后一次出现的位置. if idx <= 0: # abc => -1, .txt => 0 print('文件名不合法, 程序结束!') else: # 3. 走到这里, 说明文件名合法(但是文件存不存在还需你额外校验, 函数还没学, 暂不校验), 就拼接: 目的地文件名. dest_file_name = file_name[:idx] + '[备份]' + file_name[idx:] # print(dest_file_name) # 4. 具体的拷贝动作. copy_file(file_name, dest_file_name) # 6. 提示用户. print('拷贝成功!')
with...open
概述
with open(): 它是用来简化 文件操作的, 可以帮助我们自动释放资源.
格式
with open(文件路径, 模式, 码表) as 别名: 正常读文件数据 或者 写数据到文件
细节
-
with open()语法 会在其内部的代码执行完毕后, 自动释放资源, 无需手动释放.
-
with open()语法的本质是 一个上下文管理器对象, 这个到就业班我们会详细讲解.
演示
# 回顾: 读取文件数据. src_f = open('1.txt', 'r', encoding='utf-8') data = src_f.read() print(data) # 很多时候, 初学者容易遗忘关闭文件, 造成资源浪费, 泄露的情况. src_f.close() # 改造, 用: with open()语法来优化它. with open('1.txt', 'r', encoding='utf-8') as src_f: # 正常的逻辑代码, 这里的代码执行完毕后, 会自动释放资源. data = src_f.read() print(data) # 优化之前讲解的, 拷贝文件的代码. # 需求2: 把 data目录下的a.jpg => data目录下的b.jpg文件中. # 1. 封装数据源文件, 获取: 文件对象. src_f = open('data/a.jpg', 'rb') # 2. 封装目的地文件, 获取: 文件对象. dest_f = open('data/b.jpg', 'wb') # 3. 具体的拷贝动作. # 思路3: 循环读取, 一次读取指定数量的数据, 然后写出到目的地文件中. while True: # 一次读取指定数量的数据, 一般是: 1024 的整数倍. data = src_f.read(8192) # 一次读取 8KB # 判断读取到的数据是否为空, 如果为空, 说明文件读完了, 结束循环即可. if not data: # '' => False break # 走这里, 说明读取到数据了, 就写到目的地文件中. dest_f.write(data) # 4. 释放资源. src_f.close() dest_f.close() # 用with open() 优化上述的代码. # 1. 封装数据源文件, 目的地文件, 获取: 文件对象. with open('data/a.jpg', 'rb') as src_f, open('data/b.jpg', 'wb') as dest_f: # 2. 具体的拷贝动作. while True: # 一次读取指定数量的数据, 一般是: 1024 的整数倍. data = src_f.read(8192) # 一次读取 8KB # 判断读取到的数据是否为空, 如果为空, 说明文件读完了, 结束循环即可. if not data: # '' => False break # 走这里, 说明读取到数据了, 就写到目的地文件中. dest_f.write(data)
os
概述
全称叫Operating System, 也叫: 系统模块, 主要是操作 文件, 文件夹, 路径等的.
常用函数
getcwd() 获取当前工作目录, 即: 你写的相对路径, 都是相对于这个路径来讲的. chdir() 改变工作路径, 即: 相对路径以后就是相对于这个路径来讲的.listdir() 查看当前目录下, 所有的子级(不包括子级的子级)mkdir() 创建目录的, 如果存在就报错, 不存在就创建.rename() 修改文件的名字.remove() 删除指定的文件.
演示
# 导包 import os # 演示os模块常用的函数. # 1. 获取当前工作目录, 即: 你写的相对路径, 都是相对于这个路径来讲的. print(os.getcwd()) # D:\workspace\ai_30_basic_bj\pythonProject\day07 # 2. 设置当前的工作空间为指定的目录. os.chdir('d:\\data\\') os.chdir('d:/data/') # 重新查看当前的工作空间. print(os.getcwd()) # 读取文件内容. f = open('1.txt', 'r', encoding='utf-8') print(f.read()) f.close() # 3. 查看当前目录下所有的文件(不包括子级的子级) print(os.listdir('./')) # 获取当前目录下所有的文件(不包括子级的子集) # 4. 在指定目录下创建 子目录. os.mkdir('./data/ai30') # make directory: 制作目录, 该目录必须:不存在. # 5. 改名. os.rename('data/ai30', 'data/ai30_new') # 6. 删除文件的. os.remove('data/ai30_new/a.jpg') print('-' * 30)
扩展shutil模块
概述
它里边的函数, 大多数是和 文件(文件夹)相关的, 且大多数的函数 底层都支持递归.例如: copyfile() 就可以实现 拷贝文件.
演示
# shutil模块的 copyfile()函数可以实现: 拷贝文件. shutil.copyfile('data/1.txt', 'data/2.txt') shutil.copyfile('data/a.jpg', 'data/b.jpg')标签:文件,读取,python,open,day06,入门篇,print,txt,data From: https://blog.csdn.net/m0_60916732/article/details/141304044