字符编码与文件处理
字符编码
前提摘要
运行python程序的三个步骤:
1. 先启动python解释器 2. 再将python文件当作普通的文本文件读入内存 3. 解释执行读入内存的代码,开始识别语法
重点理论:
1、 编码与解码:
字符---编码-->unicode的二进制-------编码----->GBK的二进制
GBK的二进制-----解码-->unicode的二进制----解码->字符
2 、解决乱码问题的核心法则:
字符用什么编码格式编码的,就应该用什么编码格式进行解码
3、python解释器默认的字符编码 python2:ASCII python3:UTF-8
通过文件头可以修改python解释器默认使用的字符编码 在文件首行写:#coding:文件当初存的时候用的字符编码
针对python2解释器中定义字符串应该:
x=u"上" 对于python3解释即便是x="上"不加u前缀也是存成unicode
在python3中
x='上' # '上'存成了uncidoe
unicode--------encode----------->gbk
res=x.encode('gbk') #res是gbk格式的二进制,称之为bytes类型
gbk(bytes类型)-------decode---------->unicode
y=res.decode('gbk') #y就是unicode
关于字符编码的操作:
1. 编写python文件,首行应该加文件头:#coding:文件存时用的编码
2. 用python2写程序,定义字符串应该加前缀u,如x=u'上'
3. python3中的字符串都是unicode编码的,python3的字符串encode之后可以得到bytes类型
unicode 分为 utf-32(占4个字节),utf-16(占两个字节),utf-8(占1-4个字节), so utf-16就是现在最常用的unicode版本, 不过在文件里存的还是utf-8,因为utf8省空间
1. 以什么编码存的就要以什么编码取出
内存固定使用unicode编码,我们可以控制的编码是往硬盘存放或者基于网络传输选择编码
2 .数据是最先产生于内存中,是unicode格式,要想传输需要转成bytes格式
#unicode----->encode(utf-8)------>bytes
拿到bytes,就可以往文件内存放或者基于网络传输
#bytes------>decode(gbk)------->unicode
3.python3中字符串被识别成unicode
python3中的字符串encode得到bytes
4 .python2中的字符串就bytes ,python2中在字符串前加u,就是unicode
第一阶段:
打开python解释器,notepad++,pychar 加载到内存
第二阶段:
写一个文件就是
内存-endoce-bytes(二进制)
显示文件就是:
硬盘-decode-unicode(万国码)
第三阶段:
执行阶段,针对的是内部定义新的内存空间,例如变量
数据是最先产生于内存中,是unicode格式,要想传输需要转成bytes格式
unicode----->encode(utf-8)------>bytes
拿到bytes,就可以往文件内存放或者基于网络传输
bytes------>decode(gbk)------->unicode
python解释器在加载 .py 文件中的代码时,会对内容进行编码(默认ascill)
ASCII(American Standard Code for Information Interchange,美国标准信息交换代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言,其最多只能用 8 位来表示(一个字节),即:2**8 = 256-1,所以,ASCII码最多只能表示 255 个符号。
关于中文
为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的big5。
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。
GB2312 支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。2000年的 GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030,对嵌入式产品暂不作要求。所以手机、MP3一般只支持GB2312。
从ASCII、GB2312、GBK 到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。
有的中文Windows的缺省内码还是GBK,可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符,普通人是很难用到的,通常我们还是用GBK指代中文Windows内码。
显然ASCII码无法将世界上的各种文字和符号全部表示,所以,就需要新出一种可以代表所有字符和符号的编码,即:Unicode
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,规定虽有的字符和符号最少由 16 位来表示(2个字节),即:2 **16 = 65536,
注:此处说的的是最少2个字节,可能更多
UTF-8,是对Unicode编码的压缩和优化,他不再使用最少使用2个字节,而是将所有的字符和符号进行分类:ascii码中的内容用1个字节保存、欧洲的字符用2个字节保存,东亚的字符用3个字节保存...
所以,python解释器在加载 .py 文件中的代码时,会对内容进行编码(默认ascill)
文件操作
1 什么是文件
文件是操作系统提供给用户/应用程序的一种虚拟单位,该虚拟单位直接映射的是硬盘空间
2 为何要处理文件
用户/应用程序直接操作文件(读/写)就被操作系统转换成具体的硬盘操作,从而实现
用户/应用程序将内存中的数据永久保存到硬盘中
3 如何用文件
文件处理的三个步骤:
f=open(r'c.txt',mode='r',encoding='utf-8') 文件对象(应用程序的内存资源)======》操作系统打开的文件(操作系统的内存资源)
print(f)
data=f.read()
f.close() 向操作系统发送信号,让操作系统关闭打开的文件,从而回收操作系统的资源
注:在同一个文件目录下的话,文件路径可以填相对路径(文件名),默认在当前文件夹查找; 文件路径前面的 r 是将文件路径中可能被转义的字符转换成原生字符串。
操作文件内容的模式:
控制操作文件内容格式的两种模式:t(默认的) b
大前提: tb模式均不能单独使用,必须与纯净模式结合使用
t文本模式:
1. 读写文件都是以字符串为单位的
2. 只能针对文本文件
3. 必须指定encoding参数
b二进制模式:
1.读写文件都是以bytes/二进制为单位的
2. 可以针对所有文件
3. 一定不能指定encoding参数
r模式:只读模式
在文件不存在时则报错,如果文件存在文件指针跳到文件的开头
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open(r'c.txt',mode='rt',encoding='utf-8') as f: print(f.read()) print(f.readable()) print(f.writable()) f.write('hello') # 只能读 data=f.read() print(data,type(data)) with open(r'c.txt',mode='rb') as f: data=f.read() print(data,type(data)) res=data.decode('utf-8') print(res)
上下文管理:
with open(r'c.txt',mode='r',encoding='utf-8') as f,open(r'b.txt',mode='r',encoding='utf-8') as f1:
pass
循环读文件内容的方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open(r'c.txt',mode='rt',encoding='utf-8') as f: for line in f: print(line,end='')
w: 只写模式
1 当文件不存时,新建一个空文档
2 当文件存在时,清空文件内容,文件指针跑到文件的开头
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open('c.txt',mode='wt',encoding='utf-8') as f: print(f.readable()) print(f.writable()) print(f.read()) f.write('哈哈哈\n') f.write('你愁啥\n') f.write('瞅你咋地\n') f.write('1111\n2222\n333\n4444\n') info=['egon:123\n','alex:456\n','lxx:lxx123\n'] for line in info: f.write(line) f.writelines(info) with open('c.txt',mode='rb') as f: print(f.read()) with open('c.txt',mode='wb') as f: f.write('哈哈哈\n'.encode('utf-8')) f.write('你愁啥\n'.encode('utf-8')) f.write('瞅你咋地\n'.encode('utf-8'))
a: 只追加写模式
1 当文件不存时,新建一个空文档,文件指针跑到文件的末尾
2 当文件存在时,文件指针跑到文件的末尾
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open('c.txt',mode='at',encoding='utf-8') as f: print(f.readable()) print(f.writable()) f.read() f.write('虎老师:123\n') 在文件打开不关闭的情况下,连续的写入,下一次写入一定是基于上一次写入指针的位置而继续的 with open('d.txt',mode='wt',encoding='utf-8') as f: f.write('虎老师1:123\n') f.write('虎老师2:123\n') f.write('虎老师3:123\n') with open('d.txt',mode='wt',encoding='utf-8') as f: f.write('虎老师4:123\n') with open('d.txt',mode='at',encoding='utf-8') as f: f.write('虎老师1:123\n') f.write('虎老师2:123\n') f.write('虎老师3:123\n') with open('d.txt',mode='at',encoding='utf-8') as f: f.write('虎老师4:123\n')
文本操作中的指针问题
大前提:文件内指针的移动是Bytes为单位的,唯独t模式下的read读取内容个数是以字符为单位
f.read() 无参数,读全部,有b按字节,无b按字符
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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'))
0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用
0模式详解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open('a.txt',mode='rt',encoding='utf-8') as f: f.seek(4,0) print(f.tell()) print(f.read()) with open('a.txt',mode='rb') as f: f.seek(4,0) f.seek(2,0) print(f.tell()) print(f.read().decode('utf-8')) with open('a.txt',mode='rt',encoding='utf-8') as f: f.seek(5,0) print(f.read())
1模式详解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open('a.txt',mode='rb') as f: f.seek(3,1) print(f.tell()) f.seek(4,1) print(f.tell()) print(f.read().decode('utf-8'))
2模式详解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open('a.txt',mode='rb') as f: f.seek(-9,2) 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='')
文件修改
须知一:
硬盘空间无法修改,硬盘中的数据更新都是用新的内容覆盖旧的内容内存控制可以修改
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open('a.txt','r+t',encoding='utf-8') as f: f.seek(4,0) print(f.tell()) f.write('我擦嘞')
须知二:
文件对应的是硬盘空间,硬盘不能修改应为文件本质也不能修改,我们看到文件的内容可以修改,是如何实现的呢?大的的思路:将硬盘中文件内容读入内存,然后在内存中修改完毕后再覆盖回硬盘具体的实现方式分为两种:
1. 将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
优点: 在文件修改过程中同一份数据只有一份
缺点: 会过多地占用内存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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. 以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名
优点: 不会占用过多的内存
缺点: 在文件修改过程中同一份数据存了两份
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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')
常用的文件操作大全(包括指针):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
windows里面的换行符实际是\r\n "+" 表示可以同时读写某个文件 r+,可读写文件。【可读;可写;可追加】 w+,写读 a+,同a "U"表示在读取时,可以将 \r \n \r\n自动转换成 \n (与 r 或 r+ 模式同使用) rU r+U 打开文件 f = open('db','r',encoding="utf-8") encoding="utf-8",就是python默认帮你二进制转换成字符串 f = open('db','w') 先清空,后写入 f = open('db','x') 文件存在报错,不存在创建写内容 f = open('db','a') 追加 f = open('db','rb') 不用python处理,我直接跟0101打交到 data = f.read() print(data,type(data)) 硬盘存的二进制,给我们显示的是字符串,那是不是有一个二 进制转换成字符串的过程 f.close() f = open('db','ab') 字节追加 f.write(bytes("hello",encoding='utf-8')) 将字符串转为字节,并且以utf-8写入 f.close() r+指针变化 f = open('db','r+',encoding="utf-8") 因为指针问题,默认读到了最后,如果你定义了读几个字符,那么指针会在第几个字 符那里,但是你要追加写的话就是从最后追加 read 将指针调到某个字符的位置,如果是rb打开就是字节的位置 data = f.read(1) 一个字符 print(f.tell()) 位置就是3,按字节来的 print(data) seek 就是按照字节的位置,并且是覆盖原来的字节位置,如果是中文的话,seek1就会给你劈开 �111111 乱码了 f.seek(1) print(f.tell()) 获取当前指针的位置 f.write('1111') f.close() a+ 无论你怎么调指针的位置,都是最后结尾处加内容 w+ 先清空,在写 需要注意的是,如果该文件被打开或者使用'a'或'a+'追加,任何seek()操作将在下次写撤消 在python中的某些时候,我们需要判断指针是否可以进行移动,而实现这样的需求需要采用seekable()方法。如果可以移动,返回True,如果不可以移动,返回False # seekable()方法,判断当前文件指针是否可以移动 print(f.seekable()) 操作文件 f = open('db','r+',encoding="utf-8") f.read() 无参数,读全部,有b按字节,无b按字符 print(f.tell()) 获取当前指针位置 字节 f.seek() 调转到哪个位置 字节 f.write() 写数据,有b就是写字节,无b就是字符 f.close() 关闭文件 f.flush() 强刷到硬盘,之前是存在缓冲区,等待close时候,才会刷到硬盘 f.fileno() 文件描述符,用于后面的socker f.readable() 判断是否可读,跟模式有关 f.seekable() 判断是否可以移动指针 f.writable() 判断是否可写 f.readline() 读取一行,这时候指针换行了 f.readlines() 全部读取存为列表 f.truncate() 截断数据,将指针后面的清空 for line in f: for 循环文件对象,一行一行 print(line)