如何在 Python 中将代理对转换为普通字符串?

Posted

技术标签:

【中文标题】如何在 Python 中将代理对转换为普通字符串?【英文标题】:How can I convert surrogate pairs to normal string in Python? 【发布时间】:2016-11-03 23:23:49 【问题描述】:

这是Converting to Emoji 的后续行动。在那个问题中,OP 有一个json.dumps() 编码的文件,其中一个表情符号表示为代理对 - \ud83d\ude4f。他/她在读取文件和正确翻译表情符号时遇到问题,正确的 answer 是文件中的每一行 json.loads()json 模块将处理从代理对转换回 (I' m 假设 UTF8 编码)表情符号。

这就是我的情况:假设我只有一个普通的 Python 3 unicode 字符串,其中包含一个代理对:

emoji = "This is \ud83d\ude4f, an emoji."

如何处理此字符串以从中获取emoji 的表示形式?我希望得到这样的东西:

"This is ????, an emoji."
# or
"This is \U0001f64f, an emoji."

我试过了:

print(emoji)
print(emoji.encode("utf-8")) # also tried "ascii", "utf-16", and "utf-16-le"
json.loads(emoji) # and `.encode()` with various codecs

通常我会收到类似于UnicodeEncodeError: XXX codec can't encode character '\ud83d' in position 8: surrogates no allowed 的错误。

我在 Linux 上运行 Python 3.5.1,$LANG 设置为 en_US.UTF-8。我已经在命令行的 Python 解释器和在 Sublime Text 中运行的 IPython 中运行了这些示例 - 似乎没有任何区别。

【问题讨论】:

tweepy(我猜通常是 Twitter)似乎正在这样做。在这里提一下,希望更多谷歌搜索这个问题能找到这个答案。 反方向(单个字符到代理对):Python: Find equivalent surrogate pair from non-BMP unicode char - Stack Overflow 【参考方案1】:

由于这是一个反复出现的问题,而且错误信息略显晦涩,这里有更详细的解释。

代理是一种表示大于 U+FFFF 的 Unicode 代码点的方法。

回想一下,最初指定 Unicode 包含 65,536 个字符,但很快发现这不足以容纳世界上所有的字形。

作为(否则为固定宽度)UTF-16 编码的扩展机制,设置了一个保留区域以包含用于表达Basic Multilingual Plane 之外的代码点的机制:此特殊区域中的任何代码点都必须后跟来自同一区域的另一个字符代码,它们一起表示一个数字大于旧限制的代码点。

(严格来说,代理区域分为两半;一对中的第一个代理需要来自高代理一半,第二个代理需要来自低代理。令人困惑的是,高代理 U+D800-U +DBFF 的代码点编号低于低代理 U+DC00-U+DFFF。)

这是一种专门支持 UTF-16 编码的遗留机制,不应在其他编码中使用;他们不需要它,并且适用的标准明确规定这是不允许的。

换句话说,虽然U+12345 可以用代理对 U+D808 U+DF45 来表达,但除非您专门使用 UTF-16,否则您应该直接直接表达它。

更详细地说,这是在 UTF-8 中将其表示为单个字符的方式:

0xF0 0x92 0x8D 0x85

这里是对应的代理序列:

0xED 0xA0 0x88
0xED 0xBD 0x85

正如已接受的答案中所建议的那样,您可以使用类似的东西来回往返

>>> "\ud808\udf45".encode('utf-16', 'surrogatepass').decode('utf-16').encode('utf-8')
b'\xf0\x92\x8d\x85'

或许也可以看看http://www.russellcottrell.com/greek/utilities/surrogatepaircalculator.htm

【讨论】:

相关:***.com/questions/33642339/…【参考方案2】:

您在磁盘上的 json 文件中混合了文字字符串 \ud83d(六个字符:\ u d 8 3 d)和 单个字符 u'\ud83d'(在 Python 源代码中使用字符串文字指定代码)在内存中。这是 Python 3 上 len(r'\ud83d') == 6len('\ud83d') == 1 之间的区别。

如果您看到'\ud83d\ude4f' Python 字符串(2 个字符),则上游存在错误。通常,您不应该得到这样的字符串。如果你得到了一个并且你无法修复生成它的上游;您可以使用 surrogatepass 错误处理程序修复它:

>>> "\ud83d\ude4f".encode('utf-16', 'surrogatepass').decode('utf-16')
'?'

Python 2 was more permissive.

注意:即使您的 json 文件包含文字 \ud83d\ude4f(12 个字符);你不应该得到代理对:

>>> print(ascii(json.loads(r'"\ud83d\ude4f"')))
'\U0001f64f'

注意:结果是 1 个字符 ('\U0001f64f'),而不是代理对 ('\ud83d\ude4f')。

【讨论】:

以上是关于如何在 Python 中将代理对转换为普通字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中将上标或下标转换为普通文本

如何在python中将字符串转换为日期时间[重复]

如何在 Python 中将列表转换为带有空格的字符串?

如何在python中将异常转换为字符串[重复]

如何在python中将字符串日期转换为日期时间格式? [复制]

如何在 Python 中将字符串转换为 utf-8