Python的文本和字节序列
一、字符串的表示和存储
字符串是字符的序列,每个字符都有有一个数字作为标识,同时会有一个将标识转换为存储字节的编码方案;
s = 'hello world python'
for c in s:
print(c, end=' ')
h e l l o w o r l d p y t h o n
ACSII为协议内的每个字符分别对应一个数字,然后以这个数字的二进制形式存储到计算机;
s = 'hello world python'
for c in s:
num = ord(c)
print(num, format(num, 'b'))
104 1101000
101 1100101
108 1101100
108 1101100
111 1101111
32 100000
119 1110111
111 1101111
114 1110010
108 1101100
100 1100100
32 100000
112 1110000
121 1111001
116 1110100
104 1101000
111 1101111
110 1101110
ACSII协议覆盖的字符十分有限,使用一个字节就可以保存,这也是其比较简单的根源;
s = b'é'
File "<ipython-input-19-b82fcf157fe5>", line 1
s = b'é'
^
SyntaxError: bytes can only contain ASCII literal characters.
unicode标准为每个字符制定一个数字作为code point;
s = 'è ç í'
for c in s:
print(ord(c))
232
32
231
32
237
unicode支持大量的字符,需要使用多个字节来存储,这就涉及到字节的大小端、空间占用及与ACSII的兼容性问题;
UTF-32编码方案直接使用4个字节来承载code poin的二进制形式,涉及大小端问题,比较浪费空间,使用较少;
s = 'èçí'
for b in s.encode('utf_32be'):
print(hex(b), end=' ')
print()
for b in s.encode('utf_32le'):
print(hex(b), end=' ')
print()
for b in s.encode('utf_32'):
print(hex(b), end=' ')
0x0 0x0 0x0 0xe8 0x0 0x0 0x0 0xe7 0x0 0x0 0x0 0xed
0xe8 0x0 0x0 0x0 0xe7 0x0 0x0 0x0 0xed 0x0 0x0 0x0
0xff 0xfe 0x0 0x0 0xe8 0x0 0x0 0x0 0xe7 0x0 0x0 0x0 0xed 0x0 0x0 0x0
UTF-16编码方案根据前两个字节的范围来确定使用两个字节还是四个字节,虽然比UTF-32节省空间,但是使用也比较少;
s = 'èçí'
for b in s.encode('utf_16be'):
print(hex(b), end=' ')
print()
for b in s.encode('utf_16le'):
print(hex(b), end=' ')
print()
for b in s.encode('utf_16'):
print(hex(b), end=' ')
0x0 0xe8 0x0 0xe7 0x0 0xed
0xe8 0x0 0xe7 0x0 0xed 0x0
0xff 0xfe 0xe8 0x0 0xe7 0x0 0xed 0x0
UTF-8也使用变长字节,每个字符使用的字节个数与其Unicode编号的大小有关,编号小的使用的字节就少,编号大的使用的字节就多,使用的字节个数为1~4不等;
s = 'èçí'
for b in s.encode('utf_8'):
print(hex(b), end=' ')
0xc3 0xa8 0xc3 0xa7 0xc3 0xad
utf-16和utf-32编码方案默认生成的字节序列会添加BOM(byte-order mark)即\xff\xfe,指明编码的时候使用Interl CPU小字节序。
二、字节数组
bytes和bytearray的元素都是介于0-255之间的整数,但是通过字符编码方案也可以存储任何的字符串;字节数组切片还是对应的字节数组;
字节数组可以直接显示ASCII字符;
s = 'helloèçí'
b_arr = bytes(s, 'utf_8')
print(type(b_arr))
print(type(b_arr))
for b in b_arr:
print(b, end=' ')
print()
print('element of bytes is int number', b_arr[0])
print('splice of bytes is bytes',end = ' ' )
b_arr_splice = b_arr[:1]
print(b_arr_splice)
num_b_arr = bytes([299])
<class 'bytes'>
b'hello\xc3\xa8\xc3\xa7\xc3\xad'
104 101 108 108 111 195 168 195 167 195 173
element of bytes is int number 104
splice of bytes is bytes b'h'
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-61-b8f064f91cf5> in <module>()
13 print(b_arr_splice)
14
---> 15 num_b_arr = bytes([299])
ValueError: bytes must be in range(0, 256)
struct模块提供了一些函数,把打包的字节序列转换成不同类型字段组成的元组,还有一些函数用于执行反向转换,把元组转换成打包的字节序列。struct模块能处理bytes、bytearray和memoryview对象。
import struct
record_format = 'hd4s'
pack_bytes = struct.pack(record_format, 7 , 3.14,b'gbye')
print(type(pack_bytes))
print(pack_bytes)
with open('struct.b', 'wb') as fp:
fp.write(pack_bytes)
record_size = struct.calcsize(record_format)
with open('struct.b', 'rb') as fp:
record_bs = fp.read(record_size)
print(struct.unpack(record_format, record_bs))
三、不要依赖默认编码
读写文本文件的时候最好要显示的指定编码方案,防止编码方案不匹配出现乱码或者错误;
open('cafe.txt', 'w', encoding='utf-8').write('café')
fp = open('cafe.txt')
print(fp)
print(fp.read())
由于Linux的默认编码是UTF-8,所以运行结果正常
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='UTF-8'>
café
但是在windows 10上执行就不这么幸运了,我们可以看到IO的默认编码方案是cp936
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp936'>
caf茅
在Linux和windows上分别执行以下探测默认编码方案的代码
import sys, locale
expressions = '''
locale.getpreferredencoding()
type(my_file)
my_file.encoding
sys.stdout.isatty()
sys.stdout.encoding
sys.stdin.isatty()
sys.stdin.encoding
sys.stderr.isatty()
sys.stderr.encoding
sys.getdefaultencoding()
sys.getfilesystemencoding()
'''
with open('encoding', 'w') as my_file:
for expression in expressions.split():
value = eval(expression)
print(expression.rjust(30), '->', repr(value))
在Ubuntu上执行,可以看到输出的都是UTF-8;
locale.getpreferredencoding() -> 'UTF-8'
type(my_file) -> <class '_io.TextIOWrapper'>
my_file.encoding -> 'UTF-8'
sys.stdout.isatty() -> True
sys.stdout.encoding -> 'utf-8'
sys.stdin.isatty() -> True
sys.stdin.encoding -> 'utf-8'
sys.stderr.isatty() -> True
sys.stderr.encoding -> 'utf-8'
sys.getdefaultencoding() -> 'utf-8'
sys.getfilesystemencoding() -> 'utf-8'
在windows 10上执行,locale.getpreferredencoding()和my_file的编码都是cp936;
locale.getpreferredencoding() -> 'cp936'
type(my_file) -> <class '_io.TextIOWrapper'>
my_file.encoding -> 'cp936'
sys.stdout.isatty() -> True
sys.stdout.encoding -> 'utf-8'
sys.stdin.isatty() -> True
sys.stdin.encoding -> 'utf-8'
sys.stderr.isatty() -> True
sys.stderr.encoding -> 'utf-8'
sys.getdefaultencoding() -> 'utf-8'
sys.getfilesystemencoding() -> 'utf-8'
如果没有指定编码方案,操作文本文件的时候默认使用locale.getpreferredencoding(),在windows10上将python的执行结果重定向到文件,可以看到sys.stdout.encoding变成了cp936;
locale.getpreferredencoding() -> 'cp936'
type(my_file) -> <class '_io.TextIOWrapper'>
my_file.encoding -> 'cp936'
sys.stdout.isatty() -> False
sys.stdout.encoding -> 'cp936'
sys.stdin.isatty() -> True
sys.stdin.encoding -> 'utf-8'
sys.stderr.isatty() -> True
sys.stderr.encoding -> 'utf-8'
sys.getdefaultencoding() -> 'utf-8'
sys.getfilesystemencoding() -> 'utf-8'
python使用sys.getdefaultencoding()进行二进制数据与字符串之间的转换;
sys.getfilesystemencoding( )用于编解码文件名(不是文件内容)。把字符串参数作为文件名传给open( )函数时就会使用它;
四、规范化字符串之后进行比较
因为Unicode有组合字符(变音符号和附加到前一个字符上的记号,打印时作为一个整体),所以字符串比较起来很复杂。
# 同样的一个字符会有不同的构成方式
s1 = 'café'
s2 = 'cafe\u0301'
print((s1, s2))
print((len(s1), len(s2)))
print(s1 == s2)
('café', 'café')
(4, 5)
False
U+0301是COMBINING ACUTE ACCENT,加在“e”后面得到“é”。在Unicode标准中,'é'和'e\u0301'这样的序列叫“标准等价物”(canonical equivalent),应用程序应该把它们视作相同的字符。但是,Python看到的是不同的码位序列,因此判定二者不相等。
Python中unicodedata.normalize函数提供的Unicode规范化。这个函数的第一个参数是这4个字符串中的一个:'NFC'、'NFD'、'NFKC'和'NFKD'。NFC(Normalization Form C)使用最少的码位构成等价的字符串,而NFD把组合字符分解成基字符和单独的组合字符。这两种规范化方式都能让比较行为符合预期:
# normalize字符串再进行比较
from unicodedata import normalize
s1 = 'café'
s2 = 'cafe\u0301'
print((s1, s2))
print((len(s1), len(s2)))
print(s1 == s2)
s1_nfc_nor = normalize('NFC', s1)
s2_nfc_nor = normalize('NFC', s2)
print((s1_nfc_nor, s2_nfc_nor))
print((len(s1_nfc_nor), len(s2_nfc_nor)))
print(s1_nfc_nor == s2_nfc_nor)
s1_nfd_nor = normalize('NFD', s1)
s2_nfd_nor = normalize('NFD', s2)
print((s1_nfd_nor, s2_nfd_nor))
print((len(s1_nfd_nor), len(s2_nfd_nor)))
print(s1_nfd_nor == s2_nfd_nor)
# ('café', 'café')
# (4, 5)
# False
# ('café', 'café')
# (4, 4)
# True
# ('café', 'café')
# (5, 5)
# True
在另外两个规范化形式(NFKC和NFKD)的首字母缩略词中,字母K表示“compatibility”(兼容性)。这两种是较严格的规范化形式,对“兼容字符”有影响。虽然Unicode的目标是为各个字符提供“规范的”码位,但是为了兼容现有的标准,有些字符会出现多次。例如,虽然希腊字母表中有“μ”这个字母(码位是U+03BC,GREEK SMALL LETTER MU),但是Unicode还是加入了微符号'µ'(U+00B5),以便与latin1相互转换。因此,微符号是一个“兼容字符”。
# NFKC的规范化
from unicodedata import normalize, name
half = '½'
print(len(half))
print(hex(ord(half)))
half_nor = normalize('NFKC', half)
print(half_nor)
print(type(half_nor))
print(len(half_nor))
for c in half_nor:
print(hex(ord(c)), end=' ')
print()
four_squared = '4²'
four_squared_no = normalize('NFKC', four_squared)
print(four_squared_no)
micro = 'µ'
micro_nor = normalize('NFKC', micro)
print(micro_nor)
print(ord(micro), ord(micro_nor))
print(name(micro), name(micro_nor))
# 1
# 0xbd
# 1⁄2
# <class 'str'>
# 3
# 0x31 0x2044 0x32
# 42
# μ
# 181 956
# MICRO SIGN GREEK SMALL LETTER MU
使用'1/2'替代'½'可以接受,微符号也确实是小写的希腊字母'µ',但是把'4²'转换成'42'就改变原意了。某些应用程序可以把'4²'保存为'42',但是normalize函数对格式一无所知。因此,NFKC或NFKD可能会损失或曲解信息。
大小写折叠其实就是把所有文本变成小写,再做些其他转换。这个功能由str.casefold( )方法(Python 3.3新增)支持。对于只包含latin1字符的字符串s,s.casefold( )得到的结果与s.lower( )一样,唯有两个例外:微符号'µ'会变成小写的希腊字母“μ”(在多数字体中二者看起来一样);德语Eszett(“sharp s”,ß)会变成“ss”。
# 大小写折叠
micro = 'µ'
print(name(micro))
micro_cf = micro.casefold()
print(name(micro_cf))
print((micro, micro_cf))
eszett = 'ß'
print(name(eszett))
eszett_cf = eszett.casefold()
print((eszett, eszett_cf))
# MICRO SIGN
# GREEK SMALL LETTER MU
# ('µ', 'μ')
# LATIN SMALL LETTER SHARP S
# ('ß', 'ss')
Google搜索涉及很多技术,其中一个显然是忽略变音符号(如重音符、下加符等),至少在某些情况下会这么做。去掉变音符号不是正确的规范化方式,因为这往往会改变词的意思,而且可能误判搜索结果。但是对现实生活却有所帮助:人们有时很懒,或者不知道怎么正确使用变音符号,而且拼写规则会随时间变化,因此实际语言中的重音经常变来变去。
# 极端规范化,去掉变音符号
import unicodedata
import string
def shave_marks(txt):
txt_nor = normalize('NFD', txt)
txt_shaved = ''.join(c for c in txt_nor if not unicodedata.combining(c))
return normalize('NFC', txt_shaved)
order = 'è ç í'
print(shave_marks(order))
greek = 'έ é'
print(shave_marks(greek))
def shave_marks_latin(txt):
txt_nor = normalize('NFD', txt)
latin_base = False
keep = []
for c in txt_nor:
if unicodedata.combining(c) and latin_base:
continue;
keep.append(c)
if not unicodedata.combining(c):
latin_base = c in string.ascii_letters
shaved = ''.join(keep)
return normalize('NFC', shaved)
print(shave_marks_latin(order))
print(shave_marks_latin(greek))
# e c i
# ε e
# e c i
# έ e
Python的文本和字节序列的更多相关文章
- 《流畅的Python》第二部分 数据结构 【序列构成的数组】【字典和集合】【文本和字节序列】
第二部分 数据结构 第2章 序列构成的数组 内置序列类型 序列类型 序列 特点 容器序列 list.tuple.collections.deque - 能存放不同类型的数据:- 存放的是任意类型的对象 ...
- Fluent_Python_Part2数据结构,04-text-byte,文本和字节序列
文本和字节序列 人使用文本,计算机使用字节序列 1. 大纲: 字符.码位和字节表述 bytes.bytearray和memoryview等二进制序列的独特特性 全部Unicode和陈旧字符集的编解码器 ...
- Python 文本和字节序列
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica } Python 3 明确区分了人类可读的文本字符串和原始的字节序列.隐式 ...
- python高级(四)—— 文本和字节序列(编码问题)
本文主要内容 字符 字节 结构体和内存视图 字符和字节之间的转换——编解码器 BOM鬼符 标准化Unicode字符串 Unicode文本排序 python高级——目录 文中代码均放在github上: ...
- 流畅的python第四章文本和字节序列学习记录
字符问题 把码位转化成字节序列的过程是编码,把字节序列转化成码位的过程是解码 把unicode字符串当成人类可读的文本,码位当成机器可读的, 将字节序列编程人类可读是解码,把字符串编码成字节序列是编码 ...
- Python文本和字节序列
ASCII码 早期人们用8位二进制来编码英文字母(最前面的一位是0) 也就是说,将英文字母和一些常用的字符和这128种二进制0.1串一一对应起来, 比如:大写字母“A”所对应的二进制位“0100000 ...
- python教程(四)·序列
距离上次的小项目已经休息了很长一段时间,是时候来继续本系列教程了.这一节开始我们将深入python中的数据结构. 序列的概念 在python中,最基本的数据结构是序列,序列包含一个或多个元素,每个元素 ...
- Python菜鸟文本处理4种方法
自从认识了python这门语言,所有的事情好像变得容易了,作为小白,逗汁儿今天就为大家总结一下python的文本处理的一些小方法. 话不多说,代码撸起来. python大小写字符互换 在进行大小写互换 ...
- ubuntu下把python脚本转为二进制字节码文件
ubuntu下把python脚本转为二进制字节码文件 听语音 原创 | 浏览:354 | 更新:2017-12-22 14:48 1 2 3 4 5 6 7 分步阅读 自己拥有个几个python脚本文 ...
随机推荐
- JS数字每三位加逗号的最简单方法
<script> function thousands(num){ var str = num.toString(); var reg = str.indexOf("." ...
- Mybites学习
参考链接:https://www.cnblogs.com/dongying/p/4073259.html <select <!-- 1. id (必须配置) id是命名空间中的唯一标识符, ...
- Vuex入门、同步异步存取值进阶版
关注公众号查看原文: 1. vueX介&绍安装 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方 ...
- LDAP + Samba 安装配置流程
LDAP + Samba 安装配置 基础环境:Ubuntu18.04 安装samba root@cky:~# apt install samba smbldap-tools -y 查看版本 root@ ...
- 保姆级别学生党安装Clion IDE(面向华师同学)
保姆级别学生党安装Clion IDE(面向华师同学) 界面UI 废话不多说,直接上图 具备功能 UI美观 (下面会介绍) 基础的代码编写能力 大容量的IDE插件 (下面会介绍) 代码补全,以及搭配Ki ...
- Snort + Barbyard2 + Snorby环境搭建
1.环境 ubuntu-14.04.5 daq-2.0.7 Snort-2.9.15.1 Barbyard2 snorby Mysql Docker 2.架构 3.安装步骤 Ubuntu配置 如果是刚 ...
- 巧用 -webkit-box-reflect 倒影实现各类动效
在很久之前的一篇文章,有讲到 -webkit-box-reflect 这个属性 -- 从倒影说起,谈谈 CSS 继承 inherit -webkit-box-reflect 是一个非常有意思的属性,它 ...
- HDOJ-1257(贪心/动态规划)
最少拦截系统 HDOJ-1257 我做这题的思路就是采用暴力或者贪心.也就是每次循环选出从第一个未被选择的元素开始,依次把后面可以选择的元素作为一个系统.最后统计可以有多少个系统. 还有人的思路就是利 ...
- FPGA的开发板
板卡架构 板载FPGA(K7-325T)处理24端口10/100/1000M以太网数据: FPGA外挂4Gbit的DDR3颗粒,最大支持800MHz: 板载CPU进行系统配置.管理,并与客户端软件通信 ...
- 导出文件,responseType设置了blob,实际返回了JSON格式的错误信息的处理方式
需求:导出文件 问题描述:由于后台直接返回的文件流,在请求下载的方法中将XHR 的 responseType 指定为 blob 或者 arraybuffer.但并不是每次的操作都是成功的,所以在接口错 ...