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脚本文 ...
随机推荐
- CentOS7集群环境Elastic配置
CentOS7集群环境Elastic配置 (首先去官网下载elasticsearch的source code并解压到/usr/soft目录下) (以下默认root账户) 1.更改配置文件 文件路径:/ ...
- Linux下安装mysql-5.7.28详细步骤
一.下载Mysql 下载地址:https://downloads.mysql.com/archives/community/ 二.环境配置 检测系统是否自带Mysql # rmp -qa|grep m ...
- 配置Nginx的坑及思路
我配置的是Django + uwsgi + Nginx 说下思路,先进行模块化测试: Django: Django 下 第一个坑是sql版本低问题,原因用pip安装不正确,在网上查了下按这个文章重装下 ...
- Django-1.11中文文档-模型Models(一)
官方文档链接 模型是数据信息的唯一并明确的来源.它包含了我们储存的数据的基本字段和行为.通常,每个模型映射到一张数据库表. 基本概念: 每个模型都是django.db.models.Model的一个子 ...
- Linux磁盘分区格式化和扩容
Note:根据各系统上磁盘的类型不同,磁盘命名规则也会不同:例如/dev/xvd,/dev/sd,/dev/vd,/dev/hd 目录 磁盘格式化 MBR格式 GPT分区 磁盘扩容 MBR格式扩容 G ...
- Javascript学习,DOM对象,方法的使用
JavaScript: ECMAScript: BOM: DOM: 事件 DOM的简单学习 功能:控制html文档内容 代码:获取页面标签(元素)对象和Element document.getElem ...
- 在scanf函数中占位符使用错误而产生的一些错误
出现的问题 在做编程题的的时候,遇到了一个很奇怪的错误,出问题的代码如下: 1 #include <cstdio> 2 using namespace std; 3 4 int main( ...
- 基于Docker的MindSpore安装与使用基础介绍
技术背景 MindSpore是一款新一代AI开源计算框架,其特色在于:创新编程范式,AI科学家和工程师更易使用,便于开放式创新:该计算框架可满足终端.边缘计算.云全场景需求,能更好保护数据隐私:可开源 ...
- P2261 [CQOI2007]余数求和 【整除分块】
一.题面 P2261 [CQOI2007]余数求和 二.分析 参考文章:click here 对于整除分块,最重要的是弄清楚怎样求的分得的每个块的范围. 假设$ n = 10 ,k = 5 $ $$ ...
- Nodejs学习笔记(3) 创建服务器:Web 模块(http)与 express 框架
目录 参考资料 1. 使用 http 模块创建服务器 1.1 实现思路及代码 1.2 HTTP 结构 1.2.1 Request中的重要字段 1.2.2 Response 头信息:文件类型.状态码.连 ...