【问题标题】:Python 3.3: Access string internal representation?Python 3.3:访问字符串内部表示?
【发布时间】:2014-03-13 11:05:00
【问题描述】:

在 Python >= 3.3 中,为了帮助解决 Unicode 编码和解码问题,我希望能够从 Python 代码中检查存储在字符串中的实际内部数据。我该怎么做?

有一个 str.encode() 方法,它返回一个字节表示,但通常这是一个由特定编解码器翻译的字节序列(由“编码”参数选择),而不是存储在str 对象。

有一个“unicode_internal”编码选项,但它已被弃用,并且不清楚在 3.3 中它是否返回真实的内部数据(如何组织?),或者它的一些翻译。

PEP 393 描述了 Unicode 数据的内部结构,从中看来,从 Python 访问它需要报告字符串类型(1/2/4 字节)、表示(ASCII/紧凑)以及字节数组包含字符串内容,(我认为其格式为 ASCII、UCS1、2 或 4)。

我还没有找到在 Python 中提供这种访问的 str 类型的方法。

还有其他方法吗?也许是使用结构的聪明方法?还是公开这些字符串内部结构的 C 库?

2014 年 3 月 13 日更新:

感谢所有回复了关于为什么不应该访问字符串内部结构的建议的人。对于普通的 Python 程序,这当然是有效的建议。

不过,我的问题是:怎么做?

扩展基本原理:这是为了解决编码-解码问题,其中一个函数(可能在某个库中)创建并返回一个 str,而另一个函数(可能在某个其他库中)应该做某事与那个海峡。

我想检查该中间 str 的确切内容,(即:我想将问题空间分成两半),并且这样做不引入另一个变量,即让一个或另一个 python 函数将该数据转换为一些其他形式(如带有转义序列的 ASCII)。

除其他原因外,我想知道确切的内部数据,以防其中一个库实际上对内部数据格式敏感。所述库很可能是用 C 编写的,可以访问该数据,但处理不正确。

此外,确实应该将 str 视为一系列代码点,而无需关注内部内部表示。但是,如果字符串处理中确实存在错误,我不想被它误导,如果没有,我希望有信心没有。考虑到字符串库的复杂性,零错误将是一项了不起的成就。

那么:如何检查字符串的内部结构?

【问题讨论】:

  • 内部表示如何帮助您解决编码或解码问题?在尝试确定内容时,我会坚持使用ascii()
  • 除非您正在编写需要处理str 类型内部的C 扩展,否则绝对不需要处理内部C 结构。这就像试图处理dict 类型的内部哈希表;它与 Python 代码的编码或解码问题无关。
  • 无论是否编码,您都在寻找通过编码对字节的特定解释。如果我没记错的话,我不久前看到 Python 的 unicode 在内部存储为 UTF-16。我会检查这个。但顺便提一下,即使是 Unicode 也需要一种将自身存储在内存中的方法,最终将其转换为特定编码的字节。
  • @PauloBu:Python 3.3 and up will use Latin-1, UCS-2 or UCS-4,根据字符串的实际内容,安全内存。但是,这对于 Python 代码是完全透明的,并且与您尝试将此类值编码为字节时发生的情况无关。
  • @PauloBu:UCS-2 和 UTF-16 是不同的编码。它们之间的混淆已经给程序员带来了很多痛苦。

标签: python string unicode python-3.3


【解决方案1】:

Python 的内部字符串表示严格来说是一个内部实现细节,并且可能会因 Python 的一个版本和另一个版本以及操作系统而异。由于问题指定 Python 版本号 >=3.3,我假设我们正在讨论 CPython(1/2/4 字节字符表示)并使用 id() 给出内存地址的 CPython 实现细节。以下使用Ubuntu 19.10的系统CPython 3.7.5。

from ctypes import string_at
from sys import getsizeof
from binascii import hexlify
a = "ABCDE"
print(hexlify(string_at(id(a), getsizeof(a))))

输出:

b'0100000000000000c0988500000000000500000000000000625866dab454b033e
  50064016c006d010000000000000000414243444500'

您可以在十六进制末尾看到“ABCDE”,从 41 计数到 45。如果从 Unicode 代码点范围 128-255 将字符添加到该字符串,例如 0xA2 处的分号,“¢ ABCDE”,该字符仍然可以用单个字节表示,这就是 CPython 所做的,尽管字符串前面的空行由于某种原因而增长:

b'0200000000000000c09885000000000006000000000000003b7ac7a960368ad4a
  4005a006501650200000000000000000000000000000000000000000000000000
  00000000000000a2414243444500'

