字符编码、文件处理、指针移动
目录:
字符编码
字符编码:
储备知识点:
1. 计算机系统分为三层:
应用程序
操作系统
计算机硬件
2. 运行python程序的三个步骤
1. 先启动python解释器
2. 再将python文件当作普通的文本文件读入内存
3. 解释执行读入内存的代码,开始识别语法
代码是在编辑器写的,不存在任何语法。 边写边存到内存(Unicode编码),然后再通过解码显示到显示屏。从内存刷到硬盘时候,是从Unicode转换成你要保存的格式(GBK)。 从硬盘读取出来的时候,先通过保存的格式(GBK)解码成Unicode到内存中,再用Unicode解码成原先写的字符。
|
字符编码
1. 什么是字符编码
字符编码表: 人类的字符<------------>数字
1Bytes=8bit
1B=8b 1字节等于8个二进制位
常用的编码表:
ASCII码:只能识别英文字符,1英文字符=8bit=1Bytes
用8个二进制bit(比特位)位表示一个英文字符
GBK:能识别汉字与英文,1汉字=16bit=2Bytes,1英文字符=8bit=1Bytes
unicode:能够识别万国字符,1字符=2Bytes=16bit
两大特点:
1. 能够兼容万国字符
2. 与各个国家的字符编码都有映射关系
utf-8:是unicode的转换格式,在往硬盘存储时从unicode精简为utf-8,即从16bit转换为8bit,节省存储空间来提高运行速度。
1个英文字符=1Bytes 1汉字=3Bytes
字符串类型演变:
python2有两种"字符串"相关类型: 种类一 #coding:gbk x='上' # '上'存成了GBK编码后的二进制 种类二: x=u'上' # '上'存成了unicode编码后的二进制 python3有两种"字符串"相关类型 x='上' # '上'存成了unicode编码后的二进制 res=x.encode('gbk') #gbk格式的二进制
重点理论:
1 编码与解码:
字符(输入)---编码-->unicode的二进制(存内存)-------编码----->GBK的二进制(存硬盘)
GBK的二进制(硬盘)-----解码-->unicode的二进制(内存)----解码->字符(显示器)
2 解决乱码问题的核心法则:
字符用什么编码格式编码的,就应该用什么编码格式进行解码
3 python解释器默认的字符编码
python2:ASCII(程序加载到内存中的时候都是unicode,开始识别语法时候会定义字符串变量,就会继续在内存中开辟空间,会使用ASCII编码)
python3:Utf-8(Python3中,定义字符串变量时候,默认使用Unicode编码,和内存默认编码相同)
通过文件头可以修改python解释器默认使用的字符编码
在文件首行写:#coding:文件当初存的时候用的字符编码
-------------------------------------------------------------------------------------------------
针对python2解释器中定义字符串应该:
x=u"上"
对于python3解释即便是x="上"不加u前缀也是存成unicode
-------------------------------------------------------------------------------------------------
在python3中:
x='上' #上存成了unicode
unicode--------encode(编码)----------->gbk(存储到硬盘时候转换成gbk)
res=x.encode('gbk') #res是gbk格式的二进制,称之为bytes类型
gbk(bytes类型)-------decode(解码)---------->unicode
y=res.decode('gbk') #y就是unicode
-------------------------------------------------------------------------------------------------
关于字符编码的操作(结论):
1. 编写python文件,首行应该加文件头:#coding:文件存时用的编码(不用担心Python程序运行的前两个阶段,不会出现乱码)
2. 用python2写程序,定义字符串应该加前缀u,如x=u'上'
3. python3中的字符串都是unicode编码的,python3的字符串encode之后可以得到bytes类型
2. 为何字符要编码
人类与计算机打交道用的都是人类的字符,而计算机无法识别人类的字符,只能识别
二进制,所以必须将人类的字符编码成计算机能识别的二进制数字. 3. 如何用字符编码
文件处理:
1 什么是文件
文件是操作系统提供给用户/应用程序的一种虚拟单位,该虚拟单位直接映射的是硬盘空间
2 为何要处理文件
用户/应用程序直接操作文件(读/写)就被操作系统转换成具体的硬盘操作,从而实现
用户/应用程序将内存中的数据永久保存到硬盘中
3 如何用文件
文件处理的三个步骤:
f=open(r'c.txt',mode='r',encoding='utf-8') #打开文件,得到文件对象,f是文件对象(应用程序的内存资源)--》操作系统打开的文件(操作系统的内存资源)
# print(f)
data=f.read() #读写操作
f.close() #向操作系统发送信号,让操作系统关闭打开的文件,从而回收操作系统的资源
上下文管理:(使用这种打开方式不用自己去关闭文件,在程序结束时会自动关闭文件)
with open(r'c.txt',mode='r',encoding='utf-8') as f,open(r'b.txt',mode='r',encoding='utf-8') as f1:
读写文件的操作
pass
大前提: tb模式均不能单独使用,必须与纯净模式结合使用
操作文件内容的模式:t(默认),b
t(默认的):操作文件内容都是以字符串为单位,会自动帮我们解码,必须指定encoding参数(解码时候没人告诉计算机用什么解码,而window系统默认是GBK编码,所以要指定)
b: 操作文件内容都是以Bytes(二进制)为单位,硬盘中存的时什么就取出什么,一定不能指定encoding参数
t文本模式:
1. 读写文件都是以字符串为单位的
2. 只能针对文本文件
3. 必须指定encoding参数
b二进制模式:
1.读写文件都是以bytes/二进制为单位的
2. 可以针对所有文件
3. 一定不能指定encoding参数
总结:t模式只能用于文本文件,而b模式可以用于任意文件(如图片,视频,文本)
文件打开的三种纯净模式:r(默认的) w a
一、r模式:只读模式,在文件不存在时则报错,如果文件存在文件指针跳到文件的开头
with open(r'c.txt',mode='rt',encoding='utf-8') as f:(红色r代表原生字符,没有转义,可以避免\n被当成换行符号)
print(f.read())
print(f.readable())
print(f.writable())
f.write('hello') # 报错,只能读
data=f.read()
print(data,type(data))#str
with open(r'c.txt',mode='rb') as f: data=f.read() #b模式 print(data,type(data)) #b'jinrui:123\r\nxuechengeng:12345\r\negon:1234\r\nalex:alex123' <class 'bytes'> res=data.decode('utf-8') print(res)
with open(r'c.txt',mode='rt',encoding='utf-8') as f:
line=f.readline()
print(line,end='')
line1=f.readline() #一行一行的读,读完文件没有关闭的话,指针会跳到下一行的开头;如果文件关闭的话指针重新回到起点。
print(line1,end='')
line2 = f.readline()
print(line2,end='')
lines=f.readlines() #一次读多行
print(lines)
循环读文件内容的方法:
with open(r'c.txt',mode='rt',encoding='utf-8') as f: #f是文件对象 for line in f: print(line,end='*')
# egon:123
*alex:456
*lxx:789* #正常的\n有换行,print也有换行,但是end('=')覆盖了\n,?
二、w模式:只写模式 在文件不存在时会创建空文档,文件存在会清空文件,文件指针跑到文件开头
with open('b.txt',mode='wt',encoding='utf-8') as f:
print(f.writable())
print(f.readable())
f.write('你好\n')
f.write('我好\n') # 强调:在文件不关闭的情况下,后写的内容一定跟着前写内容的后面
f.write('大家好\n')
f.write('111\n222\n333\n')
lines=['1111','22222','33333']
for line in lines:
f.write(line) #11112222233333。没有\n不会换行;
f.writelines(lines) #不是写很多行的意思,而是上面for循环的简写模式,没有\n不会换行
三、a模式:只追加写模式 在文件不存在时会创建空文档,文件存在会将文件指针直接移动到文件末尾,追加内容
# r+ w+ a+,读写模式
with open('a.txt',mode='r+t',encoding='utf-8') as f:
print(f.readable())
print(f.writable())
print(f.readline())
f.write('你好啊')
with open('d.txt','wb') as f:
f.write('你好'.encode('gbk')) #在d.txt文件中写入‘你好’,用gbk编码,所以要查看必须用gbk模式查看
实现功能代码:
注册:
1 name = input('用户名:') 2 pwd = input('密码:') 3 with open('db.txt',mode='at',encoding='utf-8') as f: 4 info = '%s:%s\n'%(name,pwd) 5 f.write(info) 6 print('注册成功')
登录:
1 # coding:utf-8 2 name = input('请输入用户名:>').strip() 3 pwd = input('请输入密码:>').strip() 4 with open(r'c.txt', mode='rt', encoding='utf-8') as f: 5 for line in f: 6 n, p = line.strip('\n').split(':') 7 if name == n and pwd == p: 8 print('登录成功') 9 break 10 else: 11 print('账号或密码错误')
拷贝文件:
1 src_file=input('源文件路径:') 2 drc_file=input('目标路径:') 3 with open(r'%s'%src_file,mode='rb') as f,open(r'%s'%drc_file,mode='wb') as f1: 4 for line in f: 5 f1.write(line)
指针移动:
大前提:文件内指针的移动是Bytes为单位的,唯独t模式下的read(不是r模式)读取内容个数是以字符为单位
文本读取模式:
with open('a.txt',mode='rt',encoding='utf-8') as f:
data=f.read(3) #从开头读取三个字符
print(data)
字节读取模式:
with open('a.txt',mode='rb') as f:
data=f.read(3) #从开头读取三个字节
print(data.decode('utf-8'))
f.seek(指针移动的字节数,模式控制): 控制文件指针的移动,都是以字节为单位
模式控制: 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的 强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用
f.tell()查看文件指针当前距离文件开头的位置
# 0模式详解:文件开头
a.txt文件第一行内容:你a我擦嘞111
with open('a.txt',mode='rt',encoding='utf-8') as f: f.seek(4,0) #从文件开头,读取四个字节 print(f.tell()) print(f.read()) #从4开始往后读完
with open('a.txt',mode='rb') as f:
f.seek(4,0)
# f.seek(2,0) #报错,因为没有完整截到一个字符你,在decode时会报错
print(f.tell())
print(f.read().decode('utf-8'))
with open('a.txt',mode='rt',encoding='utf-8') as f:
f.seek(5,0) #5将我拆分了
print(f.read()) #报错,t模式下的read是以字符为单位
# 1模式详解:当前位置
with open('a.txt',mode='rb') as f:
f.seek(3,1)
print(f.tell()) #3
f.seek(4,1)
print(f.tell()) #7,正好是我字结束
print(f.read().decode('utf-8')) #可以正常打印
# 2模式详解:
with open('a.txt',mode='rb') as f: f.seek(-9,2) #-9是向前移动九位 data=f.read() print(data.decode('utf-8'))
打印登录日志:
# tail -f access.log
with open('access.log',mode='rb') as f:
f.seek(0,2)
while True:
line=f.readline()
if len(line) == 0:
# 没有内容
continue
else:
print(line.decode('utf-8'),end='')
文件的修改:
须知一:
硬盘空间无法修改,硬盘中的数据更新都是用新的内容覆盖旧的内容
内存内容可以修改
硬盘上直接覆盖:
with open('a.txt','r+t',encoding='utf-8') as f: f.seek(4,0) print(f.tell()) f.write('我擦嘞') #是直接将原位上的数据覆盖了,不存在插入
须知二:
文件对应的是硬盘空间,硬盘不能修改因为文件本质也不能修改
我们看到文件的内容可以修改,是如何实现的呢?
大的的思路:将硬盘中文件内容读入内存,然后在内存中修改完毕后再覆盖回硬盘
修改文件的两种方式:
1. 将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
优点: 在文件修改过程中同一份数据只有一份
缺点: 会过多地占用内存,如果文件过大,会造成内存溢出
with open('db.txt',mode='rt',encoding='utf-8') as f:
data=f.read()
with open('db.txt',mode='wt',encoding='utf-8') as f:
f.write(data.replace('kevin','SB'))
2. 以读的方式打开原文件,以写的方式打开一个临时文件,按行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件命名为原文件名
优点: 不会占用过多的内存
缺点: 在文件修改过程中同一份数据存了两份
import os
with open('db.txt',mode='rt',encoding='utf-8') as read_f,\
open('.db.txt.swap',mode='wt',encoding='utf-8') as wrife_f:
for line in read_f:#读一行
wrife_f.write(line.replace('SB','kevin'))#改一行,然后写进去
os.remove('db.txt')
os.rename('.db.txt.swap','db.txt')