讲师中心 微信公众号
AI工具推荐 视频效率加速

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

胖伟君_3331

胖伟君_3331

发布时间:2024-10-06 08:06:23

|

481人浏览过

|

来源于dev.to

转载

使用 python os 模块和 unicode 进行字符编码

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

  • python,以及
  • 它的“os”模块

处理未指定的字符编码。

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

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

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

我的决定是:

  • 让 python os 模块从文件系统获取名称并将它们放入 python unicode 字符串中 - 也就是说,我不会编写代码来告诉它如何解释名称的编码,直到之后 它将它们存储在 python 变量中;
  • 编写以下函数用于显示路径文件名(到终端或屏幕小部件) - 这至关重要,因为否则程序就会崩溃。
 python
def pfn4print( p_pfn ):
    return p_pfn.encode('utf-8', 'surrogateescape').decode('cp1252', 'backslashreplace') 



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

该代码的基本原理是:

  • python os 模块默认将从文件系统获取的字节放入 python unicode 字符串中;
  • 然后我们想将其转回原始字节序列,这是通过encode('utf-8', 'surrogateescape')实现的
  • 这是有效的,因为 os 模块的文档说这就是制作 python unicode 字符串的方法,即它使用了“surrogateescape”

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

  • 它在很长一段时间内是最常见的:比早期的 ms-dos 集和其他语言集都长;
  • 因为它几乎可以表示 8 位字节模式的所有 256 个字符,所以它通常应该显示一些内容,即使它不是最初的预期内容。

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

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

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

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

  • 无效;和
  • 它的顺序是什么。

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

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

  • python 在内部处理 unicode;
  • 以及它知道的所有不同编码;
  • 以及编码和解码时可用的所有选项;

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

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

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

macOS UI 自动化技能包 —— 用 Python 控制你的 Mac
macOS UI 自动化技能包 —— 用 Python 控制你的 Mac

让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限

下载
 python
def 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 形式完成的。

我们的示例文件名是:

  • 毛皮 中间的字母是“带有分音符的拉丁文小写字母 u”

编码

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

  • 0x66 0xfc 0x72

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

  • u+0066 u+fffd!!fc u+0072

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

  • u+0066 u+00fc u+0072

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

  • 0x66 0xc3 0xbc 0x72 是:一个一字节字符,一个二字节字符,一个一字节字符。

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

  • 0x00 0x66 0x00 0xfc 0x00 0x72 是三个两字节字符。

参见:

  • 带有分音符号的拉丁文小写字母 u
  • 来自 unicode 组:latin-1 supplement

python序列

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


$ python3
python 3.10.12 (main, sep 11 2024, 15:47:36) [gcc 11.4.0] on linux
type "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



回顾一下之前的评论:

  • ba_2 是该字符串在 cd-rom 上的存放方式
  • s_1 是当我们要求 python 操作系统在不指定编码的情况下读取 ba_2 时发生的情况 - 因此它假定 utf-8 但随后必须处理 252 作为 utf-* 字符的第一个字节无效的事实,所以操作系统将其存储为代理
  • 我们可以看到它是如何在 ba_1 中正确编码为 utf-8

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

  • 三个字符的 utf-8 编码需要四个字节 - 本质上是因为“ü”需要两个字节
  • “在 utf-8 中无效的事情”的问题会以一种奇怪的方式处理,但这是可以逆转的

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

  • 这个示例仍然简单,因为“ü”的旧式 windows-1252 字节编码与其 unicode 代码点编号相匹配:252

哦,还有:

  • 我实际上不记得我从哪里得到 u+fffd!!fc 符号。
  • 我什至可能已经编造了它,作为一种混合方式来表达“将显示为 unicode 替换字符,但知道它来自十六进制 fc”
  • 如果你查看 python 会话,你会看到 s_1 中间的代码点是十进制 56572,即十六进制 0xdcfc

实施例2

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

  • 欧元符号
  • 30 欧元
  • 在 windows-1252 中编码为 0x80
  • 在 unicode 中,这是代码点 u+20ac(十进制 8364)
  • (如果您想知道,这种差异是因为 microsoft 决定使用“c1 控制代码”范围内的位置,而 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



这次注意事项:

  • ba_1 是 utf-8 编码,注意欧元符号需要 三个 个字节
  • ba_2 是该字符串在 windows 98 等中的形式
  • s_1 是当我们要求 python 操作系统在不指定编码的情况下读取 ba_2 时发生的情况 - 因此它假定 utf-8 但随后必须处理它所得到的内容
  • 我们仍然存在以奇怪的方式处理“在 utf-8 中无效的事情”的问题,但这可以逆转
  • 此时你会看到,s_1前面的码位是十进制的56448,也就是十六进制的0xdc80

尾注

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

热门AI工具

更多
VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

相关专题

更多
js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1798

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2824

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6164

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

5929

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

7534

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

11214

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

5759

2025.07.29

c++字符串相关教程
c++字符串相关教程

本专题整合了c++字符串相关教程,阅读专题下面的文章了解更多详细内容。

6327

2025.08.07

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

100

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Python uv官方安装与入门文档
Python uv官方安装与入门文档

共0课时 | 0人学习

Buffalo框架快速入门指南
Buffalo框架快速入门指南

共0课时 | 0人学习

Valgrind支持平台说明
Valgrind支持平台说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn