字符编码与文件处理

字符编码

前提摘要

运行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模式:只读模式

在文件不存在时则报错,如果文件存在文件指针跳到文件的开头

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)
View Code

上下文管理:
with open(r'c.txt',mode='r',encoding='utf-8') as f,open(r'b.txt',mode='r',encoding='utf-8') as f1:
pass

循环读文件内容的方法:

with open(r'c.txt',mode='rt',encoding='utf-8') as f:
for line in f:
  print(line,end='')
View Code

w: 只写模式
1 当文件不存时,新建一个空文档
2 当文件存在时,清空文件内容,文件指针跑到文件的开头

with open('c.txt',mode='wt',encoding='utf-8') as f:
print(f.readable())
print(f.writable())
print(f.read())
f.write('哈哈哈
')
f.write('你愁啥
')
f.write('瞅你咋地
')

f.write('1111
2222
333
4444
')

info=['egon:123
','alex:456
','lxx:lxx123
']
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('哈哈哈
'.encode('utf-8'))
f.write('你愁啥
'.encode('utf-8'))
f.write('瞅你咋地
'.encode('utf-8'))
View Code

a: 只追加写模式
1 当文件不存时,新建一个空文档,文件指针跑到文件的末尾
2 当文件存在时,文件指针跑到文件的末尾

with open('c.txt',mode='at',encoding='utf-8') as f:
print(f.readable())
print(f.writable())
f.read()

f.write('虎老师:123
')


在文件打开不关闭的情况下,连续的写入,下一次写入一定是基于上一次写入指针的位置而继续的
with open('d.txt',mode='wt',encoding='utf-8') as f:
f.write('虎老师1:123
')
f.write('虎老师2:123
')
f.write('虎老师3:123
')

with open('d.txt',mode='wt',encoding='utf-8') as f:
f.write('虎老师4:123
')

with open('d.txt',mode='at',encoding='utf-8') as f:
f.write('虎老师1:123
')
f.write('虎老师2:123
')
f.write('虎老师3:123
')
with open('d.txt',mode='at',encoding='utf-8') as f:
f.write('虎老师4:123
')
View Code

文本操作中的指针问题

大前提:文件内指针的移动是Bytes为单位的,唯独t模式下的read读取内容个数是以字符为单位

f.read() 无参数,读全部,有b按字节,无b按字符

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'))
View Code
f.seek(指针移动的字节数,模式控制): 控制文件指针的移动
 
模式控制:
0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的

强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用
f.tell()查看文件指针当前距离文件开头的位置
 

0模式详解

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())
View Code

1模式详解

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'))
View Code

2模式详解

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='')
View Code

文件修改

须知一:
硬盘空间无法修改,硬盘中的数据更新都是用新的内容覆盖旧的内容内存控制可以修改

with open('a.txt','r+t',encoding='utf-8') as f:
  f.seek(4,0)
  print(f.tell())
  f.write('我擦嘞')
View Code

须知二:
文件对应的是硬盘空间,硬盘不能修改应为文件本质也不能修改,我们看到文件的内容可以修改,是如何实现的呢?大的的思路:将硬盘中文件内容读入内存,然后在内存中修改完毕后再覆盖回硬盘具体的实现方式分为两种:

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'))
View Code

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')
View Code

常用的文件操作大全(包括指针):

windows里面的换行符实际是


"+" 表示可以同时读写某个文件
r+,可读写文件。【可读;可写;可追加】
w+,写读
a+,同a

"U"表示在读取时,可以将 
 
 
自动转换成 
 (与 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)
View Code
原文地址:https://www.cnblogs.com/596014054-yangdongsheng/p/9673889.html