python学习-4

[TOC]

4.1 字符问题

把码位转换成字节序列的过程是编码encode;把字节序列转换成码位的过程是解码decode

1
2
3
4
5
6
7
8
9
10
s = 'café'
print(len(s))
b = s.encode('utf8')# 使用 UTF-8 把 str 对象编码成 bytes 对象,编码后“é”是2个字节。
print(b,len(b))
'''
4
b'caf\xc3\xa9' 5
'''
print(b.decode('utf8'),len(b))# 使用 UTF-8 把 bytes 对象解码成 str 对象。
#café 5

4.2 字节概要

bytes 或 bytearray 对象的各个元素是介于 0~255(含)之间的整数,而不像 Python 2 的 str 对象那样是单个的字符。

1
2
3
4
5
6
7
8
9
10
11
#4.2
cafe = bytes('café',encoding='utf_8') # bytes 对象可以从 str 对象使用给定的编码构建。
print(cafe[0]) #各个元素是 range(256) 内的整数。
#99
print(cafe[:1]) #bytes 对象的切片还是 bytes 对象,即使是只有一个字节的切片。
#b'c'
cafe_arr = bytearray(cafe)
print(cafe_arr)
#bytearray(b'caf\xc3\xa9')
print(cafe_arr[-1:])
#bytearray(b'\xa9')

虽然二进制序列其实是整数序列,但是它们的字面量表示法表明其中有 ASCII 文本。

因此,各个字节的值可能会使用下列三种不同的方式显示

可打印的 ASCII 范围内的字节(从空格到 ~),使用 ASCII 字符本身。

制表符、换行符、回车符和 \ 对应的字节,使用转义序列 \t、\n、\r 和 \。

其他字节的值,使用十六进制转义序列(例如,\x00 是空字节)。

因此,我们看到的是 b’caf\xc3\xa9’:前 3 个字节 b’caf’ 在可打印的 ASCII 范围内,

**二进制序列有个类方法是 str 没有的,名为 fromhex,**它的作用是解 析十六进制数字对(数字对之间的空格是可选的),构建二进制序列

1
2
print(bytes.fromhex('31 45 FF FF'))
#b'1E\xff\xff'

使用缓冲类对象构建二进制序列是一种低层操作,可能涉及类型转换。

1
2
3
4
5
from numpy import number
number = array.array('h',[-1,-2,0,1,1,2])
octets = bytes(number)
print(octets)
#b'\xff\xff\xfe\xff\x00\x00\x01\x00\x01\x00\x02\x00'

结构体和内存视图

struct 模块提供了一些函数,把打包的字节序列转换成不同类型字段 组成的元组,还有一些函数用于执行反向转换,把元组转换成打包的字节序列。struct 模块能处理 bytes、bytearray 和 memoryview 对象。

这里用了PNG图了,知道意思就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import struct
fmt = '<4s3sHH' # 结构体的格式:< 是小字节序,3s3s 是两个 3 字节序列,HH 是两个16 位二进制整数。
with open(BASE_DIR+'/1.png','rb') as fp:
png = memoryview(fp.read()) #用内存中的文件内容创建一个 memoryview 对象
header = png[:11]
print(bytes(header))
print(struct.unpack(fmt,header))#拆包 memoryview 对象,得到一个元组,包含类型、版本、宽度和高度。
del header
del png
# 删除引用,释放 memoryview 实例所占的内存。

"""
(b'\x89PNG', b'\r\n\x1a', 10, 0)
"""

4.3 基本的编解码器

Python 自带了超过 100 种编解码器(codec, encoder/decoder),用于在文本和字节之间相互转换。每个编解码器都有一个名称,如 ‘utf_8’, 而且经常有几个别名,如 ‘utf8’、‘utf-8’ 和 ‘U8’。这些名称可以传给 open()、str.encode()、bytes.decode() 等函数的 encoding 参数。

1
2
3
4
5
6
7
8
#4.3
for codec in ['latin_1', 'utf_8', 'utf_16']:
print(codec, 'El Niño'.encode(codec), sep='\t')
'''
latin_1 b'El Ni\xf1o'
utf_8 b'El Ni\xc3\xb1o'
utf_16 b'\xff\xfeE\x00l\x00 \x00N\x00i\x00\xf1\x00o\x00'
'''

