Python-学习笔记05(文件和编码)

Author Avatar
EmptinessBoy 7月 07, 2020
  • 在其它设备中阅读本文章

文件编码解码

字符编码

Unicode「字符集」UTF-8「编码规则」


字符集:为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point)
编码规则:将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)

计算机内存里面使用的是 unicode,硬盘里面使用的是 utf-8。因为存储 utf-8 更加节约空间。

utf-8 是变长的也就意味着用户输入字符 “你好ab” 的时候,如果内存也用 utf-8 则需要经历一个计算的过程,然后才能存储,这个时候计算消耗的时间会非常的多(总时间 = 计算机时间 + 存储时间)但如果内存里面的编码采用 unicode 则不需要计算

我们现在使用的编辑器默认都是 utf-8

python 字符编码

python3 内部默认使用的是 unicode 码

# 编码与解码
s = 'hello world 你好'

# 编码:将人看的变成计算机能够看的懂的
res = s.encode('utf-8')
print(res)  # b'hello world \xe4\xbd\xa0\xe5\xa5\xbd'
"""在python中的bytes类型你可以直接看成二进制格式"""

# 解码:将计算机看到懂的变成人能看的懂
print(res.decode('gbk'))
print(res.decode('utf-8'))
print(res.decode())


>>> b'hello world \xe4\xbd\xa0\xe5\xa5\xbd'
hello world 浣犲ソ
hello world 你好
hello world 你好

文件

计算机文件,是存储在某种长期储存设备上的一段数据流。其特点是所存信息可以长期、多次使用,不会因为断电而消失。

计算机文件可分为二种: 二进制文件和文本文件

  • 图形文件及文字处理程序等计算机程序都属于二进制文件。
  • 文本文件则是可以用文字处理程序阅读的简单文本文件。

文件读写步骤

  1. 打开文件
  2. 处理数据
  3. 关闭文件

open 函数

fileobj = open(filename, mode)

fileobj 是 open() 返回的文件对象,filename 是该文件的文件名。

mode 是指明文件类型和操作的字符串。

  • mode 的第一个字母表明对其的操作。
  • mode 的第二个字母是文件类型:
    • t(可省略)代表文本类型文件;
    • b 代表二进制类型文件。

文件打开模式

文件打开模式含义
“r”只读模式 (默认)
“w”覆盖写模式 (不存在则新创建;存在则重写新内容)
“a”追加模式 (不存在则新创建;存在则只追加内容)
“x”创建写模式 (不存在则新创建;存在则出错)
“+”与 r/w/a/x 一起使用,增加读写功能
“t”文本类型
“b”二进制类型

文件读写函数

名称含义
open()打开文件
read(size)从文件读取长度为size的字符串,如果未给定或为负则读取所有内容
readline()读取整行,返回字符串
readlines()读取所有行并 返回列表
write(s)把字符串 s 的内容写入文件
writelines(s)向文件写入一个元素为字符串的列表,如果需要换行则要自己加入每行的换行符。
seek(off, whence=0)设置文件当前位置
tell()返回文件读写的当前位置
close()关闭文件。关闭后文件不能再进行读写操作。

读取文本文件

文本文件内容(hello.txt):

Hello, I am emptinessboy! 你好,我是 emptinessboy!

文本方式打开:

textfile1 = open("hello.txt","rt",encoding="utf-8")  # 打开文件(文本方式)
t = textfile1.readline() # 读取文件
print(t)    # 处理数据(打印文件)
textfile1.close()   # 关闭文件

>>> Hello, I am emptinessboy!   你好,我是 emptinessboy!

二进制方式打开:

textfile2 = open("hello.txt","rb")  # 打开文件(二进制方式)
t = textfile2.readline() # 读取文件
print(t)    # 处理数据(打印文件)
textfile2.close()   # 关闭文件


>>> b'Hello, I am emptinessboy!   \xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe6\x88\x91\xe6\x98\xaf emptinessboy!\r\n'

