unicode 与 utf8 的关系

unicode 定义了统一的字符集, UTF8 则是一种编码 unicode 字符集的方式.

在 python2 中, str 类型, unicode 类型都是 basestring 的子类, 其中 str 类型就好比 C 语言中的字符串, unicode 类型就好比 C 语言中的宽字符串.

因为 utf8 的编码方式不使用 0x0 填充, 一串 utf8 字符流实际上也是一串合法的 c 字符流 (0x0 结尾) --- 也就是合法的 str 类型的字符流.

编程相关的编码问题

关于编程语言的编码问题, 可能会涉及到这么几个内容:

源文件自身的编码

编辑器/IDE 所理解的源文件的编码方式

编译器/解释器对源代码中字符常量的处理方式

下文我们会一一说明.

源文件自身的编码

源文件自身的编码是由谁决定的呢? 实际是我们自己决定的, 还记得保存文件时弹出的对话框吗? 对话框中有一个选项就是文件的格式, 可能大部分人没有关注, 如果我们不去 touch 选项的话, 那文件就以操作系统的默认编码格式存储了. 这在 Linux, MacOS 上是 utf8, 某些 windows 上是 gbk.

在 vim 中我们通过 set fileencoding=utf8 选项来告诉 vim 应该把文件保存成 utf8 格式 --- 也就是将每个字符按照其 utf8 中定义的 code point 存到磁盘上.

编辑器/IDE 所理解的源文件的编码方式

纯文本文件是没有 header 或者叫 meta 信息的 (请忽略 BOM 这个异类), 编辑器打开文本文件的时候实际上不知道他是什么格式, 只能自己根据文本内容猜测一下它的格式, 但是大部分编辑器是不干这个活的, 只是跟随操作系统的默认格式, 比如 Linux, MacOS 上默认的文本格式都是 utf8, 所以 vim, emacs 这些编辑器打开文本文件时就认为文件内容是 utf8 格式的; 而以前中文 windows 系统默认的文本格式是 gbk, 所以在 windows 系统中创建的文本文件, 到 Linux 系统中用 vim 打开会发现是乱码, 我们不得不手动执行一下 :e ++enc=gbk 告诉 vim 用 gbk 编码重新渲染这个文件.

我们刚刚用到了 "渲染" 这个词, 为什么这么说呢? 因为 vim 只是个编辑器, 他不负责文本在显示器上的显示, 负责将文本输出到显示器 (显卡) 的是你机器上的 X 服务程序, 而 vim 又是怎么和 X 服务程序通信的呢? 实际上这中间还有个 X 客户程序, X 客户程序是什么呢? 就是你打开 vim 的地方, 一般来说也就是 xterm, rxvt, Terminal 这些终端程序.

在我们使用 vim 编辑文本的时候, 所看到的结果其实全都是 vim 将结果输出到标准输出的过程, vim 的标准输出是连着 xterm 的, 所以文本文件的编辑过程就在 xterm 中为我们展现出来.

另外要注意的是, :e ++enc=gbk 只是告诉 vim, 这个文件的格式是 gbk, 这样一来 vim 才能够正确的理解该文件的格式, 但是在 vim 内部对字符的处理, vim 输出到标准输出 xterm, 以及 xterm 到 X 服务程序之间的通信, 全部无一例外走的是 utf8 格式, 这是 Linux 生态传统. 我们当然可以配置 xterm 的字符格式, 也可以更改 vim 内部处理字符的方式 (这两件事我没试过, 我猜是可以的, 至少通过改它们源码一定是可以的), 但这当然是强烈不推荐的.

也就是说, :e ++enc=gbk 之后, 只是告诉 vim, 在读入文件的时候按照 gbk 的编码来理解这些字符, 然后这每个被理解的字符在 vim 内部都会转换成对应的 utf8 编码的字符, 后续输出到标准输出 (xterm) 时输出的就是这些 utf8 字符, 接着 xterm 这边只要你没有闲的蛋疼改配置那它一定也是默认理解 utf8 字符的, xterm 会将这些字符, 字符的编码方式 (这里是 utf8), 以及采用的字体, 字号大小等信息拼成一张指令单, 告诉 X 服务程序, 让 X 服务程序按照这个指令单渲染这些字符到显示器上, X 服务程序就会解析出每个字符的 code point, 字号等信息, 然后到字库中找对应这个 code point 的 glyph, 正确的输出到显示器上.

如果你没有 :e ++enc=gbk , vim 自然会以为文本格式就是 utf8, 按照 utf8 的编码方式来理解文本内容, 结果巧了 gbk 的编码方式被解读城 utf8 的话, code point 正好落入了 utf8 编码的乱码区, 于是这些乱码信息就被 vim 传给了 xterm 进而传给了 X 服务程序.

上面是输出过程, 下面我们看一看输入过程.