如果将高于 Unicode 255 的字符添加到字符串中,例如 0x153 处的 oe 连字“–ABCDE”,则整个字符串将切换为每个字符两个字节,其中“–”是 little-endian” 5301”,“A”为“4100”等等:

b'0200000000000000c0988500000000000600000000000000e50dd134c7e9b87ca
  83d22c59341424300000000000000000000000000000000000000000000000000
  000000000000005301410042004300440045000000'

【讨论】:

  • 这就是我一直在寻找的东西,哦,那是很多年前的事了。我希望我有机会在某个时候重新审视这个话题。
  • 酷。迟到总比不到好。谷歌搜索晦涩问题的常见诅咒是第一个命中是没有答案的 Stackoverflow 问题。如果我后来碰巧偶然发现了答案,那么我很想回去回答这个问题。
【解决方案2】:

Python 中的 Unicode 字符串应被视为 Unicode 代码点序列。这在内部如何表示对于编码和解码问题完全无关紧要。

您可以通过对字符串的各个字符使用 ord() 函数来访问 Unicode 代码点的数值:

>>> list(map(ord, "abc €"))
[97, 98, 99, 32, 8364]

我不认为这对调试编码问题(或其他任何事情)特别有帮助,但它可能会在概念上阐明 Unicode 字符串是什么。

【讨论】:

  • 是的,我很清楚 Python 3.3+ 的字符串模型是一个代码点序列,其内部表示与 Python 代码无关(当然,在空间消耗、复制速度等方面可能仍然是一个问题)。我已经在我的问题陈述中添加了仍然希望检查内部结构数据的理由——从概念上讲,检查 Python 库和其他处理字符串的库是否满足了这种“无忧无虑”。
【解决方案3】:

将 unicode 值 introduced by PEP-393 转换为更节省空间的存储空间仅出于性能原因

因此,它们对编码和解码到 unicode str 值在 Python 代码中的工作方式的影响为零。从 Python 访问内部表示绝对没有意义。字符A 存储为41410041000000,具体取决于字符串中最高代码点需要多少空间,但它仍将被编码为41 ASCII、拉丁文- 1 或 UTF-8。

除非您正在编写一个必须处理这种内部表示的 C 扩展,否则绝对无需担心 Python 实际存储数据的方式。

要调试编码或解码问题,我会使用 ascii() function 来表示仅使用 ASCII 代码点和 Python 字符串文字转义的字符串,或者您可以使用 ord() function 将单个字符转换为每个代码点的整数.

对于字节值,binascii.hexlify() function 也可以方便地将一系列字节快速转换为其十六进制表示形式。

【讨论】:

  • 感谢 Martijn 对普通 Python 程序的回复。我在我的问题中添加了为什么我确实想要检查字符串的内部表示的理由。关于 ascii 和 ord 的建议对于这方面的问题很有用,但不能透明地揭示实际用于特定字符串的内部结构。即:一个仍然依赖于一个额外的库函数来将字符串内部代表转换为 ASCII,从中可以推断出使用了什么代表。
  • 对于您的假设情况,您将使用根据 PEP 会导致不同内部结构的构建字符串对其进行测试,并使用这些字符串进行测试以查看第 3 方库行为是否发生变化。 然而,这是一个极端的边缘案例,在我看来,这是一个稻草人。无法从 Python 访问内部,因为这种边缘情况完全属于 C 领域,您可以使用 C 调试器来处理它。如果你想处理这种情况,你可以研究 Python 源代码和 API 文档。
  • 如果这类问题你不感兴趣,那很好,但这并不意味着它对其他人不感兴趣,或者它普遍是个稻草人。我同意可以使用测试用例、C 调试器等进行调查。也就是说,如果有理由使用 C 调试器进行调查,那么为什么人们会对直接从 Python 调查它们的方法不感兴趣呢?我的问题的重点是找出是否确实有这样的方法。听起来你不需要一个,也不要想象需要一个 - 很公平。
  • 不,我告诉你为什么没有从 Python 到内部的这种访问。不是我不感兴趣,而是 Python 中没有这样的 API 可以看到那个内部表示。
  • 感谢您的回答。根据从字符串内部屏蔽 Python 代码,我确实希望没有直接的 API 来提供开箱即用的内部表示(或者它将是一个相对迟钝的字节检查 API)。这导致了我的问题中的建议,即可能存在一个 C 库,有人为方便这种检查而编写了一个 C 库。
猜你喜欢
  • 2020-10-05
  • 2016-05-04
  • 1970-01-01
  • 2015-08-09
  • 2011-04-16
  • 1970-01-01
  • 2010-12-22
  • 2018-06-19
  • 1970-01-01
相关资源
最近更新 更多