0

我在另一个主题上找到了类似问题的解决方案,但不幸的是它对我不起作用。这是我的问题:

我正在从我想在另一个文件中搜索的代理对 unicode 制作数据帧(例如:“\uD83C\uDFF3”、“\u26F9”、“\uD83C\uDDE6\uD83C\uDDE8”):

    with open("unicodes.csv", "rt") as csvfile:                                        
        emoticons = pd.read_csv(csvfile, names=["xy"])
        emoticons = pd.DataFrame(emoticons)
        emoticons = emoticons.astype(str)

接下来,我正在阅读带有文本的文件,其中一些行包含代理对 unicode:

    for chunk in pd.read_csv(path, names=["xy"], encoding="utf-8", chunksize=chunksize):            
        spam = pd.DataFrame(chunk)
        spam = spam.astype(str)

在这个 for 循环中,我正在检查 line 是否包含 surrogatepairs unicode,如果它是真的,那么我想将这个 surrogatepair unicode 打印为表情符号 - 这就是我编码和解码这个“i”值的原因,它是 str:(解决方案来自:如何在 Python 中使用代理对?

        for i in emoticons.xy:
            if spam["xy"].str.contains(i, regex=False).any():                                 
                print(i.encode('utf-16', 'surrogatepass').decode('utf-16'))

               #printing:
               #\uD83C\uDFF3 
               #\u26F9
               #\uD83C\uDDE6\uD83C\uDDE8

因此,当我启动程序时,它仍然将 surrogatepairs unicode 打印为 str,而不是 emoji,但是当我自己将 surrogatepair unicode 输入打印函数时,它可以工作:

    print("\uD83C\uDFF3".encode("utf-16", "surrogatepass").decode("utf-16", "surrogatepass"))

    #printing:
    # 

我究竟做错了什么?我试图从这个 i 和另一个解决方案中制作字符串,但它仍然不起作用。

编辑:

hexdump -C file.csv
00004b70  5c 75 44 38 33 44 5c 75  44 45 45 39 0a 5c 75 44  |\uD83D\uDEE9.\uD|
00004b80  38 33 44 5c 75 44 45 45  42 0a 5c 75 44 38 33 44  |83D\uDEEB.\uD83D|
00004b90  5c 75 44 45 45 43 0a 5c  75 44 38 33 44 5c 75 44  |\uDEEC.\uD83D\uD|
00004ba0  43 42 41 0a 5c 75 44 38  33 44 5c 75 44 45 38 31  |CBA.\uD83D\uDE81|

EDIT2:所以我发现了一些可行的方法,但仍需要改进: https ://stackoverflow.com/a/54918256/4789281

我要转换的另一个文件中的文本看起来文件:

"O żółtku zapomniałaś \uD83D\uDE02"
"Piękny outfit \uD83D\uDE0D"

当我这样做时,另一个主题中的建议是:

print(codecs.decode(i,encoding='unicode_escape',errors='surrogateescape').encode('utf-16', 'surrogatepass').decode('utf-16'))

我有这样的事情:

O żóÅtku zapomniaÅaÅ 
PiÄkny outfit 

所以我的代理对被替换了,但是我的波兰字符被一些奇怪的东西替换了。

4

1 回答 1

1

你走在正确的轨道上。您尝试中断的内容是因为您在阅读文件后在“str”中拥有的不是“代理对” - 相反,它们是代理对的反斜杠编码代码点,编码为文本。

也就是说:文件中的序列“5c 75 44 38 33 44”是实际的 ascii 字符“\uD83D”(总共 6 个字符),而不是代理代码点 0xD83D(正确解码后,它与下一个代理一起“ \uDE0D" 将是字符串中的单个字符)。

我说你在正确的轨道上的部分是:你真的必须将它编码成一个字节序列,然后将它解码回来。问题是您必须使用“latin1”对其进行编码(只是为了尝试保留字符串上的任何其他非 ascii 字符 - 如果您的代码点无法在 latin1 中表示,它可能会中断),然后用特殊的“unicode 转义”编解码器。或charmap编码,它将保留字符串上的其他字符,然后使用相同的编解码器将其解码回来。此时,两个代理字符都将是 Python 字符串中的两个字符的文本:

In [16]: "\\uD83D\\uDE0D".encode("latin1").decode("unicode escape", "surrogatepass")                              
Out[16]: '\ud83d\ude0d'

坏消息是 - 这不是一个非常有效的 STR - 代理字符本身不应该存在于内部表示中 - 相反,它们应该组合成所需的最终字符。因此,尝试将其打印出来会中断:

In [19]: a  = "\\uD83D\\uDE0D".encode("utf-8").decode("unicode escape")                                          

In [20]: print(a)                                                                                                       
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-20-bca0e2660b9f> in <module>
----> 1 print(a)

UnicodeEncodeError: 'utf-8' codec can't encode characters in position 0-1: surrogates not allowed

在这里使用“surrogatepass”错误策略将无济于事——你将得到一个不可打印的字节序列。

因此,第二次必须对其进行“编码”和“解码”——这一次,您在文本中拥有的字符是实际的“代理”代码点,它们将是有效的 utf-16 进行解码。因此,现在的路径是对该序列进行编码,使用“surrogatepass”强制这些字符,然后从 utf-16 解码,最终将代理对理解为单个字符:

In [30]: a  = "\\uD83D\\uDE0D".encode("unicode escape").decode("unicode escape")                                          

In [31]: a                                                                                                              
Out[31]: '\ud83d\ude0d'

In [32]: b = a.encode("utf-16", "surrogatepass").decode("utf-16")                                                       

In [33]: print(b)                                                                                                       

总结:

您将文件读取为 utf-8 文本,以读取可能的其他非 ascii 字符,将结果编码为“unicode escape”并将其解码回来 - 这会将文件中扩展的人类可读“\uXXXX”序列转换为代理项代码点。然后将其转换回 utf-16,告诉 Python 忽略代理项,然后“按原样”复制,然后从 utf-16 解码回来:

def decode_surrogate_ascii(txt):
    interm = txt.encode("latin1").decode("unicode escape")
    return interm.encode("utf-16", "surrogatepass").decode("utf-16")

您所要做的就是在您的数据框感兴趣的列中应用上述函数:

emoticons = emoticons.apply(pd.Series(lambda row: (decode_surrogate_ascii(item) if isinstance(item,  str) else item for item in row ))
于 2019-11-22T16:00:42.950 回答