web-dev-qa-db-ja.com

Unicode文字列から絵文字を正しく抽出する

Python 2で作業していて、絵文字と他のUnicode文字を含む文字列があります。リスト内の各エントリが単一の文字/絵文字であるリストに変換する必要があります。

x = u'????????xyz????????'
char_list = [c for c in x]

必要な出力は次のとおりです。

['????', '????', 'x', 'y', 'z', '????', '????']

実際の出力は次のとおりです。

[u'\ud83d', u'\ude18', u'\ud83d', u'\ude18', u'x', u'y', u'z', u'\ud83d', u'\ude0a', u'\ud83d', u'\ude0a']

どうすれば目的の出力を達成できますか?

21
Aaron

まず、Python2では、Unicode文字をUnicode文字として表示するには、Unicode文字列(u'<...>')を使用する必要があります。そして 正しいソースエンコーディング ソースコードで\UXXXXXXXX表現ではなく、文字自体を使用したい場合。

さて、 Python:代理ペアが含まれているときに正しい文字列の長さを取得する および Pythonは単一のUnicode文字列に対して2の長さを返す 、Python2の「狭い」ビルド(sys.maxunicode==65535)、32ビットUnicode文字は サロゲートペア として表され、これは文字列関数に対して透過的ではありません。これは3.3でのみ修正されています( PEP039 )。

最も簡単な解決策(3.3+への移行を除いて)は、3番目のリンクで概説されているようにソースからPython "wide"ビルドをコンパイルすることです。その中で、Unicode文字はすべて4バイトです(したがって、メモリを大量に消費する可能性があります)が、幅の広いUnicode文字を日常的に処理する必要がある場合、これはおそらく許容できる価格です。

「狭い」ビルドの解決策は、文字列関数のカスタムセットを作成することですlenslice;おそらくunicodeのサブクラスとして)サロゲートペアを検出し、それらを単一の文字として処理します。私は既存のものを簡単に見つけることができませんでした(これは奇妙です)が、書くのはそれほど難しくありません:

  • TF-16#U +10000からU + 10FFFF-ウィキペディア
    • 最初の文字(高代理)は範囲0xD800..0xDBFFにあります
    • 2番目の文字(低いサロゲート)-範囲0xDC00..0xDFFF
    • これらの範囲は予約されているため、通常の文字として使用することはできません

したがって、サロゲートペアを検出するコードは次のとおりです。

def is_surrogate(s,i):
    if 0xD800 <= ord(s[i]) <= 0xDBFF:
        try:
            l = s[i+1]
        except IndexError:
            return False
        if 0xDC00 <= ord(l) <= 0xDFFF:
            return True
        else:
            raise ValueError("Illegal UTF-16 sequence: %r" % s[i:i+2])
    else:
        return False

そして、単純なスライスを返す関数:

def slice(s,start,end):
    l=len(s)
    i=0
    while i<start and i<l:
        if is_surrogate(s,i):
            start+=1
            end+=1
            i+=1
        i+=1
    while i<end and i<l:
        if is_surrogate(s,i):
            end+=1
            i+=1
        i+=1
    return s[start:end]

ここでは、これらの関数は組み込み関数よりもはるかに遅いため、支払う代償はパフォーマンスです。

>>> ux=u"a"*5000+u"\U00100000"*30000+u"b"*50000
>>> timeit.timeit('slice(ux,10000,100000)','from __main__ import slice,ux',number=1000)
46.44128203392029    #msec
>>> timeit.timeit('ux[10000:100000]','from __main__ import slice,ux',number=1000000)
8.814016103744507    #usec
17
ivan_pozdeev

niseg ライブラリ(pip install uniseg):

# -*- coding: utf-8 -*-
from uniseg import graphemecluster as gc

print list(gc.grapheme_clusters(u'????????xyz????????'))

出力[u'\U0001f618', u'\U0001f618', u'x', u'y', u'z', u'\U0001f60a', u'\U0001f60a']、および

[x.encode('utf-8') for x in gc.grapheme_clusters(u'????????xyz????????'))]

文字のリストをUTF-8でエンコードされた文字列として提供します。

10
James Hopkin