4.4 了解编解码问题

多数非 UTF 编解码器只能处理 Unicode 字符的一小部分子集。把文本转 换成字节序列时,如果目标编码中没有定义某个字符,那就会抛出 UnicodeEncodeError 异常,除非把 errors 参数传给编码方法或函数,对错误进行特殊处理.

4.4.1 处理UnicodeEncodeError

1
2
3
4
5
6
7
8
9
10
11
12
13
city = 'São Paulo'
print( city.encode('utf_8'))
print(city.encode('utf_16'))
print(city.encode('utf_?')) #这个能解码大多数字符?
#3个处理异常的方式,1.忽略,2.替代成?3.使用xml实体
print(city.encode('cp437', errors='ignore') )
print(city.encode('cp437', errors='replace'))
print(city.encode('cp437', errors='xmlcharrefreplace'))
'''
b'So Paulo'
b'S?o Paulo'
b'S&#227;o Paulo'
'''

4.4.2 处理UnicodeDecodeError

不是每一个字节都包含有效的 ASCII 字符,也不是每一个字符序列都是 有效的 UTF-8 或 UTF-16。因此,把二进制序列转换成文本时,如果假 设是这两个编码中的一个,遇到无法转换的字节序列时会抛出 UnicodeDecodeError。

1
2
3
4
5
6
7
8
9
10
octets = b'Montr\xe9al'  #这些字节序列是使用 latin1 编码的“Montréal”;'\xe9' 字节对应“é”。
print(octets.decode('cp1252'))
#Montréal
#可以使用 'cp1252'(Windows 1252)解码,因为它是 latin1 的有效超集。
print(octets.decode('iso8859_7'))
#Montrιal
#ISO-8859-7 用于编码希腊文,因此无法正确解释 '\xe9' 字节,而且没有抛出错误。
print(octets.decode('utf_8', errors='replace'))
#Montr�al
#使用 'replace' 错误处理方式,\xe9 替换成了“ ”(码位是U+FFFD),这是官方指定的 REPLACEMENT CHARACTER(替换字符),表示未知字符。

4.4.3 使用预期之外的编码加载模块时抛出的 SyntaxError

Python 3 默认使用 UTF-8 编码源码,Python 2(从 2.5 开始)则默认使用 ASCII**。如果加载的 .py 模块中包含 UTF-8 之外的数据,而且没有声明编码,会得到类似下面的消息:**

1
2
3
SyntaxError: Non-UTF-8 code starting with '\xe1' in file ola.py on line
1, but no encoding declared; see http://python.org/dev/peps/pep-0263/
for detail

解决,在文件顶部添加一个coding 注释

1
2
3
# coding: cp1252
print('Olá, Mundo!')

4.4.4 如何找出字节序列的编码(Chardet库)

统一字符编码侦测包 Chardethttps://pypi.python.org/pypi/chardet)就是 这样工作的,它能识别所支持的 30 种编码。Chardet 是一个 Python 库, 可以在程序中使用,不过它也提供了命令行工具 chardetect。

4.4.5 BOM:有用的鬼符

UTF-16 编码的序列开头有几个额外的字节,

1
2
3
4
u16 = 'El Niño'.encode('utf_16')
print(u16)
#b'\xff\xfeE\x00l\x00 \x00N\x00i\x00\xf1\x00o\x00'
#b'\xff\xfe'。这是 BOM,即字节序标记(byte-ordermark),指明编码时使用 Intel CPU 的小字节序。

在小字节序设备中,各个码位的最低有效字节在前面:字母 ‘E’ 的码位 是 U+0045(十进制数 69),在字节偏移的第 2 位和第 3 位编码为 69 和 0。

1
2
3
print(list(u16))
#[255, 254, 69, 0, 108, 0, 32, 0, 78, 0, 105, 0, 241, 0, 111, 0]
#FF FE 69 00

UTF-16 编码在要编码的文本前面加上特殊的不可见字 符 ZERO WIDTH NO-BREAK SPACE(U+FEFF)。在小字节序系统中, 这个字符编码为 b’\xff\xfe’(十进制数 255, 254)。

