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']
どうすれば目的の出力を達成できますか?
まず、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文字を日常的に処理する必要がある場合、これはおそらく許容できる価格です。
「狭い」ビルドの解決策は、文字列関数のカスタムセットを作成することです(len
、slice
;おそらくunicode
のサブクラスとして)サロゲートペアを検出し、それらを単一の文字として処理します。私は既存のものを簡単に見つけることができませんでした(これは奇妙です)が、書くのはそれほど難しくありません:
0xD800..0xDBFF
にあります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
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でエンコードされた文字列として提供します。