Python 文本和字节序列(处理文本文件)

本章将讨论下述话题:
字符、码位和字节表述
bytes、bytearray 和 memoryview 等二进制序列的独特特性
全部 Unicode 和陈旧字符集的编解码器
避免和处理编码错误
处理文本文件的最佳实践
默认编码的陷阱和标准 I/O 的问题
规范化 Unicode 文本,进行安全的比较
规范化、大小写折叠和暴力移除音调符号的实用函数
使用 locale 模块和 PyUCA 库正确地排序 Unicode 文本
Unicode 数据库中的字符元数据
能处理字符串和字节序列的双模式 API

处理文本文件

处理文本的最佳实践是“Unicode 三明治”(如图 4-2 所示)。 意思是,
要尽早把输入(例如读取文件时)的字节序列解码成字符串。这种三明
治中的“肉片”是程序的业务逻辑,在这里只能处理字符串对象。在其他
处理过程中,一定不能编码或解码。对输出来说,则要尽量晚地把字符
串编码成字节序列。多数 Web 框架都是这样做的,使用框架时很少接
触字节序列。例如,在 Django 中,视图应该输出 Unicode 字符串;
Django 会负责把响应编码成字节序列,而且默认使用 UTF-8 编码。

Unicode 三明治——目前处理文本的最佳实践

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

可以看出,处理文本文件很简单。但是,如果依赖默认编码,你会遇到
麻烦。
看一下示例 4-9 中的控制台会话。你能发现问题吗?

示例 4-9 一个平台上的编码问题(如果在你的机器上运行,它可
能会发生,也可能不会)

>>> open('cafe.txt', 'w', encoding='utf_8').write('café')
4 >>> open('cafe.txt').read()
'café'

问题是:写入文件时指定了 UTF-8 编码,但是读取文件时没有这么做,
因此 Python 假定要使用系统默认的编码(Windows 1252),于是文件的
最后一个字节解码成了字符 ‘é’,而不是 ‘é’。

我是在 Windows 7 中运行示例 4-9 的。在新版 GNU/Linux 或 Mac OS X
中运行同样的语句不会出问题,因为这几个操作系统的默认编码是
UTF-8,让人误以为一切正常。如果打开文件是为了写入,但是没有指
定编码参数,会使用区域设置中的默认编码,而且使用那个编码也能正
确读取文件。但是,如果脚本要生成文件,而字节的内容取决于平台或
同一平台中的区域设置,那么就可能导致兼容问题。

需要在多台设备中或多种场合下运行的代码,一定不能依赖
默认编码。打开文件时始终应该明确传入 encoding= 参数,因为
不同的设备使用的默认编码可能不同,有时隔一天也会发生变化。

示例 4-9 中有个奇怪的细节:第一个语句中的 write 函数报告写入了 4
个字符,但是下一行读取时却得到了 5 个字符。示例 4-10 是对示例 4-9
的扩展,对这个问题以及其他细节做了说明。

示例 4-10 仔细分析在 Windows 中运行的示例 4-9,找出并修正问

>>> fp = open('cafe.txt', 'w', encoding='utf_8')
>>> fp ➊
<_io.TextIOWrapper name='cafe.txt' mode='w' encoding='utf_8'>
>>> fp.write('café')
4 ➋
>>> fp.close()
>>> import os
>>> os.stat('cafe.txt').st_size
5 ➌
>>> fp2 = open('cafe.txt')
>>> fp2 ➍
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp1252'>
>>> fp2.encoding ➎
'cp1252'
>>> fp2.read()
'café' ➏
>>> fp3 = open('cafe.txt', encoding='utf_8') ➐
>>> fp3
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='utf_8'>
>>> fp3.read()
'café' ➑
>>> fp4 = open('cafe.txt', 'rb') ➒
>>> fp4
<_io.BufferedReader name='cafe.txt'> ➓
>>> fp4.read() ⓫
b'caf\xc3\xa9'

