python 获取字符编码值_9. 字符编码与Python之文件操作

字符编码

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

7b4dc35f85cb

图片.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()中的数据写入到文件, 并且指针处于行尾, 而且默认是不换行的

7b4dc35f85cb

图片.png

# 如果想要换行, 在写入的内容后加换行符即可'\n'

with open('d.txt', mode = 'wt', encoding= 'utf-8') as f:

f.write("hahaha\n")

# 需要注意的是, 此时, 再次写入数据, 因为文件是存在的, 所以会清空文件, 再把新的内容写进去, 所以, w模式不支持追加, 而是每次新输入都把源文件的数据清空,文件指针放到首位, 把新的内容写进去

7b4dc35f85cb

图片.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

7b4dc35f85cb

图片.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)

7b4dc35f85cb

图片.png

7b4dc35f85cb

图片.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()又是从文件指针当前位置开始读, 所以读不出来数据

7b4dc35f85cb

图片.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

7b4dc35f85cb

图片.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 案例

准备文件

7b4dc35f85cb

图片.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()是把数据一次性读入内容, 但是节省了硬盘空间, 因为文件上的数据只有一份

第二种方式: 耗费硬盘空间, 因为在文件修改期间, 需要生成临时文件, 但是内存空间是不浪费的, 每次都是读一行加载到内存, 做修改, 然后写入到新文件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值