为什么 'é' 和 'é' 编码为不同的字节?
Why is 'é' and 'é' encoding to different bytes?
问题
为什么相同的字符在我的代码库的不同部分编码为不同的字节?
上下文
我有一个生成临时文件树的单元测试,然后检查以确保我的扫描确实找到了有问题的文件。
def test_unicode_file_name():
test_regex = "é"
file_tree = {"files": ["é"]} # File created with python.open()
with TempTree(file_tree) as tmp_tree:
import pdb; pdb.set_trace()
result = tasks.find_files(test_regex, root_path=tmp_tree.root_path)
expected = [os.path.join(tmp_tree.root_path, "é")]
assert result == expected
函数失败
for dir_entry in scandir(current_path):
if dir_entry.is_dir():
dirs_to_search.append(dir_entry.path)
if dir_entry.is_file():
testing = dir_entry.name
if filename_regex.match(testing):
results.append(dir_entry.path)
PDB 会话
当我开始深入研究时,我发现测试字符(从我的单元测试中复制)和 dir_entry.name
中的字符编码为不同的字节。
(Pdb) testing
'é'
(Pdb) 'é'
'é'
(Pdb) testing == 'é'
False
(Pdb) testing in 'é'
False
(Pdb) type(testing)
<class 'str'>
(Pdb) type('é')
<class 'str'>
(Pdb) repr(testing)
"'é'"
(Pdb) repr('é')
"'é'"
(Pdb) 'é'.encode("utf-8")
b'\xc3\xa9'
(Pdb) testing.encode("utf-8")
b'e\xcc\x81'
您的操作系统(猜测是 MacOS)已将文件名 'é'
转换为 Unicode Normal Form D,并将其分解为无重音 'e'
和组合重音。您可以通过 Python 解释器中的快速会话清楚地看到这一点:
>>> import unicodedata
>>> e1 = b'\xc3\xa9'.decode()
>>> e2 = b'e\xcc\x81'.decode()
>>> [unicodedata.name(c) for c in e1]
['LATIN SMALL LETTER E WITH ACUTE']
>>> [unicodedata.name(c) for c in e2]
['LATIN SMALL LETTER E', 'COMBINING ACUTE ACCENT']
为确保您进行同类比较,您可以将 dir_entry.name
给出的文件名转换回 Normal Form C,然后再针对您的正则表达式进行测试:
import unicodedata
for dir_entry in scandir(current_path):
if dir_entry.is_dir():
dirs_to_search.append(dir_entry.path)
if dir_entry.is_file():
testing = unicodedata.normalize('NFC', dir_entry.name)
if filename_regex.match(testing):
results.append(dir_entry.path)
问题
为什么相同的字符在我的代码库的不同部分编码为不同的字节?
上下文
我有一个生成临时文件树的单元测试,然后检查以确保我的扫描确实找到了有问题的文件。
def test_unicode_file_name():
test_regex = "é"
file_tree = {"files": ["é"]} # File created with python.open()
with TempTree(file_tree) as tmp_tree:
import pdb; pdb.set_trace()
result = tasks.find_files(test_regex, root_path=tmp_tree.root_path)
expected = [os.path.join(tmp_tree.root_path, "é")]
assert result == expected
函数失败
for dir_entry in scandir(current_path):
if dir_entry.is_dir():
dirs_to_search.append(dir_entry.path)
if dir_entry.is_file():
testing = dir_entry.name
if filename_regex.match(testing):
results.append(dir_entry.path)
PDB 会话
当我开始深入研究时,我发现测试字符(从我的单元测试中复制)和 dir_entry.name
中的字符编码为不同的字节。
(Pdb) testing
'é'
(Pdb) 'é'
'é'
(Pdb) testing == 'é'
False
(Pdb) testing in 'é'
False
(Pdb) type(testing)
<class 'str'>
(Pdb) type('é')
<class 'str'>
(Pdb) repr(testing)
"'é'"
(Pdb) repr('é')
"'é'"
(Pdb) 'é'.encode("utf-8")
b'\xc3\xa9'
(Pdb) testing.encode("utf-8")
b'e\xcc\x81'
您的操作系统(猜测是 MacOS)已将文件名 'é'
转换为 Unicode Normal Form D,并将其分解为无重音 'e'
和组合重音。您可以通过 Python 解释器中的快速会话清楚地看到这一点:
>>> import unicodedata
>>> e1 = b'\xc3\xa9'.decode()
>>> e2 = b'e\xcc\x81'.decode()
>>> [unicodedata.name(c) for c in e1]
['LATIN SMALL LETTER E WITH ACUTE']
>>> [unicodedata.name(c) for c in e2]
['LATIN SMALL LETTER E', 'COMBINING ACUTE ACCENT']
为确保您进行同类比较,您可以将 dir_entry.name
给出的文件名转换回 Normal Form C,然后再针对您的正则表达式进行测试:
import unicodedata
for dir_entry in scandir(current_path):
if dir_entry.is_dir():
dirs_to_search.append(dir_entry.path)
if dir_entry.is_file():
testing = unicodedata.normalize('NFC', dir_entry.name)
if filename_regex.match(testing):
results.append(dir_entry.path)