UTF-16 有两个变种:UTF-16LE,显式指明使用小字节序;UTF-16BE, 显式指明使用大字节序。如果使用这两个变种,不会生成 BOM:

1
2
3
4
5
6
print(list('El Niño'.encode('utf_16le')))
print(list('El Niño'.encode('utf_16be')))
'''
[69, 0, 108, 0, 32, 0, 78, 0, 105, 0, 241, 0, 111, 0]
[0, 69, 0, 108, 0, 32, 0, 78, 0, 105, 0, 241, 0, 111]
'''

如果有 BOM,UTF-16 编解码器会将其过滤掉,为你提供没有前导 ZERO WIDTH NO-BREAK SPACE 字符的真正文本。根据标准,如果文件 使用 UTF-16 编码,而且没有 BOM那么应该假定它使用的是 UTF-16BE(大字节序)编码。然而,Intel x86 架构用的是小字节序,因此有 很多文件用的是不带 BOM 的小字节序 UTF-16 编码。 与字节序有关的问题只对一个字(word)占多个字节的编码(如 UTF-16 和 UTF-32)有影响UTF-8 的一大优势是,不管设备使用哪种字节序,生成的字节序列始终一致,**因此不需要 BOM。**尽管如此,某些 Windows 应用(尤其是 Notepad)依然会在 UTF-8 编码的文件中添加 BOM;而且,Excel 会根据有没有 BOM 确定文件是不是 UTF-8 编码, 否则,它假设内容使用 Windows 代码页(codepage)编码。**UTF-8 编码 的 U+FEFF 字符是一个三字节序列:b’\xef\xbb\xbf’。**因此,如果文 件以这三个字节开头,有可能是带有 BOM 的 UTF-8 文件。然而, Python 不会因为文件以 b’\xef\xbb\xbf’ 开头就自动假定它是 UTF-8 编码的。

4.5 处理文本文件

处理文本的最佳实践是“Unicode 三明治”(如图 4-2 所示)。 意思是, 要尽早把输入(例如读取文件时)的字节序列解码成字符串。这种三明治中的“肉片”是程序的业务逻辑,在这里只能处理字符串对象。在其他处理过程中,一定不能编码或解码。对输出来说,则要尽量晚地把字符串编码成字节序列。

在 Python 3 中能轻松地采纳 Unicode 三明治的建议,因为内置的 open 函数会在读取文件时做必要的解码,以文本模式写入文件时还会做必要 的编码,所以调用 my_file.read() 方法得到的以及传给 my_file.write(text) 方法的都是字符串对象

1
2
3
4
5
open('cafe.txt','w',encoding='utf_8').write('café')
print(open('cafe.txt').read())
#caf茅
print(open('cafe.txt',encoding='utf_8').read())
#'café'

写入文件时指定了 UTF-8 编码,但是读取文件时没有这么做, 因此 Python 假定要使用系统默认的编码(Windows 1252),于是文件的 最后一个字节解码成了字符 ‘é’,而不是 ‘é’。(我这vscode输出茅,输出的末尾多了变成了b’caf\xe8\x8c\x85’,也可能win11或者py3.9默认编码变了?后续测试是cp936编码)

仔细分析在 Windows 中运行的示例

1
2
3
4
5
6
7
8
9
print(open('cafe.txt','w',encoding='utf_8'))
#<_io.TextIOWrapper name='cafe.txt' mode='w' encoding='utf_8'>
#默认情况下,open 函数采用文本模式,返回一个 TextIOWrapper对象。
print(open('cafe.txt'))
#<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp936'>
#这里说明默认打开使用的是cp936编码
fp = open('cafe.txt','rb',encoding='utf_8').read() #'rb' 标志指明在二进制模式中读取文件。
print(fp)
#café

使用encoding='utf_8’打开则是对的

