PHP前端开发

使用 Python os 模块和 Unicode 进行字符编码

百变鹏仔 4天前 #Python
文章标签 字符

今天我意识到我没有发表一些关于如何做的笔记:

处理未指定的字符编码。

这是我在让我的程序 foldatry 处理旧文件系统时必须解决的问题。

请注意,这仅涉及文件和文件夹名称,我尚未检查文件内容的情况。

立即学习“Python免费学习笔记(深入)”;

我的决定是:

 pythondef pfn4print( p_pfn ):    return p_pfn.encode('utf-8', 'surrogateescape').decode('cp1252', 'backslashreplace') 

请注意,cp1252 是 iso-8859-1 编码标准的 windows-1252 超集。

该代码的基本原理是:

然后为了显示,它使用 windows-1252 编解码器,原因有两个:

替代方案是始终将所有路径和文件名作为字节字符串处理,这似乎工作量太大。

现在,如果您对 unicode 了解很多,您可能会知道并非所有字节序列在其标准编码中都是有效的。

那么当 python os 模块遇到这些(即在字节序列中,它没有被告知如何解码)时,它会做什么?

嗯,这就是“surrogateescape”的用武之地,因为这实际上让 python 将错误的字节序列存储为:

这就是为什么encode('utf-8', 'surrogateescape') 给我们返回原始字节。

现在我承认这看起来有点“神奇”。虽然您当然可以更深入地了解如何:

但我的猜测是,你不需要走那么深,只需相信“surrogateescape”就能完成它的伎俩。

请注意,这都是为了程序不会崩溃处理。最终,正确处理未指定的字符编码就是确定哪种编码对其有效。有一些工具可以很好地做到这一点,但要确定它需要人类的判断 - 本质上是因为它是由人类的不幸事件造成的。

附:下面的函数将字符串转换为其 unicode 序数列表(即代码点序列)。方便检查 python 真正认为的 unicode 字符串。

 pythondef string_as_list_of_code_points( p_str):    return list( ord(a_char) for a_char in list(p_str) )

实施例1

这来自我在 windows 98 时代制作的 cd-rom。因此,文件名肯定不是以 unicode 形式完成的。

我们的示例文件名是:

编码

在 cd-rom 上,编码是这三个字节的序列:

通过 os 模块读入 python3 - 它变成以下 unicode 代码点序列:

如果我们成功转换 - 见下文 - 那么 python 将保存这个 unicode“代码点”序列:

如果 python 将其写入 utf-8,它将变成这四个字节:

我对 windows-nt 内部使用的理解是“ucs-2”,以字节为单位,将是:

参见:

python序列

好吧,让我们看看它在 python 交互式会话中是如何工作的。以下内容是从终端窗口剪下来的。

$ python3python 3.10.12 (main, sep 11 2024, 15:47:36) [gcc 11.4.0] on linuxtype "help", "copyright", "credits" or "license" for more information.>>> s_0 = "für">>> print( list( ord(a_char) for a_char in list(s_0) ) )[102, 252, 114]>>> ba_1 = s_0.encode( "utf-8", "surrogateescape")>>> print( list( ba_1 ) )[102, 195, 188, 114]>>> ba_2 = s_0.encode("cp1252", "backslashreplace")>>> print( list( ba_2 ) )[102, 252, 114]>>> s_1 = ba_2.decode("utf-8", "surrogateescape")>>> print( list( ord(a_char) for a_char in list(s_1) ) )[102, 56572, 114]>>> ba_3 = s_1.encode( "utf-8", "surrogateescape")>>> print( list( ba_2 ) )[102, 252, 114]>>> s_2 = ba_3.decode("cp1252")>>> print( list( ord(a_char) for a_char in list(s_2) ) )[102, 252, 114]>>> print( s_2)für

回顾一下之前的评论:

根据您对代码点和编码的熟悉程度,可能会有两个惊喜:

同样,可能不会引起您注意的是:

哦,还有:

实施例2

现在让我们看一个示例,其中字符的 windows-1252 字节编码字节与其 unicode 代码点编号匹配。

参见:

python序列

这里的操作序列与我们在示例 1 中执行的操作相同,但这次使用了以欧元符号开头的短字符串。

>>> s_0 = "€30">>> print( list( ord(a_char) for a_char in list(s_0) ) )[8364, 51, 48]>>> ba_1 = s_0.encode( "utf-8", "surrogateescape")>>> print( list( ba_1 ) )[226, 130, 172, 51, 48]>>> ba_2 = s_0.encode("cp1252", "backslashreplace")>>> print( list( ba_2 ) )[128, 51, 48]>>> s_1 = ba_2.decode("utf-8", "surrogateescape")>>> print( list( ord(a_char) for a_char in list(s_1) ) )[56448, 51, 48]>>> ba_3 = s_1.encode( "utf-8", "surrogateescape")>>> print( list( ba_2 ) )[128, 51, 48]>>> s_2 = ba_3.decode("cp1252")>>> print( list( ord(a_char) for a_char in list(s_2) ) )[8364, 51, 48]>>> print( s_2)€30

这次注意事项:

尾注

我也许应该补充一点,我并不声称这里显示的方法是完整的或无懈可击的。这只是我添加到我的程序中以应对它所遇到的情况的内容。我确实计划在晚些时候重新审视这一切,但还有很多重要的其他功能将首先出现。所以,根本不是“现在很快”。