Python-关于如何在Python中存储和处理字符串的困惑

时间:2019-03-23 09:47:41

标签: python unicode encode

我正在尝试阅读字符串在Python中的工作方式,并且在解读各种功能方面遇到了困难。这是我的理解。希望获得关于如何记住这些细微差别的更正和新观点。

  • 首先,我知道Unicode演变为适应世界各地的多种语言和口音。但是python如何存储字符串? 如果我定义s = 'hello',则存储字符串s的编码是什么?是Unicode吗?还是以纯字节存储?在做type(s)时,我得到的答案是<type 'str'>。但是,当我执行us = unicode(s)时,us的类型为<type 'unicode'>usstr类型还是python中实际上有unicode类型?

  • 此外,我知道要存储空间,我知道我们使用encode()函数将字符串编码为字节。因此,假设bs = s.encode('utf-8', errors='ignore')将返回一个字节对象。因此,现在当我将bs写入文件时,是否应该以{{1​​}}模式打开文件?我已经看到,如果以wb模式打开,它将在文件中将字符串存储为w

  • decode()函数的作用是什么?(我知道,这个问题太开放了。)是否像我们将其应用于字节对象,然后将字符串转换为我们选择的编码?还是总是将其转换回Unicode序列?是否可以从以下几行中得出其他见解?

b"<content in s>"
  • >>> s = 'hello' >>> bobj = bytes(s, 'utf-8') >>> bobj 'hello' >>> type(bobj) <type 'str'> >>> bobj.decode('ascii') u'hello' >>> us = bobj.decode('ascii') >>> type(us) <type 'str'> 如何工作?我读到它将尝试执行对象描述中的 str ()函数。但是,此功能对Unicode字符串和常规字节编码字符串的作用有何不同?

先谢谢了。

1 个答案:

答案 0 :(得分:2)

重要提示:下面介绍python3的行为。尽管python2在概念上有一些相似之处,但是暴露的行为会有所不同。

简而言之:由于unicode支持,python3中的字符串对象是更高级别的抽象。取决于解释器如何在内存中表示它。因此,当涉及序列化时(例如,将字符串的文本表示形式写入文件),首先需要使用指定的编码(例如UTF-8)将其显式编码为字节序列。字节到字符串的转换即解码也是如此。在python2中,使用unicode类可以实现相同的行为,而str则是bytes的同义词。

虽然这不是您问题的直接答案,但请查看以下示例:

import sys

e = ''
print(len(e))            # 0
print(sys.getsizeof(e))  # 49

a = 'hello'
print(len(a))            # 5
print(sys.getsizeof(a))  # 54

u = 'hello平仮名'
print(len(u))                 # 8
print(sys.getsizeof(u))       # 90
print(len(u[1:]))             # 7
print(sys.getsizeof(u[1:]))   # 88
print(len(u[:-1]))            # 7
print(sys.getsizeof(u[:-1]))  # 88
print(len(u[:-2]))            # 6
print(sys.getsizeof(u[:-2]))  # 86
print(len(u[:-3]))            # 5
print(sys.getsizeof(u[:-3]))  # 54
print(len(u[:-4]))            # 4
print(sys.getsizeof(u[:-4]))  # 53

j = 'hello'
print(len(j))                 # 8
print(sys.getsizeof(j))       # 108
print(len(j[:-1]))            # 7
print(sys.getsizeof(j[:-1]))  # 104
print(len(j[:-2]))            # 6
print(sys.getsizeof(j[:-2]))  # 100

字符串在Python中是不可变的,这使解释器可以方便地决定在创建阶段对字符串进行编码的方式。让我们回顾一下上面的数字:

  • 空字符串对象的开销为49个字节。
  • 带有ASCII符号且长度为5的字符串的大小为49 + 5。编码每个符号使用1个字节。
  • 带有混合(ASCII +非ASCII)符号的字符串甚至具有更高的内存占用量 虽然长度仍然是8。
  • uu[1:]的差,同时uu[:-1]的差为90 - 88 = 2 bytes。即编码每个符号使用2个字节。即使字符串的前缀可以用每个符号1个字节编码。这为我们提供了对字符串进行恒定时间索引操作的巨大优势,但我们为此付出了额外的内存开销。
  • 字符串j的内存占用量更大。只是因为我们不能使用每个符号2个字节来编码其中的所有符号,所以现在解释器每个符号使用4个字节。

好的,请继续检查行为。我们已经知道,解释器以每个符号方式以偶数字节存储字符串,以使我们可以按索引访问O(1)。但是,我们也知道UTF-8使用符号的可变长度表示。让我们证明一下:

j = 'hello'
b = j.encode('utf8')  # b'hello\xf0\x9f\x98\x8b\xf0\x9f\x98\x8b\xf0\x9f\x98\x8b'    
print(len(b))  # 17

因此,我们可以看到,前5个字符使用每个符号1个字节进行编码,而其余3个符号使用每个符号(17 - 5)/3 = 4个字节进行编码。这也解释了为什么python在引擎盖下每个符号表示使用4个字节。

另一种方法是,当我们有一个字节序列并将其decode转换为字符串时,解释器将决定内部字符串表示形式(每个符号1、2或4个字节),并且它是完全不透明的给程序员。唯一必须透明的是字节序列的编码。我们必须告诉解释器如何处理字节。我们应该让他决定字符串对象的内部表示形式。