第七章:字符编码与文件操作
字符编码
字符编码理论
各个编码之间的二进制,是不能互相识别的,会产生乱码。
文件的储存,传输,不能是 unicode(只能是 utf-8、utf-16、gbk、gb2312、asciid 等)。
1.字符编码只针对文本数据
2.既然计算机内部只认识 0 1 为什么我们却可以敲出人类各式各样的字符,存在一个数字跟字符的对应关系 存储该关系的地方称为:字符编码本
3.字符编码发展史
3.1.一家独大
计算机是由美国人发明的 为了能够让计算机识别英文
需要发明一个数字跟英文字母的对应关系
ASCII码:记录了英文字母跟数字的对应关系
用8bit(1字节)来表示一个英文字符
3.2.群雄割据
中国人
GBK码:记录了英文、中文与数字的对应关系
用至少16bit(2字节)来表示一个中文字符,很多生僻字还需要使用更多的字节
英文还是用8bit(1字节)来表示
日本人
shift_JIS码:记录了英文、日文与数字的对应关系
韩国人
Euc_kr码:记录了英文、韩文与数字的对应关系
"""
每个国家的计算机使用的都是自己定制的编码本
不同国家的文本数据无法直接交互 会出现"乱码"
"""
3.3.天下一统
unicode万国码
兼容所有国家语言字符
起步就是两个字节来表示字符
utf系列:utf8 utf16 ...
专门用于优化unocide存储问题
英文还是采用一个字节 中文三个字节
字符编码实操
1.针对乱码不要慌 切换编码慢慢试即可
2.编码与解码
编码:将人类的字符按照指定的编码编码成计算机能够读懂的数据
字符串.encode()
解码:将计算机能够读懂的数据按照指定的编码解码成人能够读懂
bytes类型数据.decode()
3.python2 与 python3 差异
python2 默认的编码是 ASCII
1.文件头
# encoding:utf8
2.字符串前面加u
u'你好啊'
python3 默认的编码是 utf 系列(unicode)
文件操作
打开文件的两种方法
# 方法一
fr = open(文件路径, 模式, 编码格式)
fr.close()
# 例
fr = open('./ysg.txt', mode='r', encoding='utf-8')
fr.close()
# 方法二
with open(文件路径, 模式, 编码格式) as fr:
pass
# 例
with open('./ysg.txt', mode='r', encoding='utf-8') as fr:
pass
with open('./ysg.txt', mode='r', encoding='utf-8') as fr,open('./ysging.txt', 'w', encoding='utf-8') as fw:
pass
'''
注意:
1、mode 可以省略
2、with open() 可以同时打开多个文件
3、路径中常会有 反斜杠与字母 的特殊组合,如:\a\r\n\t,这时使用 r'' 去除转义字符
'''
文件读写模式
注意:
- r+ 最为常用
- encoding 的编码格式一定要与文件编码格式一致
- 文件路径不存在则会直接报错
r rb 只读模式:只能读不能写
# 在本地创建 txt 格式的文件默认使用 gbk 格式
with open('e:/py/file.txt', 'r', encoding='utf-8') fr:
content = fr.read()
print(content,type(content))
# b 二进制模式 bytes
with open('e:/py/file.txt', 'rb', encoding='utf-8') frb:
content = frb.read()
print(content,type(content))
w wb 只写模式:只能写不能看
# 只写 w
# 我们写入的格式为 utf-8 那么当我们查看时也要使用 utf-8 格式
# 没有该文件 w 会创建文件
with open('e:/py/test.txt', 'w', encoding='utf-8') fw:
fw.write('正在写入...')
# 存在该文件 会覆盖写入
# 即只写的逻辑为 先清空文件再写入
with open('e:/py/test.txt', 'w', encoding='utf-8') fw:
fw.write('又一次写入...\n')
fw.write('又二次写入...\n')
# 注意:换行符一定要手动写入,后续数据读取比对的时候也一定要注意它的存在
# wb 写入
# 默认写入 bytes 类型 需要使用 encode 转换为 str 类型
# 写入与文件默认格式不同的类型 如:utf-8 会自动转换
with open('e:/py/test.txt', 'wb') fwb:
fwb.write('BBC是一个电视台.'.encode('gbk'))
a ab 追加模式:文件末尾添加数据
# 追加 a
# 追加与文件默认格式不同的类型 如:utf-8 不会自动转换
# 默认会自动追加在文件光标的位置(即有文字的最后一位上)
with open('e:/py/test.txt', 'a', encoding='gbk') fa:
fa.write('追加...')
# ab 追加
# 追加与文件默认格式不同的类型 如:utf-8 不会自动转换
with open('e:/py/test.txt', 'ab', encoding='gbk') fab:
fab.write('方式...'.encode('utf-8'))
文件操作模式
t 文本模式
默认的模式 我们上面所写的r w a其实全称是 rt wt at
1.只能操作文本文件
2.读写都是以字符为单位
3.需要指定 encoding 参数 如果不知道则会采用计算机默认的编码
b 二进制模式(bytes模式)
不是默认的模式 需要自己指定 rb wb ab
1.可以操作任意类型的文件
2.读写都是以 bytes 为单位
3.不需要指定 encoding 参数 因为它已经是二进制模式了 不需要编码
二进制模式与文本模式针对文件路径是否存在的情况下 规律是一样的!!!
文件操作的内置方法
1.read()
一次性读取文件内容 并且光标停留在文件末尾 继续读取则没有内容
并且当文件内容比较多的时候 该方法还可能会造成计算机内存溢出
括号内还可以填写数字 在文本模式下 表示读取几个字符
2.for循环
一行行读取文件内容 避免内存溢出现象的产生
3.readline()
一次只读一行内容
4.readlines()
一次性读取文件内容 会按照行数组织成列表的一个个数据值
['第一行\n', '第二行\n', '第三行\n'...]
5.readable()
fr = open("ysg.txt", "r")
print(fr.readable()) # Ture
判断文件是否具备读数据的能力
6.write()
写入数据
7.writeable()
判断文件是否具备写数据的能力
fa = open("ysg.txt", "a")
print(fa.writable()) # Ture
8.writelines()
接收一个列表 一次性将列表中所有的数据值写入
9.flush()
将内存中文件数据立刻刷到硬盘 等价于ctrl + s
fw = open("ysg.txt", "w")
fw.write('....\n')
fw.flush
文件内光标的移动
seek(offset, whence)
offset是位移量 以字节为单位
whence是模式 0 1 2
0 是基于文件开头(默认值)
文本和二进制模式都可以使用
1 是基于当前位置
只有二进制模式可以使用
2 是基于文件末尾
只有二进制模式可以使用
tell() 获取光标位置
f = open(r'F:\上海_脱产\test.txt', 'r', encoding='utf-8')
f.seek(4) #按照字节定义光标位置
print(f.tell()) # 4
数据修改(了解)
计算机硬盘修改数据的原理
硬盘写数据可以看成是在硬盘上刻字 一旦需要修改中间内容 则需要重新刻字
因为刻过的字不可能从中间再分开
硬盘删除数据的原理
不是直接删除而是改变状态 等待后续数据的覆盖才会被真正删除
文件内容修改
# 修改文件内容的方式1:覆盖写
with open(r'a.txt', 'r', encoding='utf8') as f:
data = f.read()
with open(r'a.txt', 'w', encoding='utf8') as f1:
f1.write(data.replace('jason', 'tony'))
# 修改文件内容的方式2:换地写
'''先在另外一个地方写入内容 然后将源文件删除 将新文件命名成源文件'''
import os
with open('a.txt', 'r', encoding='utf8') as read_f, \
open('.a.txt.swap', 'w', encoding='utf-8') as write_f:
for line in read_f:
write_f.write(line.replace('tony', 'kevinSB'))
os.remove('a.txt') # 删除a.txt
os.rename('.a.txt.swap', 'a.txt') # 重命名文件
作业
1.优化员工管理系统
拔高: 是否可以换成字典或者数据的嵌套使用完成更加完善的员工管理而不是简简单单的一个用户名(能写就写不会没有关系)
2.去重下列列表并保留数据值原来的顺序
eg: [1,2,3,2,1] 去重之后 [1,2,3]
l1 = [2,3,2,1,2,3,2,3,4,3,4,3,2,3,5,6,5]
3.有如下两个集合,pythons是报名python课程的学员名字集合,linuxs是报名linux课程的学员名字集合
pythons={'jason','oscar','kevin','ricky','gangdan','biubiu'}
linuxs={'kermit','tony','gangdan'}
1. 求出即报名python又报名linux课程的学员名字集合
2. 求出所有报名的学生名字集合
3. 求出只报名python课程的学员名字
4. 求出没有同时这两门课程的学员名字集合
4.统计列表中每个数据值出现的次数并组织成字典战士
eg: l1 = ['jason','jason','kevin','oscar']
结果:{'jason':2,'kevin':1,'oscar':1}
真实数据
l1 = ['jason','jason','kevin','oscar','kevin','tony','kevin']
5.编写简易版本的拷贝工具
自己输入想要拷贝的数据路径 自己输入拷贝到哪个地方的目标路径
任何类型数据皆可拷贝
ps:个别电脑 C盘文件由于权限问题可能无法拷贝 换其他盘尝试即可
6.利用文件充当数据库编写用户登录、注册功能
文件名称:userinfo.txt
基础要求:
用户注册功能>>>:文件内添加用户数据(用户名、密码等)
用户登录功能>>>:读取文件内用户数据做校验
ps:上述功能只需要实现一次就算过关(单用户) 文件内始终就一个用户信息
拔高要求:
用户可以连续注册
用户可以多账号切换登录(多用户) 文件内有多个用户信息
ps:思考多用户数据情况下如何组织文件内数据结构较为简单
提示:本质其实就是昨天作业的第二道题 只不过数据库由数据类型变成文件
解答
# 1.优化员工管理系统
info_dit = {} # {序号:{name:ysg,age:25,job:work,salary:8000}, 序号:{name:ysg,age:25,job:work,salary:8000}}
while 1:
info = '''
1.创建员工信息
2.查看单个员工信息
3.查看所有员工信息
4.修改员工信息
5.删除员工信息
'''
print(info)
num = input('输出需要执行的功能(q):').strip()
if num == 'q': break
if num == '1':
while 1:
id = input('输入员工编号(退出:q)>>>').strip().lower()
if id == 'q': break
if id in info_dit:
print('员工编号已存在,请重新输入')
continue
if not id.isdigit():
print('员工编号不为数字,请重新输入')
continue
name = input('请按照格式输入用户名:').strip()
age = input('请按照格式输入年龄:').strip()
job = input('请按照格式输入工作:').strip()
salary = input('请按照格式输入薪资:').strip()
info_dit[id] = {'name': name, 'age': age, 'job': job, 'salary': salary}
print('{} 信息录入成功'.format(name))
elif num == '2':
while 1:
id_name = input('输入员工编号或姓名(退出:q)>>>').strip().lower()
if id_name == 'q': break
if id_name.isdigit():
if id_name in info_dit:
info = '''
姓名:{}
年龄:{}
工作:{}
薪资:{}'''.format(
info_dit[id_name]['name'], info_dit[id_name]['age'], info_dit[id_name]['job'], info_dit[id_name]['salary'])
print(info)
else:
print('员工不存在')
else:
for val in info_dit.values():
if id_name == val['name']:
info = '''
姓名:{}
年龄:{}
工作:{}
薪资:{}'''.format(
val['name'], val['age'], val['job'], val['salary'])
print(info)
break
print('员工不存在')
elif num == '3':
print('打印所有员工信息')
for key, val in info_dit.items():
info = '''编号:{} ,姓名:{} ,年龄:{}, 工作:{}, 薪资:{}'''.format(
key, val['name'], val['age'], val['job'], val['salary'])
print(info)
elif num == '4':
while 1:
id_name = input('输入员工编号或姓名(退出:q)>>>').strip().lower()
if id_name == 'q': break
if id_name.isdigit():
if id_name in info_dit:
name = input('请按照格式输入用户名:').strip()
age = input('请按照格式输入年龄:').strip()
job = input('请按照格式输入工作:').strip()
salary = input('请按照格式输入薪资:').strip()
info_dit[id_name] = {'name': name, 'age': age, 'job': job, 'salary': salary}
else:
print('员工不存在')
else:
for key, val in info_dit.items():
if id_name == val['name']:
name = input('请按照格式输入用户名:').strip()
age = input('请按照格式输入年龄:').strip()
job = input('请按照格式输入工作:').strip()
salary = input('请按照格式输入薪资:').strip()
info_dit[key] = {'name': name, 'age': age, 'job': job, 'salary': salary}
break
elif num == '5':
while 1:
id_name = input('输入删除员工编号或姓名(退出:q)>>>').strip().lower()
if id_name == 'q': break
if id_name.isdigit():
if id_name in info_dit:
info_dit.pop(id_name)
print('用户序号 {} 已删除'.format(id_name))
else:
for key, val in info_dit.items():
if id_name == val['name']:
info_dit.pop(key)
print('用户 {} 已删除'.format(id_name))
break
else:
print('暂无该功能')
# 2.去重下列列表并保留数据值原来的顺序
lit = [2, 3, 2, 1, 2, 3, 2, 3, 4, 3, 4, 3, 2, 3, 5, 6, 5]
new_lit = list(set(lit))
new_lit.sort(key=lit.index)
print(new_lit)
# 3.有如下两个集合,pythons 是报名 python 课程的学员名字集合,linuxs 是报名 linux 课程的学员名字集合
pythons={'jason','oscar','kevin','ricky','gangdan','biubiu'}
linuxs={'kermit','tony','gangdan'}
# 1. 求出即报名 python 又报名 linux 课程的学员名字集合
# intersection
print(pythons.intersection(linuxs)) # {'gangdan'}
# 2. 求出所有报名的学生名字集合
# union
print(pythons | linuxs) # {'ricky', 'gangdan', 'kermit', 'tony', 'kevin', 'biubiu', 'jason', 'oscar'}
# 3. 求出只报名 python 课程的学员名字
# difference
print(pythons - linuxs) # {'ricky', 'jason', 'oscar', 'kevin', 'biubiu'}
# 4. 求出没有同时这两门课程的学员名字集合
# symmetric_difference 反交集
print(pythons ^ linuxs) # {'ricky', 'kevin', 'biubiu', 'jason', 'tony', 'kermit', 'oscar'}
# 4.统计列表中每个数据值出现的次数并组织成字典战士
lit = ['jason','jason','kevin','oscar','kevin','tony','kevin']
dit = {}
for i in lit:
dit[i] = lit.count(i)
print(dit) # {'kevin': 3, 'jason': 2, 'oscar': 1, 'tony': 1}
from collections import Counter
b = dict(Counter(lit))
print(b) # {'kevin': 3, 'jason': 2, 'oscar': 1, 'tony': 1}
5.编写简易版本的拷贝工具
target_path = input('输入源文件路径>>>')
save_path = input('输入文件保存路径>>>')
with open(r'%s' % target_path, 'rb') as frb, open(r'%s' % save_path, 'wb') as fwb:
for line in frb:
fwb.write(line)
6.利用文件充当数据库编写用户登录、注册功能
while 1:
info = '''
1.注册功能
2.登录功能
'''
print(info)
num = input('请输入功能序号(q:退出):').strip().lower()
if num == 'q': break
if num == '1':
with open(r'F:\上海_脱产\userinfo.txt', 'a', encoding='utf-8') as fa:
name = input('请输入用户名:').strip()
pawd = input('请输入密码:').strip()
age = input('请输入年龄:').strip()
job = input('请输入工作:').strip()
line = '{}|{}|{}|{}\n'.format(name, pawd, age, job)
fa.write(line)
elif num == '2':
print('开始登录')
with open(r'F:\上海_脱产\userinfo.txt', 'r', encoding='utf-8') as fa:
total = {num: line.split('|') for num, line in enumerate(fa.read().splitlines()) if num > 0}
name = input('请输入用户名:').strip()
pawd = input('请输入密码:').strip()
for val in total.values():
if name == val[0] and pawd == val[1]:
info = '''
登录成功!
用户名:{}
年龄:{}
工作:{}
'''.format(val[0], val[2], val[3])
print(info)
else:
print('无此功能')