str.encode()更改UTF-8字符

时间:2017-01-23 09:23:36

标签: python-3.x utf-8 character-encoding

有人建议此问题与6269765重复。我没有在原始代码或最小代码中使用任何b''文字。请参阅下面的embeDded编辑:

我已经将今天遇到的问题减少到这个最小的Python 3代码:

x='\xc3\xb3'
print(''.join([hex(ord(c))[2:] for c in x]))
print(''.join([hex(c)[2:] for c in x.encode()]))

当我运行此代码时,我得到:

c3b3
c383c2b3

str.encode()是否真的应该改变UTF-8字符ó(带有急性的拉丁语小写字母O)到两个字符³(带有CIRCUMFLEX和SUPERSCRIPT三的拉丁文大写字母A)?

编辑:

当一位评论者建议输入No³。在某些情况下只输入了ó,并在其他情况下从文本文件中读取。涉及的文本文件是原始问题。它是当前版本的Ubuntu的系统字典文件。该文件的日期为2011年10月23日,其文件系统路径如原始问题的命令示例所示。

原始问题涉及在该文件的第1053行遇到Asunción这个词。 Asunción中的ó字符具有字节序列 C3B3 ,在FileFormat.Info UTF-8 lookup table中描述为带有急性的拉丁文小写字母(此处描述的读者无法正确读取Unicode文本。

在任何代码中都没有使用b''文字,既不是原文,也不是最小。

当UTF字符从ó字符变为³时,发现问题的性质。这涉及将c3b3改变为c383c2b3。字典文件字面上包含两个字节c3b3,它按预期显示ó,如UTF-8表中所述。最初的问题是由于长度的变化而引发的例外。

使用str.encode()试图解决问题并发现其来源。人们认为某些地方的某些东西与str.encode()类似。

首先显示此问题的最小代码是:

x='Asunción'
print(' '.join([hex(ord(c))[2:] for c in x]))
print(' '.join([hex(c)[2:] for c in x.encode()]))

但是我发现很多人都看不到小写的急性o 所以我把它改成了十六进制代码(\ x),它们在和<之前都有相同的十六进制验证输出/ em>在str.encode()作为上面的第一个最小示例之后,使用文字全字Asunción

然后我决定单独使用受影响的字符并且十六进制输出中没有空格会更小。

编辑结束,回到原帖:

在最新的美国英语Ubuntu版本/usr/share/dict/american-english上的美国英语词典文件中遇到了这个UTF-8字符。您可以使用以下命令查看该文件中的第一个单词:

head -1053 /usr/share/dict/american-english|tail -1

您可以使用以下命令以十六进制显示它:

head -1053 /usr/share/dict/american-english|tail -1|od -Ad -tx1

字符描述来自here。我在2天前更新了Ubuntu 16.04.1 LTS上的GCC 5.4.0上编译的Python 3.5.2。

编辑:

完全避免字节而不使用str.encode()的正确答案吗?还是有更好的答案?

2 个答案:

答案 0 :(得分:1)

小Q问题从非常大的问题中抢走了问题:

  

当我运行此代码时,我得到:

     

C3B3

     

c383c2b3

     

str.encode()真的应该改变UTF-8字符ó(拉丁语   小写字母O与ACUTE)到两个字符³(拉丁文大写字母   A WITH CIRCUMFLEX和SUPERSCRIPT THREE?

没有“UTF-8字符”这样的东西。 LATIN SMALL LETTER O WITH ACUTE是Unicode字符(python:str对象)。它的Unicode代码点是U + 00F3。

>>> import unicodedata as ucd
>>> smalloacute = u"\u00f3"
>>> ucd.name(smalloacute)
'LATIN SMALL LETTER O WITH ACUTE'

现在您可以将其编码为bytes对象:

>>> smalloacute.encode('utf8')
b'\xc3\xb3'

并将您的bytes对象写入文件或任何您想要做的事情。 请注意,b'\xc3'是一个字节对象,与LATIN CAPITAL LETTER A WITH CIRCUMFLEX没有用处。同样地b'\xb3'SUPERSCRIPT THREE

你当然不想第二次旋转utf8轮;结果不是很有用:

>>> `smalloacute.encode('utf8').decode('latin1').encode('utf8')
b'\xc3\x83\xc2\xb3'`

之前看过吗?注意:decode('latin1')仅将类型从bytes更改为str

回到原来的问题/声明“str.encode()改变UTF-8字符”。简答:不,不是!从文件中获得一个2字节序列,表示Unicode o-acute字符。您可能希望直接使用它。或者,您可能希望执行bytes.decode()并在str个对象中工作。你绝对不应该bytes.kludge().encode()

答案 1 :(得分:1)

您甚至不必调用encode()来查看字符串中的内容。 Python将以交互模式向您展示:

>>> '\xc3\xb3'
'ó'

这是一个长度为2的unicode字符串,其字符正是您所看到的。根本不涉及字节或UTF-8,除非是在边界处将它们发送到终端或从源文件中读取它们。如果你想在字符串中使用unicode字符,你可以直接插入它,或者用\ x(如果是FF或更少),\ u(如果是FFFF或更少)或\ U(对于所有字符)转义它。

>>> '\xf3' == '\u00f3' == 'ó'
True

如果 出于某种原因想要一个UTF-8文字,那将是一个字节文字:

>>> b'\xc3\xb3'
b'\xc3\xb3'

这是一个长度为2的字节字符串。当你要求Python向你显示它时,它会将其显示为已写入,因为Python不知道你的字节中的内容。

>>> b'\xc3\xb3'.decode()
'ó'

输入是一个字节字符串(长度为2,包含UTF-8数据),输出为unicode字符串(长度为1)。