• day10.字符编码


    一 了解字符编码的知识储备

    python解释器执行py文件的原理 ,例如python test.py

      第一阶段:python解释器启动,此时就相当于启动了一个文本编辑器

      第二阶段:python解释器相当于文本编辑器,去打开test.py文件,从硬盘上将test.py的文件内容读入到内存中(小复习:pyhon的解释性,决定了解释器只关心文件内容,不关心文件后缀名)

      第三阶段:python解释器解释执行刚刚加载到内存中test.py的代码( ps:在该阶段,即执行时,才会识别python的语法,执行文件内代码,执行到name="egon",会开辟内存空间存放字符串"egon")

    二 什么是字符编码

      计算机要想工作必须通电,也就是说‘电’驱使计算机干活,而‘电’的特性,就是高低电平(高低平即二进制数1,低电平即二进制数0),也就是说计算机只认识数字

      很明显,我们平时在使用计算机时,用的都是人类能读懂的字符(用高级语言编程的结果也无非是在文件内写了一堆字符),如何能让计算机读懂人类的字符?

      必须经过一个过程:

      字符--------(翻译过程)------->数字 

      这个过程实际就是一个字符如何对应一个特定数字的标准,这个标准称之为字符编码。

      以下两个场景下涉及到字符编码的问题:

        1. 一个python文件中的内容是由一堆字符组成的(python文件未执行时)(将py文本加载到解释器--类似于文本编辑器)

        2. python中的数据类型字符串是由一串字符组成的(python文件执行时)

    三 字符编码的发展史

    阶段一:现代计算机起源于美国,最早诞生也是基于英文考虑的ASCII

      ASCII:一个Bytes代表一个字符

      1Bytes=8bit,8bit可以表示0-2**8-1种变化,即可以表示256个字符

    阶段二:为了满足中文,中国人定制了GBK

      GBK:2Bytes代表一个字符

      为了满足其他国家,各个国家纷纷定制了自己的编码

      日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr

    阶段三:各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

      于是产生了unicode, 统一用2Bytes代表一个字符, 2**16-1=65535,可代表6万多个字符,因而兼容万国语言

      但对于通篇都是英文的文本来说,这种编码方式无疑是多了一倍的存储空间(二进制最终都是以电或者磁的方式存储到存储介质中的)

      于是产生了UTF-8,对英文字符只用1Bytes表示,对中文字符用3Bytes

    需要强调的一点是:

      unicode:简单粗暴,所有字符都是2Bytes,优点是字符->数字的转换速度快,缺点是占用空间大

      utf-8:精准,对不同的字符用不同的长度表示,优点是节省空间,缺点是:字符->数字的转换速度慢,因为每次都需要计算出字符需要多长的Bytes才能够准确表示

    所有程序,最终都要加载到内存,程序保存到硬盘不同的国家用不同的编码格式,但是到内存中我们为了兼容万国(计算机可以运行任何国家的程序原因在于此),统一且固定使用unicode,这就是为何内存固定用unicode的原因,你可能会说兼容万国我可以用utf-8啊,可以,完全可以正常工作,之所以不用肯定是unicode比utf-8更高效啊(uicode固定用2个字节编码,utf-8则需要计算),但是unicode更浪费空间,没错,这就是用空间换时间的一种做法,而存放到硬盘,或者网络传输,都需要把unicode转成utf-8,因为数据的传输,追求的是稳定,高效,数据量越小数据传输就越靠谱,于是都转成utf-8格式的,而不是unicode。
    View Code

    五 字符编码的使用

    5.1 文本编辑器一锅端

      unicode----->encode-------->utf-8

      utf-8-------->decode---------->unicode

    5.1.2 文本编辑器nodpad++

     

    分析过程?什么是乱码

      文件从内存刷到硬盘的操作简称存文件

      文件从硬盘读到内存的操作简称读文件

    原因一:存文件时就已经乱码

    存文件时,由于文件内有各个国家的文字,我们单以shiftjis去存,

    本质上其他国家的文字由于在shiftjis中没有找到对应关系而导致存储失败,

    但当我们用文件编辑器去存的时候,编辑器会帮我们做转换,保证中文也能用shiftjis存储(硬存,必然乱码),

    这就导致了,存文件阶段就已经发生乱码

    此时当我们用shiftjis打开文件时,日文可以正常显示,而中文则乱码了

    再或者,存文件时:

    f=open('a.txt','wb')
    
    f.write('何を見て
    '.encode('shift_jis'))
    f.write('你愁啥
    '.encode('gbk'))
    f.write('你愁啥
    '.encode('utf-8'))
    f.close()

    以任何编码打开文件a.txt都会出现其余两个无法正常显示的问题

    原因二:存文件时不乱码而读文件时乱码

    存文件时用utf-8编码,保证兼容万国,不会乱码,而读文件时选择了错误的解码方式,比如gbk,则在读阶段发生乱码,读阶段发生乱码是可以解决的,选对正确的解码方式就ok了,而存文件时乱码,则是一种数据的损坏。

    5.1.3 文本编辑器pycharm

    以gbk格式保存

    以utf-8格式打开(reload)

    reload与convert的区别:

    pycharm非常强大,提供了自动帮我们convert转换的功能,即将字符按照正确的格式转换

    要自己探究字符编码的本质,还是不要用这个

    我们选择reload,即按照某种编码重新加载文件

    总结:

    无论是何种编辑器,要防止文件出现乱码(请一定注意,存放一段代码的文件也仅仅只是一个普通文件而已,此处指的是文件没有执行前,我们打开文件时出现的乱码)

    核心法则就是,文件以什么编码保存的,就以什么编码方式打开

    5.1.4 文本编辑器之python解释器

    文件test.py以gbk格式保存,内容为:

      x='林'

    无论是

      python2 test.py

    还是

      python3 test.py

    都会报错(因为python2默认ascii,python3默认utf-8)

    除非在文件开头指定#coding:gbk

    5.2 程序的执行

    python test.py   (我再强调一遍,执行test.py的第一步,一定是先将文件内容读入到内存中)

    阶段一:启动python解释器

    阶段二:python解释器此时就是一个文本编辑器,负责打开文件test.py,即从硬盘中读取test.py的内容到内存中

    此时,python解释器会读取test.py的第一行内容,#coding:utf-8,来决定以什么编码格式来读入内存,这一行就是来设定python解释器这个软件的编码使用的编码格式这个编码,

    可以用sys.getdefaultencoding()查看,如果不在python文件指定头信息#-*-coding:utf-8-*-,那就使用默认的

    python2中默认使用ascii,python3中默认使用utf-8

    阶段三:读取已经加载到内存的代码(unicode编码的二进制),然后执行,执行过程中可能会开辟新的内存空间,比如x="egon"

    内存的编码使用unicode,不代表内存中全都是unicode编码的二进制,

    在程序执行之前,内存中确实都是unicode编码的二进制,比如从文件中读取了一行x="egon",其中的x,等号,引号,地位都一样,都是普通字符而已,

    都是以unicode编码的二进制形式存放与内存中的

    但是程序在执行过程中,会申请内存(与程序代码所存在的内存是俩个空间),可以存放任意编码格式的数据,比如x="egon",会被python解释器识别为字符串,会申请内存空间来存放"hello",然后让x指向该内存地址,此时新申请的该内存地址保存也是unicode编码的egon,如果代码换成x="egon".encode('utf-8'),那么新申请的内存空间里存放的就是utf-8编码的字符串egon了

    针对python3如下图

    浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器

    如果服务端encode的编码格式是utf-8, 客户端内存中收到的也是utf-8编码的二进制。

    5.3 python2与python3的区别

    5.3.1 在python2中有两种字符串类型str和unicode

    str类型

    当python解释器执行到产生字符串的代码时(例如s='林'),会申请新的内存地址,然后将'林'encode成文件开头指定的编码格式,这已经是encode之后的结果了,所以s只能decode

    1 #_*_coding:gbk_*_
    2 #!/usr/bin/env python
    3 
    4 x=''
    5 # print x.encode('gbk') #报错
    6 print x.decode('gbk') #结果:林

    所以很重要的一点是:

    在python2中,str就是编码后的结果bytes,str=bytes,所以在python2中,unicode字符编码的结果是str/bytes

    #coding:utf-8
    s='' #在执行时,'林'会被以conding:utf-8的形式保存到新的内存空间中
    
    print repr(s) #'xe6x9ex97' 三个Bytes,证明确实是utf-8
    print type(s) #<type 'str'>
    
    s.decode('utf-8')
    # s.encode('utf-8') #报错,s为编码后的结果bytes,所以只能decode

    unicode类型

    当python解释器执行到产生字符串的代码时(例如s=u'林'),会申请新的内存地址,然后将'林'以unicode的格式存放到新的内存空间中,所以s只能encode,不能decode

    s=u''
    print repr(s) #u'u6797'
    print type(s) #<type 'unicode'>
    
    
    # s.decode('utf-8') #报错,s为unicode,所以只能encode
    s.encode('utf-8') 

    打印到终端

    对于print需要特别说明的是:

    当程序执行时,比如

    x='林'

    print(x) #这一步是将x指向的那块新的内存空间(非代码所在的内存空间)中的内存,打印到终端,而终端仍然是运行于内存中的,所以这打印可以理解为从内存打印到内存,即内存->内存,unicode->unicode

    在windows终端

    但是在python2中存在另外一种非unicode的字符串,此时,print x,会按照终端的编码执行x.decode('终端编码'),变成unicode后,再打印,此时终端编码若与文件开头指定的编码不一致,乱码就产生了

    在pycharm中(终端编码为utf-8,文件编码为utf-8,不会乱码)

    在windows终端(终端编码为gbk,文件编码为utf-8,乱码产生)

    思考题:

    分别验证在pycharm中和cmd中下述的打印结果

    #coding:utf-8
    s=u'' #当程序执行时,'林'会被以unicode形式保存新的内存空间中
    
    
    #s指向的是unicode,因而可以编码成任意格式,都不会报encode错误
    s1=s.encode('utf-8')
    s2=s.encode('gbk')
    print s1 #打印正常否?
    print s2 #打印正常否
    
    
    print repr(s) #u'u6797'
    print repr(s1) #'xe6x9ex97' 编码一个汉字utf-8用3Bytes
    print repr(s2) #'xc1xd6' 编码一个汉字gbk用2Bytes
    
    print type(s) #<type 'unicode'>
    print type(s1) #<type 'str'>
    print type(s2) #<type 'str'>
    View Code

    5.3.2 在python3 中也有两种字符串类型str和bytes(字节码)

    str是unicode

    #coding:utf-8
    s='' #当程序执行时,无需加u,'林'也会被以unicode形式保存新的内存空间中,
    
    #s可以直接encode成任意编码格式
    s.encode('utf-8')
    s.encode('gbk')
    
    print(type(s)) #<class 'str'>

    bytes(字节码)是bytes

    #coding:utf-8
    s='' #当程序执行时,无需加u,'林'也会被以unicode形式保存新的内存空间中,
    
    #s可以直接encode成任意编码格式
    s1=s.encode('utf-8')
    s2=s.encode('gbk')
    
    
    
    print(s) #
    print(s1) #b'xe6x9ex97' 在python3中,是什么就打印什么
    print(s2) #b'xc1xd6' 同上
    
    print(type(s)) #<class 'str'>
    print(type(s1)) #<class 'bytes'>
    print(type(s2)) #<class 'bytes'>

    总结:

    unicode----- encode编码 ------utf8   
    utf8----- decode解码 ------unicode
    1.用什么方式编码,就要用什么方式解码
      ps:内存中固定用unicode,我们控制的编码是往硬盘存放或基于网络传输的编码
    2.数据最先产生于内存,格式为unicode,想要传输或存储必须先编码(encode)转换成bytes格式
    3.py3中字符串默认为unicode,它可以编码(encode)为bytes(字节串)
     bytes(字节串)可以解码(decode)成unicode
    4.py2中字符串默认为bytes(字节串)
     py2的字符串前加u,就是unicode类型
  • 相关阅读:
    Scalaz(3)- 基础篇:函数概括化-Generalizing Functions
    Scalaz(2)- 基础篇:随意多态-typeclass, ad-hoc polymorphism
    Scalaz(1)- 基础篇:隐式转换解析策略-Implicit resolution
    Scalaz(0)
    泛函编程(38)-泛函Stream IO:IO Process in action
    泛函编程(37)-泛函Stream IO:通用的IO处理过程-Free Process
    泛函编程(36)-泛函Stream IO:IO数据源-IO Source & Sink
    泛函编程(35)-泛函Stream IO:IO处理过程-IO Process
    javascript基础
    jsp联合javascript操作html
  • 原文地址:https://www.cnblogs.com/maxiaotiaoshishui/p/7230536.html
Copyright © 2020-2023  润新知