python-read-file.png

文件复制 demo

复制文件不需要考虑行结构,用 read 函数就可以了。这里将 hello.txt 复制为 hello_copy.txt

source1 = open("hello.txt","r",encoding="utf-8") # UTF-8模式读取原始文件并,赋给对象source1
source2 = open("hello_copy.txt","w",encoding="utf-8") # UTF-8模式读创建待写入文件并,赋给对象source2
s1 = source1.read() # 从文件1读取所有内容
source2.write(s1) # 写入内容
source2.close() # 关闭文件流
source1.close()

python-filecp.png

从复制后的文件可以看到,复制后的行结构没用改变

多行文件读写

多行文件读写

用 readlines() 读写多行文件,用嵌套列表存放多行内容

f=open("score.txt","r")
    for line in f.readlines():
        print(line) #处理行
f.close()
多行文本读取 demo

我们尝试读取下刚才的 hello_copy.txt

f1 = open("hello_copy.txt", "r", encoding="utf-8")  # utf-8模式打开文件
f2 = f1.readlines()  # 将多行变成列表(数组)

print(f2)
for l in f2:
    print(l, end="")

f1.close()


>>> ['Hello, I am emptinessboy!   你好,我是 emptinessboy!\n', 'Hello, I am emptinessboy!   你好,我是 emptinessboy!\n', 'Hello, I am emptinessboy!   你好,我是 emptinessboy!']
Hello, I am emptinessboy!   你好,我是 emptinessboy!
Hello, I am emptinessboy!   你好,我是 emptinessboy!
Hello, I am emptinessboy!   你好,我是 emptinessboy!

实际应用:计算总评分:

这里先建一个如下格式的文本,用来存放测试信息:

python-read-lines-demo.png

这个案例中,我们使用嵌套列表来存放多行内容:我们计算每一个学生的总评

总评 = 笔试 * 50% + 平时 * 25% + 实验 * 25%

f = open("score", "r",encoding="utf-8")
head = f.readline()  # 读表头行
newhead = head[:-1] + '\t总评成绩'
print(newhead)
for line in f.readlines(): # 这一步python会从我们刚才读取文件的位置继续往下读取,而不会从头开始
    l = line.split() # 将读取的一行字符串继续按空格拆分为嵌套列表
    # 例如第一行会变成 ['2050921018', '詹延峰', '计算数学', '65', '85', '76']
    s = round(int(l[3]) * 0.5 + int(l[4]) * 0.25 + int(l[5]) * 0.25, 2)  # 求总评分
    print(' '.join(l) + ' ' + str(s))  # 加空格对齐
f.close()

运行效果:

python-read-lines-result.png

文件操作拆解

f = open('a.txt','r',encoding='utf-8')  # 默认就是r模式 read模式 只读模式

"""
在打开文本文件的时候如果你不指定编码,那么编码就依据当前操作系统的默认编码
windows     GBK
linux       utf8
Mac         ASCII
"""

# f是一个文件对象 调用文件对象的一些方法就能够操作该文件
data = f.read()
f.close()  # 向操作系统发起关闭文件的请求 回收资源

"""
打开一个文件包含两部分资源,应用程序的变量 f 和操作系统打开的文件。在操作完毕一个文件之后必须把与该文件的这两部分资源回收

变量 f 是python程序里面不需要你回收(python有自动的垃圾回收机制)
open 打开的资源存在与操作系统中(需要手工回收)
"""

with open 取代 open

with 用来创建临时运行环境

作用:with用于创建一个临时的运行环境,运行环境中的代码执行完后自动安全退出环境。

文件操作:使用 ope n进行文件操作使建议使用 with 创建运行环境,可以不用 close() 方法关闭文件,无论在文件使用中遇到什么问题都能安全的退出,即使发生错误,退出运行时环境时也能安全退出文件并给出报错信息。

关键字 with 用于上下文管理(推荐使用)