❶ 默认情况下,open 函数采用文本模式,返回一个 TextIOWrapper
对象。
❷ 在 TextIOWrapper 对象上调用 write 方法返回写入的 Unicode 字符
数。
❸ os.stat 报告文件中有 5 个字节;UTF-8 编码的 ‘é’ 占两个字节,
0xc3 和 0xa9。
❹ 打开文本文件时没有显式指定编码,返回一个 TextIOWrapper 对
象,编码是区域设置中的默认值。
❺ TextIOWrapper 对象有个 encoding 属性;查看它,发现这里的编
码是 cp1252。
❻ 在 Windows cp1252 编码中,0xc3 字节是“Ô(带波形符的 A),
0xa9 字节是版权符号。

❼ 使用正确的编码打开那个文件。
❽ 结果符合预期:得到的是四个 Unicode 字符 ‘café’。
❾ ‘rb’ 标志指明在二进制模式中读取文件。
❿ 返回的是 BufferedReader 对象,而不是 TextIOWrapper 对象。
⓫读取返回的字节序列,结果与预期相符。

除非想判断编码,否则不要在二进制模式中打开文本文件;
即便如此,也应该使用 Chardet,而不是重新发明轮子(参见 4.4.4
节)。常规代码只应该使用二进制模式打开二进制文件,如光栅图
像。

示例 4-10 的问题是,打开文本文件时依赖默认设置。默认设置有许多
来源,参见下一节。

