字符编码
1 字符在内存与硬盘中的编码对应关系
内存固定使用unicode,我们可以改变的是存入硬盘采用格式
英文+汉字-》unicode-》gbk
英文+日文-》unicode-》shift-jis
万国字符》-unicode-》utf-8
2 文本文件存取乱码问题
文本文件存取乱码问题
存乱了:解决方法是,文本编辑器的编码格式应该设置成支持文件内字符串的格式
取乱了:解决方法是,文件是以什么编码格式存如硬盘的,就应该以什么编码格式读入内存,在文本编辑器中设置编码格式和存盘格式一致
3 解决Python解释器读文件时不乱码
Python解释器默认读文件的编码
Python3默认:utf-8
Python2默认:ASCIIython2默认:ASCII
指定文件头修改默认的编码:
在py文件的首行写:
# coding:gbk
通过文件头来告诉Python解释器用哪种编码来读取文件
保证运行Python程序前两个阶段不乱码的核心法则:
指定文件头
# coding:文件当初存入硬盘时所采用的编码格式
文件是哪种编码格式存到硬盘的, Python解释器就该用哪种编码格式去读取
读取格式可以在文件中通过文件头来指定, 而存到硬盘的格式是取决于文本编辑器的设置
4 Python解释器识别语法时如何不乱码
Python3的str类型在内存默认直接存成unicode格式,无论如何都不会乱码
保证Python2的str类型不乱码
x=u'上' # 强制Python2将字符串类型存成unicode, Python2的中文字符串要加上u'字符串'
Python2解释器有两种字符串类型:str、unicode
# str类型
x='上' # 字符串值会按照文件头指定的编码格式存入变量值的内存空间
# unicode类型
x=u'上' # 强制存成unicode
文件操作部分
1、什么是文件
文件是操作系统提供给用户/应用程序操作硬盘的一种虚拟的概念/接口
用户/应用程序(open())
操作系统(文件)
计算机硬件(硬盘)
2、为何要用文件
用户/应用程序可以通过文件将数据永久保存的硬盘中
即操作文件就是操作硬盘
用户/应用程序直接操作的是文件,对文件进行的所有的操作,都是
在向操作系统发送系统调用,然后再由操作将其转换成具体的硬盘操作
3、open()模式介绍
3.1 控制文件读写内容的模式:t和b
注意: t和b不能单独使用,必须跟r/w/a连用
t文本(默认的模式, 如果指定了某种读写操作模式, 但没有指定读写内容模式, 默认就是t模式)
1. 读写都以str(unicode)为单位的.
2. 文本文件, 只有文本文件才涉及字符编码, 因此, open()的数据来源必须是文件.
3. 因为Python3中, 数据在内存是存成unicode格式, 必须指定encoding='utf-8', 把内存中的str(unicode), 编码成utf-8, 存到硬盘.
b二进制/bytes
3.2 控制文件读写操作的模式
r只读模式, 默认的操作模式
w只写模式
a只追加写模式
+:r+、w+、a+
4 文件操作基本流程
4.1 打开文件
# windows路径分隔符问题, \a,\n等在Python中都是特殊的转义字符, 如果在文件路径中存在会被当做转义
open('C:\a.txt\nb\c\d.txt')
# 解决方案一:推荐, 在文件路径前加r(rawstring), 表示使用原生字符串, 不会转义
open(r'C:\a.txt\nb\c\d.txt')
# 解决方案二: 用'/'表示路径分隔符, open()功能会自动识别转换
open('C:/a.txt/nb/c/d.txt')
f=open(r'C:\Users\David\PycharmProjects\pythonProject\s14\day11\练习.py',mode='rt')
# f的值是一种变量,占用的是Python应用程序的内存空间
print(f)
>> <_io.textiowrapper name="C:\\Users\\David\\PycharmProjects\\pythonProject\\s14\\day11\\练习.py" mode="rt" encoding="cp1252">
open()除了创建一个变量值占用Python解释器的内存空间,
还会向操作系统请求打开文件, 而操作系统会将打开的文件映射到硬盘空间.
所以, 通过应用程序对文件进行操作时, 先是由应用程序向操作系统发起读或写请求,
再由操作系统将请求转换为磁盘上的操作.
站在变量角度, f=open(), open()的返回值赋予给f, 会占用应用程序的内存
站在文件角度, f=open()会打开文件, 打开的文件是由操作系统维护, 因此占用操作系统的内存
x=int(10) # 并不涉及文件的概念, 只是纯内存的操作
4.2 操作文件:
读/写文件,应用程序对文件的读写请求都是在向操作系统发送系统调用,然后由操作系统控制硬盘把输入读入内存、或者写入硬盘
f=open(r'C:\Users\David\PycharmProjects\pythonProject\s14\day11\练习.py',mode='rt', encoding='utf-8')
# open()一个文件, 会把文件对象赋值给f,
res=f.read() # f.read()会通过文件对象, 把内容从硬盘读到文件
print(type(res))
>>
print(res)
>>
f=open(r'C:\Users\David\PycharmProjects\pythonProject\s14\day11\练习.py',mode='rt', encoding='utf-8')
res=f.read() # f.read()就是把变量f赋的文件内容从硬盘读出来到内存, 赋值给res
print(res)
4.3 关闭文件
f=open(r'C:\Users\David\PycharmProjects\pythonProject\s14\day11\练习.py',mode='rt', encoding='utf-8')
res=f.read()
f.close() # 回收操作系统资源, 因为操作系统能同时打开的文件数是有限的. 如果不手动关闭文件, 操作系统会把长时间没有用的文件关闭, 但是在没关闭之前, 文件虽然没人在用, 但是文件依然处于打开状态.
print(f) # 回收了操作系统资源后, f变量还是存在的
>> <_io.textiowrapper name="C:\\Users\\David\\PycharmProjects\\pythonProject\\s14\\day11\\练习.py" mode="rt" encoding="utf-8">
f.read() # 变量f存在,但是不能再读了, 这时f剩的只是文件对象, 而真正的文件已经被f.close()了
res = f.read()
print(res)
>> res = f.read()
ValueError: I/O operation on closed file.
del f # 回收应用程序资源, 变量一般不需要手动删除, 因为Python的垃圾回收机制会做这件事
# 一定是先回收操作系统的资源, 再回收内存资源, 如果先把内存资源的变量名f回收了, 那么f.close()就找不到f了
4.4 with上下文管理
准备两个文件, a.txt, b.txt. a.txt存aaaa, b.txt存bbbb
# 打开文本文件a.txt, 使用读模式, 并且赋值给f1.
# 注意: with 不能直接把open()的结果通过"="赋值给变量
# with会自动执行f1.close(), 在with子代码块运行完后, 关闭文件
with open('a.txt', mode = 'rt') as f1:
res = f1.read()
print(res)
>> aaaa
# with可以同时打开多个文件
with open('a.txt', mode = 'rt') as f1, open('b.txt', mode = 'rt') as f2:
res1 = f1.read()
res2 = f2.read()
print(res1) >> aaaa
print(res2) >> bbbb
open()一个文件, 得到的返回值, 是文件对象, 也可以叫文件句柄, 可以理解为对真正的磁盘上的文件的一种引用, 用来控制文件
4.5 指定字符编码
创建一个新文件, c.txt, 存汉字, 哈哈哈哈
with open('c.txt', mode = 'rt') as f1:
res1 = f1.read()
print(res1)
>> 哈哈哈哈
res1 = f1.read()
# 执行f1.read(), 会把目标文件, 从硬盘读入到内存
# 文件c.txt在硬盘是utf-8格式的, 读入到内存后也是utf-8格式
# 但是, 因为读取的是文本文件,是t模式, 所以在内存中要求读写的字符串都是unicode格式, 因此t模式会将read()读入的uts-8解码成unicode
# 解码要按照文件存成的字符编码去解码
# 但是, 如果命令中没有告诉open()用哪种字符编码格式进行解码操作, 那么open()会用操作系统默认的字符编码,去解码
# 没有指定encoding参数, 操作系统会使用自己默认的字符编码, 去解码
# Linux, Mac, utf-8
# Windows, 默认要看语言设置, 在dos命令行通过chcp命令查看活动代码页, 然后和活动代码页对照
# 因此, 如果操作系统默认不是utf-8, 那么解码的时候就是用另一个字符编码去解码utf-8, 就会出现乱码
with open('c.txt', mode = 'rt', encoding='utf-8') as f1: # 告诉open()用utf-8去解码
res1 = f1.read()
print(res1, type(res1))
>> 哈哈哈哈
# encoding='utf-8'就是规定, 无论编码还是解码, 都用utf-8的字符编码去执行. 文本读入到内存,用utd-8去解码, 写入到磁盘用utf8去编码
5 文本模式(t模式)下的文件读写操作模式
5.1 r模式
默认的操作模式
只读模式
# 当open()要读的文件不存在时, r模式会报错, 找不到文件或目录
# 当open()要读的文件存在时, r模式打开文件时, 文件指针跳到最开始位置
with open('c.txt', mode = 'rt', encoding='utf-8') as f1:
res = f1.read()
# f1.read()在没有指定任何参数的情况下, 会把文件内容, 从当前指针的位置一下读到结尾
# r模式打开文件时, 指针默认就是在文件最开始, 所以执行f1.read()的结果就是, 一次性把文件全部的内容, 都读入到硬盘. 因此读大文件时, 会造成内存被文件大量占用
print(res)
>> 哈哈哈哈
r模式小问题
with open('c.txt', mode = 'rt', encoding='utf-8') as f1:
print("第一次读".center(50,'*'))
res = f1.read()
print(res)
print("第二次读".center(50,'*'))
res = f1.read()
print(res)
>>
***********************第一次读***********************
哈哈哈哈
***********************第二次读***********************
# 第二次读c.txt文件时, 并没有结果返回, 这是因此,r模式会一次性从文件开始读到结尾, 读完, 文件指针就停在了文件结尾
# 而由于第一次读完, 文件没有关闭就开始读第二次, 那么第二次读时就是从文件默认开始读, 那肯定是没有内容的, 所以没有返回值
with open('c.txt', mode = 'rt', encoding='utf-8') as f1:
print("第一次读".center(50,'*'))
res = f1.read()
print(res)
with open('c.txt', mode='rt', encoding='utf-8') as f1:
print("第二次读".center(50,'*'))
res = f1.read()
print(res)
>>
***********************第一次读***********************
哈哈哈哈
***********************第二次读***********************
哈哈哈哈
# 这时, 第一次读完c.txt, 返回哈哈哈哈后, 再次打开c.txt, 由于文件是存在的, 那么r模式下, 文件指针会移动到文件最开始, 从最开始一直读到结尾, 最后返回哈哈哈哈
补充: 如果文件中有特殊字符, 比如换行, 那么r模式也会读取并返回
r模式案例: 利用with open()读取文本中的用户名和密码, 和用户输入的用户名和密码作比较, 如果相等, 返回登录成功, 不相等, 返回登录失败
先创建一个文本文件, 保存用户名和密码
# 注意, 文件不要有多余的空行和空格,如果有, 那么r模式读文件时, 会把空行和空格都读出来, 然后用split()去做分隔, 会造成数据错误
user.txt
admin:password
#版本1: 仅实现单用户的验证
_username = input("username: ").strip() # 将用户输入用strip()处理, 去除左右的空格
_password = input("password: ").strip()
with open("user.txt", mode='rt',encoding='utf-8') as f:
info = f.read()
username, password = info.split(':') # 将info中的数据, 用split()以":"做分隔, 分隔后的结果是个列表, 然后将列表解压, 把值赋给对应的变量
if _username == username and _password == password:
print("登录成功")
else:
print("登录失败")
#版本2: 实现多用户验证
# 先在user.txt中添加多个用户名和密码
admin:password
admin1:password1
admin2:password2
admin3:password3
********代码*********
_username = input("username: ").strip()
_password = input("password: ").strip()
with open("user.txt", mode='rt', encoding='utf-8') as f:
for line in f: # 这里不能用info = f.read(), 然后用for循环去遍历info, 因为info里存的是字符串, for循环遍历字符串会打印每一个字符
# print(line,end='')
# 这里因为本身文本每一个结尾都是换行符, 而且print默认会在结尾加换行符, 所以, 需要用end='', 把print结尾的换行符转行成空, 只留文本中每一行末尾的换行符
# username, password = line.split(':')
# 这里也不能直接使用split()去做分隔, 因为, 上面print(line,end='')的结果的每一行后面都是有换行符\n的
# 如果这里直接用split(), 那么得到的结果就是 admin, password\n, 这样去和用户输入作比较就会出问题
# info = line.strip('\n').split(':')
# 使用strip去除每行最后的换行符, strip去掉的是字符左右的空白字符, 包括空格, 换行符, 等. 这样info中存的就是一个个列表, 由用户名和密码组成
username, password = line.strip('\n').split(':') # 这样就从文件中, 得到了所有的用户名和密码对应关系
if _username == username and _password == password:
print("登录成功")
break
else: # 这里的else一定是作用在for循环, 而不是if条件判断, 因为需要把整个文件都遍历完, 发现没有符合的账号密码, 才返回登录失败
print("登录失败")
# 不过这时, 需要把整个文件每一个都遍历一遍, 如果账号密码信息众多, 会有效率问题, 之后可以考虑, 把用户名和密码转成字典格式, 如果输入的用户名在整个文本里存在, 那么就根据用户名作为key去取密码, 然后判断密码是否相等, 如果用户名本身就不存在, 那么直接返回登录失败
5.2 w模式
只写模式
# 当文件不存在时, 会创建空文件
with open('d.txt', mode = 'wt', encoding= 'utf-8') as f:
pass
图片.png
# 当文件存在时, w模式会清空文件
d.txt
哈哈哈哈
啦啦啦啦
with open('d.txt', mode = 'wt', encoding= 'utf-8') as f:
pass
>> 清空文件, 指针位于文件开始位置
只写, 不能读, 不能使用read()
with open('d.txt', mode = 'wt', encoding= 'utf-8') as f:
f.read()
>> io.UnsupportedOperation: not readable
写数据, 用write()
with open('d.txt', mode = 'wt', encoding= 'utf-8') as f:
f.write("hahaha")
# 会把write()中的数据写入到文件, 并且指针处于行尾, 而且默认是不换行的
图片.png
# 如果想要换行, 在写入的内容后加换行符即可'\n'
with open('d.txt', mode = 'wt', encoding= 'utf-8') as f:
f.write("hahaha\n")
# 需要注意的是, 此时, 再次写入数据, 因为文件是存在的, 所以会清空文件, 再把新的内容写进去, 所以, w模式不支持追加, 而是每次新输入都把源文件的数据清空,文件指针放到首位, 把新的内容写进去
图片.png
补充1: w模式, 在没有关闭文件的情况下, 连续写入数据, 新数据会接着上一次文件指针的位置, 接着写入, 并不会清空源文件
with open('d.txt', mode = 'wt', encoding= 'utf-8') as f:
f.write("ha\n")
f.write("haha\n")
f.write("haha\ha\n")
# 这时, 文件会依次写入数据
ha
haha
haha\ha
补充2: w模式, 每次关闭文件后, 连续写入, 那么每次都会把文件先清空, 文件指针回到文件开头, 然后写入数据, 因此, 对于重要的文件, 千万不要用w模式打开写入
5.3 a模式
-只追加写, 不能读
# 在文件不存在时, 会创建空文件, 文件指针处于文件开始位置
with open('e.txt', mode = 'at', encoding= 'utf-8') as f:
pass
图片.png
# 在文件存在时, 打开文件后, 会把文件指针移动到文件末尾, 然后写入新的数据
with open('e.txt', mode = 'at', encoding= 'utf-8') as f:
f.write("哈哈哈哈")
# 如果文件指针始终处于一行的结尾, 并且新写入的数据没有换行符, 那么数据就一直在同一行追加
# 在文件打开不关时, 连续追加数据时, 和w模式下, 不关文件连续追加数据一样, 都是在文件指针末尾追加数据
a模式, 每次打开文件时, 不会清空文件, 因为它是把文件指针移动到文件结尾, 然后写入数据
w模式, 每次打开文件时, 都会清空文件, 因为它是每次都把文件的指针移动到文件开始的位置, 所以会情况原有内容
补充: w和a模式的使用场景
w模式因为每次打开都会清空文件, 因此, 适用于创建新文件的场景
a模式因为每次打开不会清空文件, 而是追加写文件, 适用于日志文件, 等需要记录数据并且追加数据的场景
a模式案例: 账号注册功能
_username = input("请输入用户名: ")
_password = input("请输入密码: ")
with open("user_register.txt", mode = 'at', encoding = "utf-8") as f:
f.write("{username}:{password}\n".format(username=_username, password=_password))
# 这样每次运行, 都会把用户输入的用户名和密码, 以固定的格式存到"user_register.txt"里, 实现账号信息持久保存
admin1:password1
admin2:password2
admin3:password3
w模式案例: 实现文本文件复制
# 思路1:
with open("user_register.txt", mode = 'rt', encoding = "utf-8") as f1:
info = f1.read()
# print(info)
with open("user_register_backup.txt", mode = 'wt', encoding= "utf-8") as f2:
f2.write("{info}".format(info=info))
# 思路2, 把读文件和写文件写到一条命令
with open("user_register.txt", mode = 'rt', encoding = "utf-8") as f1, \
open("user_register_backup.txt", mode = 'wt', encoding = "utf-8") as f2:
res = f1.read()
f2.write(res)
# 实现复制脚本
_src_file = input("请输入源文件的绝对路径: ").strip()
_dst_file = input("请输入目标文件的绝对路径: ").strip()
with open(r"{src_file}".format(src_file=_src_file), mode = 'rt', encoding = "utf-8") as f1, \
open(r"{dst_file}".format(dst_file=_dst_file), mode = 'wt', encoding = "utf-8") as f2:
res = f1.read()
f2.write(res)
图片.png
图片.png
# 利用for循环把每行数据读出来, 再写入到新文件
_src_file = input("请输入源文件的绝对路径: ").strip()
_dst_file = input("请输入目标文件的绝对路径: ").strip()
with open(r"{src_file}".format(src_file=_src_file), mode = 'rt', encoding = "utf-8") as f1, \
open(r"{dst_file}".format(dst_file=_dst_file), mode = 'wt', encoding = "utf-8") as f2:
for line in f1: # 使用for循环, 将f1的每一行读出来, 写到f2里, 写进去一行, 引用计数就为0, 会被解释器清空, 内存中永远只有一行数据, 对内存压力小
f2.write(line)
5.4 +模式
"+" 不能单独使用, 必须配合r, w, 或a模式一起使用
r+t:
r+模式处理文件, 特性与限制取决于r模式
r+模式处理文件时, 文件不存在, 会直接报错, 因为是以r为基础, r模式下, 打开不存在的文件就是直接报错
with open('g.txt', mode = 'r+t', encoding= 'utf-8') as f:
...
>> FileNotFoundError: [Errno 2] No such file or directory: 'g.txt'
注意: r+模式下, 如果源文件非空, 那么使用write()写数据时, 会从文件开始位置依次覆盖, 因为r模式打开文件时, 会把文件指针移动到文件最开始
w+t
w+模式处理文件时, 如果文件不存在, 则按照w的特性, 会创建文件
with open('g.txt', mode = 'w+t', encoding= 'utf-8') as f:
...
>> 创建g.txt文件
w+模式处理文件时, 如果文件存在, 则按照w的特性, 会清空文件
with open('g.txt', mode = 'w+t', encoding= 'utf-8') as f:
...
# 先向g.txt写入一些数据, 然后执行with open(), 由于g.txt文件已经存在, 那么按照w的特性, 会把g.txt文件情况
注意: w+虽然可以读文件了, 但是只要打开文件, 内容就会被清空.
a+t
a+模式处理文件是, 如果文件不存在, 则创建文件, 如果文件存在, 则把文件指针移动到文件最末端
w+和a+的特性:
在文件不关闭的情况下, 使用w+或者a+连续写入数据后, 如果接着想要在不关闭文件的情况下用read()读数据是读不出来的, 因为w+和a+写完数据后, 文件指针会停在文件末尾, 而read()是从文件指针的当前位置开始读, 因为永远是读不出来内容的
with open('g.txt', mode = 'w+t', encoding= 'utf-8') as f:
f.write("哈哈哈\n")
f.write("哈哈哈\n")
f.write("哈哈哈\n")
f.write("哈哈哈\n")
f.write("哈哈哈\n")
print(f.read())
>> 空, 但是g.txt是有内容的, a+模式效果一样, 写输入后, 文件指针都是停在文件末尾, read()又是从文件指针当前位置开始读, 所以读不出来数据
图片.png
对于已存在的文件:
a模式打开文件, 文件指针是在文件末尾的, 读不出来内容, w模式打开文件会清空, 所以也读不出来内容
总结
1、什么是文件
用户/应用程序(f=open(),获得文件对象/文件句柄)
操作系统(文件)
计算机硬件(硬盘)
2、为何要用文件
用于应用程序操作硬盘,永久保存数据,或者从硬盘读数据
3、如何用
f=open(r'C:\new_dir\a.txt',mode='r+t',encoding='gbk')
# f.read()就是把硬盘的二进制数据, 读到内存
# t会告诉open()功能将二进制转换成字符文本, 但前提是这些二进制数据, 原本就是由字符文本转换成的二进制
# 然后open()并不知道这些二进制是什么格式的二进制, 因此open()功能会用默认的字符编码也就是系统的字符编码去打开文件,把这些文件当成是系统的字符编码去打开, 因此在open()功能要指定encoding, 告诉open()功能用什么字符编码去解码打开文件
# f.read() 读出来的就是unicode, python打印出来时会转成utf-8
res=f.read() # 读出硬盘的二进制(gbk)->t控制将二进制转换unicode->字符
print(res)
# f.write()
f.close()
print(f) # f.close()把文件关闭后, f还是存在的, 因为f是python的一个变量, 但是f.read()就不存在了, 因为文件已经关闭了
f.read() # 抛出异常
with open(...) as f,open(...) as f1:
code1
code2
code3
4. open()打开文件是没有效率问题的, 因此只是产生一个文件句柄, 发起系统调用去打开文件, 并没有读和写操作, 只有读和写操作是涉及I/O
5. 复制文件脚本
_src_file = input("请输入源文件的绝对路径: ").strip()
_dst_file = input("请输入目标文件的绝对路径: ").strip()
with open(r"{src_file}".format(src_file=_src_file), mode = 'rt', encoding = "utf-8") as f1, \
open(r"{dst_file}".format(dst_file=_dst_file), mode = 'wt', encoding = "utf-8") as f2:
#res = f1.read() # 这种方法, 会瞬间将文件内容全部读出来到内存, 赋值给res, 如果文件量大, 会占用大量内存空间
#f2.write(res)
for line in f1: # 使用for循环, 将f1的每一行读出来, 写到f2里, 写进去一行, 引用计数就为0, 会被解释器清空, 内存中永远只有一行数据, 对内存压力小
f2.write(line)
5.5 x模式
x模式(控制文件操作的模式): 只写, 不可读, 执行open()时, 如果文件存在则报错, 如果文件不存在则创建
with open(r'aa.txt', mode = 'xt', encoding ='utf-8') as f1:
pass
图片.png
文件存在时, 则报错
FileExistsError: [Errno 17] File exists: 'aa.txt'
x模式的问题是, 只能在文件不存在的时候, 进行写操作, 一旦文件创建出来了, 打开就会报错, 也就无法后续修改
6 文本模式(b模式)下的文件读写操作模式
b: binary模式
读写都是以bytes为单位
可以针对所有文件
b模式下, 一定不能指定encoding参数, b模式和encoding没关系, 不做解码操作.
b模式更通用
b模式下, 执行f.read(), 会把硬盘内容读入内存, 但是不会做任何转换, 只是二进制
Python解释器会将这些二进制转换成bytes类型
with open(r'aaaa.txt', mode = 'rb') as f1:
res = f1.read() # b模式下, 会把文件直接从硬盘加载到内存, 不做任何转换, 文本如果是以utf-8存到硬盘, 那么b模式读到内存, 就是utf-8格式的二进制, 那么赋值给res打印后也是utf-8格式的二进制, Python解释器再把它们以16进制显示出来, 如果是英文字母就直接显示英文
print(res)
>> b'\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88aaaa\r\n'
如果需要读取字符, 那么需要解码, 由utf-8解码成可读字符
print(res.decode('utf-8'))
>> 哈哈哈哈aaaa
6.1 b模式和t模式的对比
b模式更通用, 可以处理所有类型的文件, 包括文本文件, 视频, 图片等
t模式只能处理文件文件
在处理文本文件时, t模式更方式, 因为t模式会自动解码, 将读到内存中的二进制解码成可读字符
而如果使用b模式去处理文本文件, 还需要手动执行编码和解码操作
6.2 b模式案例
b模式实现文件拷贝工具
src_file=input('源文件路径>>: ').strip()
dst_file=input('源文件路径>>: ').strip()
with open(r'{}'.format(src_file),mode='rb') as f1,\
open(r'{}'.format(dst_file),mode='wb') as f2:
# res=f1.read() # 内存占用过大
# f2.write(res)
for line in f1:
f2.write(line)
循环读取文件两种方式
方式一:自己控制每次读取的数据的数据量
with open(r'test.jpg',mode='rb') as f:
while True:
res=f.read(1024) # 1024
if len(res) == 0:
break # 当长度为0时, 终止循环
print(len(res))
方式二:以行为单位读,当一行内容过长时会导致一次性读入内容的数据量过大, 此时应该使用while循环, 指定每次读的字节长度
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
for line in f:
print(len(line),line)
with open(r'g.txt',mode='rb') as f:
for line in f:
print(line)
with open(r'test.jpg',mode='rb') as f:
for line in f:
print(line)
7 文件操作的其他方法
读相关操作
7.1 readline()
一次只读一行, 以换行符为分隔符
with open(r'a.txt', mode = 'rt', encoding = 'utf-8') as f1:
res1 = f1.readline()
res2 = f1.readline()
print(res1, end='')
print(res2, end='')
>>
嘿嘿嘿ccc
哦哦哦bbb
利用while循环
while True:
res = f1.readline()
if len(res) == 0:
break
print(res,end='')
>>
嘿嘿嘿ccc
哦哦哦bbb
哈哈哈aaa
7.2 readlines()
读多行, 并且以列表的形式返回
with open(r'a.txt', mode = 'rt', encoding = 'utf-8') as f1:
res = f1.readlines()
print(res)
>> ['嘿嘿嘿ccc\n', '哦哦哦bbb\n', '哈哈哈aaa']
f.read()与f.readlines()都是将内容一次性读入内存,如果内容过大会导致内存溢出,若想将内容全读入内存,要用循环
写相关操作
7.3 writelines()
write()本身就可以写入多行, 只要用\n换行符即可
with open(r'a.txt', mode = 'wt', encoding = 'utf-8') as f1:
f1.write('111\n222\n333')
>>
111
222
333
writelines()作用是: 将列表中的元素, 写入到文件里
with open(r'a.txt', mode = 'wt', encoding = 'utf-8') as f1:
l = ['111\n','222','333']
for line in l: # for循环会遍历列表中的每个元素, 然后再写入到文件里, 但是如果想把每个元素写到单独一行, 那么列表的元素需要有个'\n', 否则都会写到以一行
f1.write(line)
>>
111
222333
writelines()可以实现上面相同的功能
with open(r'a.txt', mode = 'wt', encoding = 'utf-8') as f1:
l = ['111\n','222','333']
f1.writelines(l)
>>
111
222333
用b模式写入
with open(r'a.txt', mode = 'wb') as f1:
l = ['111\n'.encode('utf-8'),'222'.encode('utf-8'),'333'.encode('utf-8')] # 需要对字符进行encode编码, encode是字符编码, 对其他类型无效
f1.writelines(l)
>>
纯英文字符, 直接在字符前加b即可, 不用.encode()
with open(r'a.txt', mode = 'wb') as f1:
l = [b'aaa',b'bbb',b'ccc']
f1.writelines(l)
'上'.encode('utf-8') 等同于bytes('上',encoding='utf-8')
7.4 flush()
将产生的数据立即写入磁盘
7.5 其他方法
readable() 判断文件是否可读
writeable() 判断文件是否可写
closed() 判断文件是否已经被关闭
8 文件指针移动
指针移动的单位都是以bytes/字节为单位
只有一种情况特殊:
t模式下的read(n),n代表的是字符个数
with open(r'a.txt', mode = 'rt', encoding='utf-8') as f1:
res = f1.read(4) # read(n)以字符为单位读取
print(res)
>>
aaa您
seek() 用来控制文件指针的移动
f.seek(n,模式):n指的是移动的字节个数
模式0:参照物是文件开头位置
f.seek(9,0) # 位置9
f.seek(3,0) # 位置33
模式1:参照物是当前指针所在位置
f.seek(9,1) # 9
f.seek(3,1) # 12
模式2:参照物是文件末尾位置,应该倒着移动
假设文件一共12个字节
f.seek(-9,2) # 3
f.seek(-3,2) # 9
强调:只有0模式可以在t下使用,1、2必须在b模式下用
f.tell() 获取文件指针当前位置
8.1 案例
准备文件
图片.png
with open(r'a.txt', mode = 'rb') as f1:
f1.seek(9,0)
print(f1.tell()) >> 9
f1.seek(3,0)
print(f1.tell()) >> 3
当文件指针移动到某一个位置后, 执行解码操作, 那么解出来的是指针后面的字节
with open(r'a.txt', mode = 'rb') as f1:
f1.seek(3,0)
res = f1.read()
print(res.decode('utf-8'))
>> 您好
with open(r'a.txt', mode = 'rb') as f1:
f1.seek(-9,2)
print(f1.tell()) >> 0
f1.seek(-3,2)
print(f1.tell()) >> 6
补充:
得到bytes类型的三种方式
bytes >> 二进制
1、字符串编码之后的结果
'上'.encode('utf-8')
bytes('上',encoding='utf-8')
2、b'必须是纯英文字符'
3、b模式下打开文件,f.read()读出的内容
8.2 seek()应用, 实现tail -f功能
准备数据写入程序
input.py
with open(r'a.txt', mode = 'at', encoding ='utf-8') as f: # 输入一定要a模式, 追加写
f.write('哈哈哈\n')
准备tail -f程序
output.py
import time
with open(r'a.txt', mode = 'rb') as f: # 使用b模式, 更通用
f.seek(0,2) # 2模式每次打开文件后, 将指针移动到文件某位, 并且正向读0个字节, 这样只要文件没有输入, 那么每次循环后文件指针还是原地不动
while True:
output = f.readline()
if len(output) == 0:
time.sleep(0.5)
else:
print(output.decode('utf-8'),end='')
准备文件
a.txt
执行输出output.py程序
C:\python38\python.exe C:/Users/David/PycharmProjects/pythonProject/s14/day11/output.py
执行output.py后, 指针会停留在文件开始, 不断的去循环读取a.txt文件, 一旦有内容进来, 就会输出
执行input.py, 查看output.py结果
C:\python38\python.exe C:/Users/David/PycharmProjects/pythonProject/s14/day11/output.py
哈哈哈
8.3 文件修改的两种方式
8.3.1 类文本编辑器式修改
准备文件
a.txt
哈哈哈
嘿嘿嘿
哦哦哦
with open(r'a.txt', mode = 'rt', encoding='utf-8') as f:
res = f.read() # 将文件读到内存, 赋值给res
data = res.replace('哈哈哈','hahaha') # 将res中的'哈哈哈'替换成'hahaha'再赋值给data
with open(r'a.txt', mode = 'wt', encoding='utf-8') as f1: # 利用w模式打开文件, 将文件清空, 重新把data的数据写入文件, 就完成了字符串的替换
f1.write(data)
利用这种方法, 每次做文件修改, 都是先w模式打开把文件清空, 再把要写的内容全部写入到文件
8.3.2 生成一个新文件, 将内容写到新文件, 然后删除旧文件
准备文件
a.txt
哈哈哈
嘿嘿嘿
哦哦哦
import os
with open(r'a.txt', mode = 'rt', encoding = 'utf-8') as f1, \ # 打开源文件
open(r'.a.txt.swap', mode = 'wt', encoding = 'utf-8') as f2: # 以w模式打开一个新的.swap临时文件
for line in f1: # 从源文件中读一行
f2.write(line.replace('哈哈哈','aaa')) # 将源文件中需要替换的字符做替换, 然后写入写到文件, 由于新的文件是以w模式开启, 并且一直没有关闭, 所以不会清空. 循环结束, 每一行的字符都会被替换
os.remove('a.txt') # 修改完, 需要删除源文件
os.rename('.a.txt.swap','a.txt') # 然后将临时文件改名为源文件
aaa
嘿嘿嘿
哦哦哦
8.3.3 两种方式对比
第一种方式: 耗费内存空间, 因为read()是把数据一次性读入内容, 但是节省了硬盘空间, 因为文件上的数据只有一份
第二种方式: 耗费硬盘空间, 因为在文件修改期间, 需要生成临时文件, 但是内存空间是不浪费的, 每次都是读一行加载到内存, 做修改, 然后写入到新文件