with open('a.txt','r',encoding='utf-8') as f:
    data = f.read()

"""在执行完with代码块之后 with会自动帮你调用close方法"""

# with一次性打开多个文件

with open('a.txt','r',encoding='utf-8') as f1,\
        open('b.txt','r') as f2:
    pass

文件的操作模式

r 模式(read)

r 模式打开文件 文件必须存在否则直接报错。基本格式如下:

with open('a.txt','r',encoding='utf-8') as f:
    pass

# 例如:    
with open('a.txt','r',encoding='utf-8') as f:
    data = f.read()  # 读取文件内所有的内容
    print(data)

基于 r 模式,实现用户认证功能,文件作为数据库:

username = input('>>>:').strip()
password = input('>>>:').strip()
with open('userinfo','r',encoding='utf-8') as f:
    for line in f:  # f文件对象可以被for循环 类似于一行行的读取文件内容
        # 切割字符串
        usr, pwd = line.strip('\n').split('|')
        if username == usr and password == pwd:
            print('登陆成功')
            # 只要验证成功了 for循环就没有必要再执行了
            break
    else:
        print('用户名或密码错误')

运行效果:

python-file-r.png

w 模式(write)

w 模式下文件不存在,也不会报错,反而会自动创建该文件。因此 w 模式一定要慎用,文件路径前面最好加一个 r 取消转义

  1. 先清空目标文件
  2. 才是写入内容

基本格式如下:

with open(r'b.txt','w',encoding='utf-8') as f:
    pass

# 文件路径取消转义
with open(r'b.txt','w',encoding='utf-8') as f:
    pass

简单写入案例:

with open('filew','w',encoding='utf-8') as f:
    f.write('你追我...\n')
    f.write('你追我...\n')
    f.write('你追我...\n')
    f.write('heiheihei 666\n')
# f.close 在 with 模式下可以不写

运行效果:

python-file-w-1.png

1.在文件不关闭的情况下 连续的写入,后写的内容一定跟在前写的内容后面

2.如果重新以w模式打开文件,则会先清空该文件

a 模式(append)

a 模式下文件不存在,也会自动创建该文件。文件存在的情况下 a 模式打开之后文件指针会直接移动到文件末尾。

with open('c.txt','a',encoding='utf-8') as f:
    pass

with open('c.txt','a',encoding='utf-8') as f:
    f.write('11111\n')
    f.write('22222\n')

基于 a 模式实现用户的注册功能:

username = input('>>>:').strip()
password = input('>>>:').strip()
with open('userinfo.txt','a',encoding='utf-8') as f:
    res = '%s|%s\n'%(username,password)
    f.write(res)

运行效果:

python-file-a.png

+ 模式

Python 读写文件模式:在生产环境中我们只单纯的使用 r/w/a 读写分离,一般不用 +

  1. r 打开只读文件,该文件必须存在。
  2. r+ 打开可读写的文件,该文件必须存在。
  3. w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
  4. w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
  5. a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
  6. a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
  7. 上述的形态字符串都可以再加一个 b 字符,如 rb、w+b 或 ab+ 等组合,加入 b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。不过在 POSIX 系统,Linux 都会忽略该字符。

规范

读写操作

最好先判断是否可读写:

print(f.readable())  # 判断当前是否可读
print(f.writable())  # 判断当前是否可写

读取文本文件的几种方法:

with open('a.txt','rt',encoding='utf-8') as f:
    # 法一:
    # 全部读取  当文件内容过大的时候 该方法可能会导致内存溢出
    # 该方法执行完毕之后文件的指针会移动到文件末尾
    data = f.read()

    # 法二:
    # 读取一行内容 指针会移动到第二行的首部
    data = f.readline()

    # 法三:
    # 读取每一行内容 存放于列表中 当文件过大的情况下也不推荐使用
    data = f.readlines()

    # 法四(推荐):
    # 我们在读取文件内容的时候 一般都是用for循环
    for line in f:
        print(line)