编码默认值:一团糟
有几个设置对 Python I/O 的编码默认值有影响,如示例 4-11 中的
default_encodings.py 脚本所示。
示例 4-11 探索编码默认值

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()
"""
my_file = open('dummy', 'w')
for expression in expressions.split():
value = eval(expression)
print(expression.rjust(30), '->', repr(value))

示例 4-11 在 GNU/Linux(Ubuntu 14.04)和 OS X(Mavericks 10.9)中的
输出一样,表明这些系统中始终使用 UTF-8:

$ python3 default_encodings.py
locale.getpreferredencoding() -> 'UTF-8'type(my_file) -> <class '_io.TextIOWrapper'>my_file.encoding -> 'UTF-8'sys.stdout.isatty() -> Truesys.stdout.encoding -> 'UTF-8'sys.stdin.isatty() -> Truesys.stdin.encoding -> 'UTF-8'sys.stderr.isatty() -> Truesys.stderr.encoding -> 'UTF-8'sys.getdefaultencoding() -> 'utf-8'
sys.getfilesystemencoding() -> 'utf-8'

然而,在 Windows 中的输出有所不同,如示例 4-12 所示。
示例 4-12 在Windows 7(SP1)巴西版中的 cmd.exe 中输出的默认
编码;PowerShell 输出的结果相同

Z:\>chcp ➊
Página de código ativa: 850
Z:\>python default_encodings.py ➋
locale.getpreferredencoding() -> 'cp1252' ➌type(my_file) -> <class '_io.TextIOWrapper'>my_file.encoding -> 'cp1252' ➍sys.stdout.isatty() -> True ➎sys.stdout.encoding -> 'cp850' ➏sys.stdin.isatty() -> Truesys.stdin.encoding -> 'cp850'sys.stderr.isatty() -> Truesys.stderr.encoding -> 'cp850'sys.getdefaultencoding() -> 'utf-8'
sys.getfilesystemencoding() -> 'mbcs'

➊ chcp 输出当前控制台激活的代码页:850。
➋ 运行 default_encodings.py,把结果输出到控制台。
➌ locale.getpreferredencoding() 是最重要的设置。
➍ 文本文件默认使用 locale.getpreferredencoding()。
➎ 输出到控制台中,因此 sys.stdout.isatty() 返回 True。
➏ 因此,sys.stdout.encoding 与控制台的编码相同。

如果把输出重定向到文件,如下所示:

Z:\>python default_encodings.py > encodings.log

sys.stdout.isatty() 的返回值会变成
False,sys.stdout.encoding 会设为
locale.getpreferredencoding(),在那台设备中是 ‘cp1252’。

注意,示例 4-12 中有 4 种不同的编码。

如果打开文件时没有指定 encoding 参数,默认值由
locale.getpreferredencoding() 提供(在示例 4-12 中是
‘cp1252’)。

如果设定了 PYTHONIOENCODING 环境变量
(https://docs.python.org/3/using/cmdline.html#envvar-
PYTHONIOENCODING),sys.stdout/stdin/stderr 的编码使
用设定的值;否则,继承自所在的控制台;如果输入 / 输出重定向
到文件,则由 locale.getpreferredencoding() 定义。

Python 在二进制数据和字符串之间转换时,内部使用
sys.getdefaultencoding() 获得的编码;Python 3 很少如此,但
仍有发生。6 这个设置不能修改。

sys.getfilesystemencoding() 用于编解码文件名(不是文件内
容)。把字符串参数作为文件名传给 open() 函数时就会使用它;
如果传入的文件名参数是字节序列,那就不经改动直接传给 OS
API。“Unicode HOWTO”一文
(https://docs.python.org/3/howto/unicode.html)中说:“在 Windows
中,Python 使用 mbcs 这个名称引用当前配置的编码。”MBCS 是
Multi Byte Character Set(多字节字符集)的首字母缩写,在
Windows 中是陈旧的变长编码,如 gb2312 或 Shift_JIS,而不是
UTF-8。[ 关于这个话题,Stack Overflow 中有一个很好的回答“,
Difference between MBCS and UTF-8 on
Windows”(http://stackoverflow.com/questions/3298569/differencebetween-
mbcs-and-utf-8-on-windows)。]

在 GNU/Linux 和 OS X 中,这些编码的默认值都是 UTF-8,而
且多年来都是如此,因此 I/O 能处理所有 Unicode 字符。在
Windows 中,不仅同一个系统中使用不同的编码,还有只支持
ASCII 和 127 个额外的字符的代码页(如 ‘cp850’ 或
‘cp1252’),而且不同的代码页之间增加的字符也有所不同。因
此,若不多加小心,Windows 用户更容易遇到编码问题。

综上,locale.getpreferredencoding() 返回的编码是最重要的:这
是打开文件的默认编码,也是重定向到文件的
sys.stdout/stdin/stderr 的默认编码。然而,文档也说道(摘录部
分,https://docs.python.org/3/library/locale.html#locale.getpreferredencoding。

locale.getpreferredencoding(do_setlocale=True)

根据用户的偏好设置,返回文本数据的编码。用户的偏好设置在不
同系统中的设定方式不同,而且在某些系统中可能无法通过编程方
式设置,因此这个函数返回的只是猜测的编码……

因此,关于编码默认值的最佳建议是:别依赖默认值。
如果遵从 Unicode 三明治的建议,而且始终在程序中显式指定编码,那
将避免很多问题。可惜,即使把字节序列正确地转换成字符串,
Unicode 仍有不尽如人意的地方。接下来的两节讨论的话题对 ASCII 世
界来说很简单,但是在 Unicode 领域就变得相当复杂:文本规范化(即
为了比较而把文本转换成统一的表述)和排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76313.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Android学习记录】工具使用

文章目录 一. 精准找视图资源ID1. 准备工作2. 使用 uiautomator 工具2.1. 获取设备的窗口内容2.2. Pull XML 文件2.3. 查看 XML 文件 3. 直接使用 ADB 命令4. 使用 Android Studio 的 Layout Inspector总结 二. adb shell dumpsys activity1. 如何使用 ADB 命令2. 输出内容解析…

Kafka系列之:计算kafka集群topic占的存储大小

Kafka系列之:计算kafka集群topic占的存储大小 topic存储数据格式统计topic存储大小定时统计topic存储大小topic存储数据格式 单位是字节大小 size_bytes{directory="/data/datum/kafka/optics-all" } 782336计算topic存储大小脚本逻辑是: 计算指定目录或文件的大小…

C# 高级编程:Lambda 表达式

在 C# 的高级编程中,Lambda 表达式是一个强大而灵活的工具,广泛应用于 LINQ 查询、委托、事件处理以及函数式编程等多个领域。它不仅使代码更简洁、表达更直接,而且在某些场景中能极大提高代码的可读性与可维护性。本文将从 Lambda 表达式的基本语法入手,深入探讨其原理、常…

《软件设计师》复习笔记(11.5)——测试原则、阶段、测试用例设计、调试

目录 1. 测试基础概念 2. 测试方法分类 3. 测试阶段 真题示例&#xff1a; 题目1 题目2 题目3 4. 测试策略 5. 测试用例设计 真题示例&#xff1a; 6. 调试与度量 真题示例&#xff1a; 1. 测试基础概念 定义&#xff1a;系统测试是为发现错误而执行程序的过程&…

方案解读:虚拟电厂标杆项目整体建设方案【附全文阅读】

在电力市场背景下,传统电力现货市场存在电能定价不合理、分布式电源并网困难等问题。本虚拟电厂标杆项目旨在研究全时间尺度虚拟电厂智能管控关键技术,通过研制虚拟电厂控制器样机、开发运行管理平台,实现对分布式能源的合理优化配置。项目内容涵盖虚拟调控、建设目标、建设…

PyTorch 深度学习实战(37):分布式训练(DP/DDP/Deepspeed)实战

在上一篇文章中&#xff0c;我们探讨了混合精度训练与梯度缩放技术。本文将深入介绍分布式训练的三种主流方法&#xff1a;Data Parallel (DP)、Distributed Data Parallel (DDP) 和 DeepSpeed&#xff0c;帮助您掌握大规模模型训练的关键技术。我们将使用PyTorch在CIFAR-10分类…

OpenAI重返巅峰:o3与o4-mini引领AI推理新时代

引言 2025年4月16日&#xff0c;OpenAI发布了全新的o系列推理模型&#xff1a;o3和o4-mini&#xff0c;这两款模型被官方称为“迎今为止最智能、最强大的大语言模型&#xff08;LLM&#xff09;”。它们不仅在AI推理能力上实现了质的飞跃&#xff0c;更首次具备了全面的工具使…

【AI插件开发】Notepad++ AI插件开发实践:支持配置界面

一、引用 此前的系列文章已基本完成了Notepad的AI插件的功能开发&#xff0c;但是此前使用的配置为JSON配置文件&#xff0c;不支持界面配置。 本章在此基础上集成支持配置界面&#xff0c;这样不需要手工修改配置文件&#xff0c;直接在界面上操作&#xff0c;方便快捷。 注…

Android12 ServiceManager::addService源码解读

源码 Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {auto ctx mAccess->getCallingContext();// apps cannot add servicesif (multiuser_get_app_id(ctx.uid) >…

第十四节:实战场景-何实现全局状态管理?

React.createElement调用示例 Babel插件对JSX的转换逻辑 React 全局状态管理实战与 JSX 转换原理深度解析 一、React 全局状态管理实现方案 1. Context API useReducer 方案&#xff08;轻量级首选&#xff09; // 创建全局 Context 对象 const GlobalContext createConte…

第四十八篇 电信行业数仓建设实战指南:从架构设计到场景落地

目录 一、云原生架构设计实战1.1 计算存储分离架构搭建1.2 实时离线融合方案 二、维度建模深度解析2.1 电信业务建模方法论2.2 典型模型设计示例 三、ETL流程优化实践3.1 增量同步技术选型3.2 数据清洗规范 四、核心场景实现方案4.1 用户流失预警模型 五、数据治理实施指南5.1 …

2025年山东燃气瓶装送气工考试真题练习

燃气瓶装送气工考试真题练习 单选题 1、液化石油气主要成分是&#xff08; &#xff09;。 A. 甲烷 B. 丙烷、丁烷 C. 一氧化碳和氢气 答案&#xff1a;B 2、燃气钢瓶搬运过程中&#xff0c;正确的做法是&#xff08; &#xff09;。 A. 滚动钢瓶 B. 踢钢瓶 C. 轻拿轻…

《AI大模型应知应会100篇》第24篇:限定输出格式:如何让AI回答更加结构化

第24篇&#xff1a;限定输出格式&#xff1a;如何让AI回答更加结构化 摘要 在日常使用AI的过程中&#xff0c;我们经常希望得到的不仅仅是“正确”的答案&#xff0c;更是一个清晰、规范、易于处理的回答。无论是生成数据分析报告、代码片段&#xff0c;还是教学内容&#xff…

【MySQL】数据库和表的操作详解

目录 一、数据库&#xff1a; 1、查看数据库&#xff1a; 2、创建数据库&#xff1a; 3、删除数据库&#xff1a; 4、数据库的编码问题&#xff1a; 5、校验规则对数据库的影响&#xff1a; 6、修改数据库&#xff1a; 7、库的备份与恢复&#xff1a; 8、查看链接情况…

Docker--Docker镜像原理

docker 是操作系统层的虚拟化&#xff0c;所以 docker 镜像的本质是在模拟操作系统。 联合文件系统&#xff08;UnionFS&#xff09; 联合文件系统&#xff08;UnionFS&#xff09; 是Docker镜像实现分层存储的核心技术&#xff0c;它通过将多个只读层&#xff08;Image Laye…

双层Key缓存

双层 Key 缓存是一种针对 缓存击穿 和 雪崩问题 的优化方案&#xff0c;其核心思想是通过 主备双缓存 的机制&#xff0c;确保在热点数据过期时仍能提供可用服务&#xff0c;同时降低对数据库的瞬时压力。以下是其核心原理、实现细节及适用场景的深度解析&#xff1a; 一、核心…

力扣每日打卡 2176. 统计数组中相等且可以被整除的数对(简单)

力扣 2176. 统计数组中相等且可以被整除的数对 简单 前言一、题目内容二、解题方法1. 暴力解法2.官方题解官方也是暴力解法 前言 这是刷算法题的第十三天&#xff0c;用到的语言是JS 题目&#xff1a;力扣 2176. 统计数组中相等且可以被整除的数对(简单) 一、题目内容 给你一…

云服务器和物理服务器

服务器&#xff0c;作为互联网世界中数据存储与处理的关键枢纽&#xff0c;其重要性不言而喻。在众多服务器类型中&#xff0c;云服务器和物理服务器占据了主导地位&#xff0c;它们各自有着独特的特点和应用场景。咱们就来深入探讨一下这两者的区别。

Kubernetes Pod 调度策略:从基础到进阶

文章目录 环境Kubernetes 部署Kubernetes Pod 调度策略Kubernetes Pod 调度策略对照表调度流程经历阶段案例展示生成yaml文件默认调度节点选择器为节点添加标签编写 Deployment 配置文件应用资源并查看调度结果 Node Affinity&#xff08;节点亲和性&#xff09;为节点添加标签…

SQLite、MySQL、SQL Server、Oracle 和 PostgreSQL 五种数据库的区别

以下是 SQLite、MySQL、SQL Server、Oracle 和 PostgreSQL 五种主流关系型数据库管理系统(RDBMS)的区别,从多个维度进行对比: 1. 架构与部署 SQLite(Structured Query Language Lite‌): 嵌入式数据库,无服务器架构。数据库存储在一个单一的磁盘文件中。部署简单,适合轻量…