明白了输出过程, 输入过程就很好理解了, 你在 XIM 软件 (输入法软件) 里打字, 一般会有选字框的对吧, 选好字 (这个过程在写输入法的人口中被称作 "上屏幕") 之后, 这些字的 code point 就被发送给 xterm, 还记得吗 Linux 生态系统 utf8 是官定编码格式, XIM 软件也是 X 客户程序之一, 所以发给 xterm 的 code point 也是 utf8 格式. xterm 收到这些 code point 之后给谁呢? 当然是经由标准输入给到了 vim, 然后后面的事情就和上面一样了: vim 将这些字符通过标准输出给回到 xterm, 进而给到 X 服务程序渲染到显示器上.

可以看到, 只要你不要闲的没事瞎折腾 X 系统的编码, 那么输入过程是很难出现乱子的, 这也是为什么你可能打开一个文件是乱码, 但是你继续在这个文件中输入内容却并不是乱码的原因:

编译器/解释器对源代码中字符常量的处理方式

(以下内容针对 python2)

前面说的纯文本文件是不包含 meta 信息的, 几乎所有编程语言的源代码文件都是纯文本文件, .py 文件当然也不例外, 连 vim 这样的专业编辑器都没法知道源文件的编码格式, python 解释器肯定也是不知道的, 所以 python 也是选用了一种默认的编码方式来解释 .py 源文件, 与编辑器程序不同的是, python 默认认为源文件是 ascii 编码的, 因为程序代码一般都是英文字符嘛, 所以你如果写了这么一个 python 源文件:

#!/usr/bin/env python

a = '你好, 世界'

print a

并保存为 utf8 格式, 然后执行, 会发生下面的错误:

$ ls

test.py

$ python test.py

File "test.py", line 3

SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

这就是因为 python 解释器按照 ascii 的编码方式解读源文件, 结果突然读到一个字节 xe4 发现这个字节并不在 ascii 范围内, 与就报错退出了. 这个过程和 vim 其实是类似的, 只不过 vim 不会报错退出, 而是 "将错就错", 把乱码呈现给我们.

那么如何解决这个问题呢? 很简单, 上面错误信息里已经给出了, 让我们参考 pep0263 就好了. 看过 pep0263 之后我们就知道了, 原来 python 是采用了在源文件中写明文件格式的办法, 所以我们只要在 shell-bang 下面一行加上如下内容就行了:

#!/usr/bin/env python

# -*- coding: utf-8 -*-

a = '你好, 世界'

print a

然后再执行就能够正确输出 "你好, 世界" 了.

误区

经过上文, 我们应该知道要在程序源代码中使用 utf8 字符, 首先源文件顶部得有这么一行了: # -*- coding: utf-8 -* , 否则解释器直接报错.

然后看这么一个问题, 当我们写下这么一段程序时:

#!/usr/bin/env python

# -*- coding: utf-8 -*-

a = '你好, 世界'

b = u' => hello, world'

print a + b

我们会得到这么一个错误:

Traceback (most recent call last):

File "test.py", line 6, in

print a + b

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

这个错误是由于没有分清 "unicode 对象" 和 "包含 unicode 序列的 str 对象" 的区别导致的. 前面我们说了, 在 python 中, str 和 unicode 都是 basestring 的子类, str 对象就相当于 C 语言的字符串类型, unicode 则相当于 C 语言中的宽字符序列.

在上面的代码中, 虽然 a 的值是 '你好, 世界', 但是 a 实际上是 str 对象, '你好, 世界' 之所以能够显示出来, 是因为源文件格式是 utf8 而且编辑器和 X 服务程序也都支持 utf8 字符集. 但是变量 a 中存储的实际上是 'xe4xbdxa0xe5xa5xbd, xe4xb8x96xe7x95x8c' (使用 a.encode('string_escape') 或者 a.encode('hex_codec') 方法查看).

由于 b 是 unicode 类型而 a 是 str 类型, 而 python 又是强类型, 所以上面的 a + b 的时候, 所以要先对 a 做一次 decode 目的是将其转化成 unicode 对象, 然后再将它和 b 相加. 然而对 str 类型 decode 实际上是没什么意义的, decode 时所选用的 encoding 默认就是 'ascii' 或者是 sys.getdefaultencoding() 的返回值, 当然这个方法的返回值默认也是 'ascii'. 然而采用 'ascii' 去 decode a 时, 自然就会因为 a 里面包含了 xe4 这样的非 ascii 字符而导致 decode 失败报错.

Python3 的进步

python2 时代对 unicode 的支持问题一直广为诟病, str , unicode , encode , decode 等概念也是十分混乱, 在 python3 中这些概念的清晰度有了很大的提升.

比如在 python3 中 str 不再相当于 C 语言中的 char * , 而是像 C 语言的宽字符类型或者 Java 的 String 类型那样, 本身就是支持宽字符的类型. 而且增加还增加了 bytes 类型, bytes 类型这下才是相当于 C 语言的 char * 类型. 同时取消了 str 类型的 decode 方法, 以及取消了 bytes 类型的 encode 方法.