写入文本文件的推荐方法:

Python_write和writelines的区别

  1. write()需要传入一个字符串做为参数,否则会报错
  2. writelines()既可以传入字符串又可以传入一个字符序列,并将该字符序列写入文件
    • 注意 :writelines必须传入的是字符序列,不能是数字序列。
    • 错误写法如:list_1023 = [1,2,3,4,5]
with open('b.txt','wt',encoding='utf-8') as f:
    f.write('111')
    f.writelines(['aaa\n','bbb\n','ccc\n'])

    # 立刻将内存数据刷到硬盘 类似于ctrl+s
    f.flush()

修改文件

有两种方法可以实现文件修改:

  1. 将文件内容全部读取到内存 在内存中修改完毕之后覆盖原来的内容
  2. 重新创建一个新文件 然后将老文件内容一行行读取到内存修改之后写入新文件
    之后将老文件删除 将新文件名修改为老文件名 完成替换

适用于小文件:

with open('a.txt','r',encoding='utf-8') as f:
    data = f.read()
with open('a.txt','w',encoding='utf-8') as f:
    f.write(data.replace('tony','jason')) #替换

上面的方法在文件修改过程中同一份数据只有一份。文件过大的时候比较吃资源。

适用于大文件:

import os

with open('a.txt','r',encoding='utf-8') as read_f,\
    open('a.backend.txt','w',encoding='utf-8') as write_f:
    for line in read_f:
        write_f.write(line.replace('tony','jason')) #替换

os.remove('a.txt')
os.rename('a.backend.txt','b.txt')

这种方法不会占用太多的内存,不过在文件修改的时候同一份数据存了两份 比较消耗硬盘空间

主动控制文件内指针移动

大前提:文件内指针的移动都是 bytes 为单位,唯一列外的是 t 模式下的 read(n),n 是以字符串为单位。

# 字符模式
with open('a.txt','rt',encoding='utf-8') as f:
    data = f.read(3)  # 读取三个字符
    print(data)

# 字节模式
with open('a.txt','rb') as f:
    data = f.read(3)  # 读取三个字节
    print(data)

用 seek 控制指针

控制指针移动有三种模式,语法如下:

seek(指针移动的字节数,模式)
  • 0 模式:以文件开头作为移动的参照
  • 1 模式:以指针当前位置作为移动的参照
  • 2 模式:以文件末尾作为移动的参照

其中 0 模式以在 t 和 b 下使用,而 1 和 2 模式只能在b模式下使用

测试文件如下:

# a.txt
哈哈哈1234567890
abcdefghijk
lmnopqrstuv
wxyz
吼

读取三个字符:

with open('a.txt','rt',encoding='utf-8') as f:
    data = f.read(3)  # 读取三个字符
    print(data)


>>> 哈哈哈

读取三个字节:

with open('a.txt','rb') as f:
    data = f.read(3)  # 读取三个字节
    print(data)


>>> b'\xe5\x93\x88'

光标向后移动三个字节:(t模式)

with open('a.txt','rt',encoding='utf-8') as f:
    f.seek(3,0)  # utf8用一个 bytes 表示英文 3个 bytes 表示中文
    print(f.tell())  # 查看当前文件指针距离文件开头的位置 结果是3
    print(f.read())


>>> 3
哈哈1234567890
abcdefghijk
lmnopqrstuv
wxyz
吼

光标向后移动六个字节:(b模式)

with open('a.txt','rb') as f:
    f.seek(6,0)
    print(f.read().decode('utf-8'))


>>> 哈1234567890
abcdefghijk
lmnopqrstuv
wxyz
吼

This blog is under a CC BY-NC-ND 4.0 Unported License
本文链接:https://coding.emptinessboy.com/2020/07/Python-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B005%EF%BC%88%E6%96%87%E4%BB%B6%E5%92%8C%E7%BC%96%E7%A0%81%EF%BC%89/