有几个设置对 Python I/O 的编码默认值有影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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()
"""

for expression in expressions.split():

value = eval(expression)
print(expression,value)


'''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'''

4.6 为了正确比较而规范化Unicode字符串

因为 Unicode 有组合字符(变音符号和附加到前一个字符上的记号,打 印时作为一个整体),所以字符串比较起来很复杂。 例如,“café”这个词可以使用两种方式构成,分别有 4 个和 5 个码位, 但是结果完全一样:

1
2
3
4
5
6
7
8
9
10
11
s1 = 'café'
s2 = 'cafe\u0301'
print(s1,s2)
#café café
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 看到的是不同的码位序列,因此判定二者不相等。 这个问题的解决方案是使用 unicodedata.normalize 函数提供的 Unicode 规范化。这个函数的第一个参数是这 4 个字符串中的一 个:‘NFC’、‘NFD’、‘NFKC’ 和 ‘NFKD’。下面说明前两个。

NFC(Normalization Form C)使用最少的码位构成等价的字符串,而 NFD 把组合字符分解成基字符和单独的组合字符。这两种规范化方式都 能让比较行为符合预期:s1,s2同上

1
2
3
4
5
from unicodedata import normalize
print(len(normalize('NFC',s1)),len(normalize('NFC',s2)))
#4 4
print(len(normalize('NFD',s1)),len(normalize('NFD',s2)))
#5 5

使用 NFC 时,有些单字符会被规范成另一个单字符。例如,电阻的单位欧姆(Ω)会被规范成希腊字母大写的欧米加。这两个字符在视觉上 是一样的,但是**比较时并不相等,因此要规范化,防止出现意外: ** 偷懒不敲了

image-20220328002138862

**保存文本之前,最好使用 normalize(‘NFC’, user_text) 清洗字符串。**NFC 也是 W3C 的“Character Model for the World Wide Web: String Matching and Searching”规范

在 NFKC 和 NFKD 形式中,各个兼容字符会被替换成一个或多个“兼容 分解”字符,即便这样有些格式损失,但仍是“首选”表述——理想情况 下,格式化是外部标记的职责,不应该由 Unicode 处理。下面举个例 子。二分之一 ‘½’(U+00BD)经过兼容分解后得到的是三个字符序列 ‘1/2’;微符号 ‘µ’(U+00B5)经过兼容分解后得到的是小写字母 ‘μ’(U+03BC)。偷懒x2

image-20220328231803755

4.6.1 大小写折叠

大小写折叠其实就是把所有文本变成小写,再做些其他转换。这个功能 由 str.casefold() 方法(Python 3.3 新增)支持。 对于只包含 latin1 字符的字符串 s,s.casefold() 得到的结果与 s.lower() 一样,唯有两个例外:微符号 ‘µ’ 会变成小写的希腊字 母“μ”(在多数字体中二者看起来一样);德语 Eszett(“sharp s”,ß) 会变成“ss”。

4.6.2 规范化文本匹配实用函数

由前文可知,NFC 和 NFD 可以放心使用,而且能合理比较 Unicode 字 符串。对大多数应用来说,NFC 是最好的规范化形式。不区分大小写的 比较应该使用 str.casefold()。 如果要处理多语言文本,工具箱中应该有示例 4-13 中的 nfc_equal 和 fold_equal 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from unicodedata import normalize
def nfc_equal(str1,str2):
return normalize('NFC', str1) == normalize('NFC', str2)
def fold_equal(str1,str2):
return (normalize('NFC', str1).casefold() == normalize('NFC', str2).casefold())

s1 = 'café'
s2 = 'cafe\u0301'
print (s1 == s2)
print(nfc_equal(s1, s2))
print(nfc_equal('A', 'a'))
'''
False
True
False
'''

4.6.3 极端“规范化”:去掉变音符号(跳)

Google 搜索涉及很多技术,其中一个显然是忽略变音符号(如重音符、 下加符等),至少在某些情况下会这么做。去掉变音符号不是正确的规 范化方式,因为这往往会改变词的意思,而且可能误判搜索结果。但是 对现实生活却有所帮助:人们有时很懒,或者不知道怎么正确使用变音 符号,而且拼写规则会随时间变化,因此实际语言中的重音经常变来变 去。 除了搜索,去掉变音符号还能让 URL更易于阅读,至少对拉丁语系语 言是如此。下面是维基百科中介绍圣保罗市(São Paulo)的文章的 URL:

1
2
3
4
5
6
7
8
import unicodedata
import string
def shave_marks(txt):
"""去掉全部变音符号"""
norm_txt = unicodedata.normalize('NFD', txt)
shaved = ''.join(c for c in norm_txt
if not unicodedata.combining(c))
return unicodedata.normalize('NFC', shaved)

4.7 Unicode文本排序

Python 比较任何类型的序列时,会一一比较序列里的各个元素。对字符 串来说,比较的是码位。可是在比较非 ASCII 字符时,得到的结果不尽如人意。 下面对一个生长在巴西的水果的列表进行排序:

1
2
3
4
fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
sorted(fruits)
print(fruits)#['caju', 'atemoia', 'cajá', 'açaí', 'acerola']

在 Python 中,非 ASCII 文本的标准排序方式是使用 locale.strxfrm 函数,根据 locale 模块的文档 (https://docs.python.org/3/library/locale.html? highlight=strxfrm#locale.strxfrm),这 个函数会“把字符串转换成适合所 在区域进行比较的形式”。

使用 locale.strxfrm 函数之前,必须先为应用设定合适的区域设置, 还要祈祷操作系统支持这项设置。在区域设为 pt_BR 的 GNU/Linux(Ubuntu 14.04)中,可以使用示例 4-19 中的命令。

1
2
3
4
5
6
7
import locale
locale.setlocale(locale.LC_COLLATE,'pt_BR.UTF-8')
fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
sorted_fruits = sorted(fruits, key=locale.strxfrm)
print(sorted_fruits)
#使用 locale.strxfrm 函数做排序键之前,要调用setlocale(LC_COLLATE, «your_locale»)。

使用Unicode排序算法排序

James Tauber,一位高产的 Django 贡献者,他一定是感受到了这一痛 点,因此开发了 PyUCA 库(https://pypi.python.org/pypi/pyuca/),这是 Unicode 排序算法(Unicode Collation Algorithm,UCA)的纯 Python 实现。

image-20220329200306193

4.8 Unicode数据库

Unicode 标准提供了一个完整的数据库(许多格式化的文本文件),不 仅包括码位与字符名称之间的映射,还有各个字符的元数据,以及字符 之间的关系。例如,Unicode 数据库记录了字符是否可以打印、是不是 字母、是不是数字,或者是不是其他数值符号。字符串的 isidentifier、isprintable、isdecimal 和 isnumeric 等方法就 是靠这些信息作判断的。 str.casefold 方法也用到了 Unicode 表中的信息。 unicodedata 模块中有几个函数用于获取字符的元数据。例如,字符在标准中的官方名称是不是组合字符(如结合波形符构成的变音符号等), 以及符号对应的人类可读数值(不是码位)。

unicodedata.name() 和 unicodedata.numeric() 函数,以及字符串的 .isdecimal() 和 .isnumeric() 方法的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import unicodedata
import re
re_digit = re.compile(r'\d')
sample = '1\xbc\xb2\u0969\u136b\u216b\u2466\u2480\u3285'
for char in sample:
print('U+%04x' % ord(char),
char.center(6),
're_dig' if re_digit.match(char) else '-',
'isdig' if char.isdigit() else '-',
'isnum' if char.isnumeric() else '-',
format(unicodedata.numeric(char), '5.2f'),
unicodedata.name(char),
sep='\t')
➊ U+0000 格式的码位。
➋ 在长度为 6 的字符串中居中显示字符。
➌ 如果字符匹配正则表达式 r'\d',显示 re_dig。
➍ 如果 char.isdigit() 返回 True,显示 isdig。
➎ 如果 char.isnumeric() 返回 True,显示 isnum。
➏ 使用长度为 5、小数点后保留 2 位的浮点数显示数值。
➐ Unicode 标准中字符的名称。
'''

U+00b2 ² - isdig isnum 2.00 SUPERSCRIPT TWO
U+0969 ३ re_dig isdig isnum 3.00 DEVANAGARI DIGIT THREEU+136b ፫ - isdig isnum 3.00 ETHIOPIC DIGIT THREE
U+216b Ⅻ - - isnum 12.00 ROMAN NUMERAL TWELVE
U+2466 ⑦ - isdig isnum 7.00 CIRCLED DIGIT SEVEN
U+2480 ⒀ - - isnum 13.00 PARENTHESIZED NUMBER THIRTEENU+3285 ㊅ - - isnum 6.00 CIRCLED IDEOGRAPH SIX
'''

4.9 支持字符串和字节序列的双模式API

标准库中的一些函数能接受字符串或字节序列为参数,然后根据类型展 现不同的行为。re 和 os 模块中就有这样的函数。

4.9.1 正则表达式中的字符串和字节序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#4.9
import re
re_number_str = re.compile(r'\d+')
re_word_str = re.compile(r'\w+')
re_numbers_bytes = re.compile(rb'\d+')
re_words_bytes = re.compile(rb'\w+')

text_str = ("Ramanujan saw \u0be7\u0bed\u0be8\u0bef"
" as 1729 = 1³ + 12³ = 9³ + 10³.")

text_bytes = text_str.encode('utf_8')

print('Text', repr(text_str), sep='\n ')
print('Numbers')
print(' str :', re_number_str.findall(text_str))
print(' bytes:', re_numbers_bytes.findall(text_bytes))
print('Words')
print(' str :', re_word_str.findall(text_str))
print(' bytes:', re_words_bytes.findall(text_bytes))

'''
str : ['௧௭௨௯', '1729', '1', '12', '9', '10']
bytes: [b'1729', b'1', b'12', b'9', b'10']
Words
str : ['Ramanujan', 'saw', '௧௭௨௯', 'as', '1729', '1³', '12³', '9³', '10³']
bytes: [b'Ramanujan', b'saw', b'as', b'1729', b'1', b'12', b'9', b'10']
'''
'''
字符串模式 r'\d+' 能匹配泰米尔数字和 ASCII 数字。
字节序列模式 rb'\d+' 只能匹配 ASCII 字节中的数字。
字符串模式 r'\w+' 能匹配字母、上标、泰米尔数字和 ASCII 数字。
字节序列模式 rb'\w+' 只能匹配 ASCII 字节中的字母和数字。

'''

4.9.2 os函数中的字符串和字节序列(跳)

GNU/Linux 内核不理解 Unicode,因此你可能发现了,对任何合理的编 码方案来说,在文件名中使用字节序列都是无效的,无法解码成字符 串。在不同操作系统中使用各种客户端的文件服务器,在遇到这个问题 时尤其容易出错。 为了规避这个问题,os 模块中的所有函数、文件名或路径名参数既能 使用字符串,也能使用字节序列。如果这样的函数使用字符串参数调 用,该参数会使用 sys.getfilesystemencoding() 得到的编解码器 自动编码,然后操作系统会使用相同的编解码器解码。这几乎就是我们 想要的行为,与 Unicode 三明治最佳实践一致。 但是,如果必须处理(也可能是修正)那些无法使用上述方式自动处理 的文件名,可以把字节序列参数传给 os 模块中的函数,得到字节序列 返回值。这一特性允许我们处理任何文件名或路径名,不管里面有多少 鬼符

image-20220329231114828

fsencode(filename)

如果 filename 是 str 类型(此外还可能是 bytes 类型),使用 sys.getfilesystemencoding() 返回的编解码器把 filename 编码成 字节序列;否则,返回未经修改的 filename 字节序列。

fsdecode(filename)

如果 filename 是 bytes 类型(此外还可能是 str 类型),使用 sys.getfilesystemencoding() 返回的编解码器把 filename 解码成 字符串;否则,返回未经修改的 filename 字符串。 在 Unix 衍生平台中,这些函数使用 surrogateescape 错误处理方式 (参见下述附注栏)以避免遇到意外字节序列时卡住。Windows 使用的 错误处理方式是 strict。

image-20220329231425423

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!,本博客仅用于交流学习,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 文章作者拥有对此站文章的修改和解释权。如欲转载此站文章,需取得作者同意,且必须保证此文章的完整性,包括版权声明等全部内容。未经文章作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。若造成严重后果,本人将依法追究法律责任。 阅读本站文章则默认遵守此规则。