因为 decode 就是讲字节流解析成对象, encode 就是讲对象编码成字节流, 所以对 bytes 进行 encode, 和对已经表示对象的 str 进行 decode, 都是没有意义的. pyton2 时代 str 类型作为字节流类型时, 存在 str.encode(), 以及 unicode.decode 这两个方法其实都是无意义的.

python程序采用unicode编码、英文字符,Python 与 Unicode相关推荐

  1. python寻找字符串中的英文字符,python如何解析字符串中出现的英文人名?

    这里有四个例子,结果来自google scholar str1 = "Jakes, William C., and Donald C. Cox. Microwave mobile commu ...

  2. python程序基础知识(五)python文本文件操作

    目录 1.概述 2.写文本文件 3.读文本文件 3.1 读字符函数read 3.2 读取一行的函数readline和读取所有行的readlines 4.文件编码 5.文件指针 6.二进制文件 1.概述 ...

  3. Python程序设计基础第一章笔记:Python概述

    Python程序设计基础笔记 目录 Python程序设计基础笔记 第一章:python概述 1.1 python是这样一种语言 1.2 python版本之争 略 1.3 python编程规范与代码优化 ...

  4. 代写python期末作业价格_代写program留学生作业、代做Python程序语言作业、代写Python课程设计作业...

    代写program留学生作业.代做Python程序语言作业.代写Python课程设计作业 日期:2019-11-29 12:55 Completing the Final Project - Pyth ...

  5. Unicode编码和字符相互转换

    Unicode编码和字符相互转换 1. 字符转Unicode编码 (charCodeAt) let s = "abcdef" console.log(s.charCodeAt(2) ...

  6. html用unicode编码转换汉字,汉字与Unicode编码相互转换(Js版)

    Unicode编码转换工具 //ASCII 转换 Unicode function AsciiToUnicode(){ if (document.getElementById("conten ...

  7. python程序采用unicode编码、英文字符_python--基础字符编码

    一 了解字符编码的知识储备 一 计算机基础知识 二 文本编辑器存取文件的原理(nodepad++,pycharm,word) #1.打开编辑器就打开了启动了一个进程,是在内存中的,所以,用编辑器编写的 ...

  8. python unicode编码转换中文_python实现unicode转中文及转换默认编码的方法

    本文实例讲述了python实现unicode转中文及转换默认编码的方法.分享给大家供大家参考,具体如下: 一.在爬虫抓取网页信息时常需要将类似"\u4eba\u751f\u82e6\u77e ...

  9. Python程序不使用函数将字符大写

    In this article, we will go for capitalizing the characters i.e. conversion from lowercase to upperc ...

最新文章

  1. stm32cubemx无法生成工程_经验分享 | STM32CubeMX + STM32F1系列开发时遇到的四个问题及解决方案分享...
  2. redis 生成dump.rdb文件
  3. Python字典get()方法的实际应用
  4. Java反射原理学习之MethodHandle debug
  5. bugfix:MySQL内存使用率无限增长以及kill手法
  6. Wi-Fi 6还没用上,Wi-Fi 7就要来了?
  7. mysql auto_inc_MySQL innodb_autoinc_lock_mode 详解
  8. java rsync上传_Rsync自动同步工具
  9. 未来教育 ***java二级考试题库第二十五套错题***
  10. 弱电设计:智能建筑设计标准GB50314-2015,pdf版本
  11. 给VS2008安装MSDN
  12. Java华氏度与摄氏度之间的转换
  13. 基本算法总结,力扣题目整理
  14. nvme分区选mbr还是guid_怎么分辨硬盘是GUID格式还是MBR格式以及怎样更改
  15. 无与伦比的工业设计 iPhone 4详细评测
  16. Ubuntu16.04系统安装搜狗输入法教程
  17. 用vc对oracle数据库编程,用VC开发基于ORACLE数据库应用程序
  18. 国企招聘:中航证券有限公司2023届校招火热进行中
  19. 中国黑客群体的真实收入
  20. BSA-Xylan 牛血清白蛋白-木聚糖,血清白蛋白HSA/卵清白蛋白OVA/乳清白蛋白偶联糖

热门文章

  1. 基于VTK的Delaunay的三角剖分算法
  2. Blender纹理基础学习视频教程 CGCookie – Fundamentals of Texturing in Blender
  3. 正则表达式分类 区别
  4. 贪心:jump 游戏(获取最少跳跃的次数以及跳跃路径)
  5. 末学者笔记--openstack共享组件:rabbitmq(3)
  6. Java 集合——List集合
  7. oracle使用小技巧
  8. python三层架构
  9. Cassandra安装测试
  10. Linux环境编程--进程