8

Google App Engine は Python 2.5.2 を使用しており、明らかに UCS4 が有効になっています。ただし、GAE データストアは内部で UTF-8 を使用します。したがって、u'\ud834\udd0c' (長さ 2) をデータストアに格納すると、それを取得すると、'\U0001d10c' (長さ 1) が得られます。文字列を格納する前後で同じ結果が得られるように、文字列内の Unicode 文字の数を数えようとしています。そのため、文字列を受信したらすぐに (u'\ud834\udd0c' から '\U0001d10c' に) 正規化してから、その長さを計算してデータストアに入れようとしています。UTF-8にエンコードしてから再度デコードできることはわかっていますが、もっと簡単で効率的な方法はありますか?

4

2 に答える 2

4

UTF-8にエンコードしてから再度デコードできることはわかっています

はい、これは「UCS-4 文字列の UTF-16 サロゲート」入力がある場合の問題を解決するための通常のイディオムです。しかし、Mechanical snail が言ったように、この入力形式が正しくないため、生成されたものを優先して修正する必要があります。

もっと簡単で効率的な方法はありますか?

まあ...次のような正規表現を使用して手動で行うことができます:

re.sub(
    u'([\uD800-\uDBFF])([\uDC00-\uDFFF])',
    lambda m: unichr((ord(m.group(1))-0xD800<<10)+ord(m.group(2))-0xDC00+0x10000),
    s
)

確かにもっと簡単ではありません...実際にもっと効率的かどうかについても疑問があります!

于 2011-08-03T14:41:34.803 に答える
2

残念ながら、3.3 より前のバージョンでの CPython インタープリターの動作は、「狭い」または「広い」Unicode サポートで構築されているかによって異なります。したがって、 への呼び出しなどの同じコードlenが、標準インタープリターの異なるビルドでは異なる結果になる可能性があります。例については、この質問を参照してください。

「ナロー」と「ワイド」の違いは、「ナロー」インタープリターは内部に 16 ビット コード ユニット (UCS-2) を格納するのに対し、「ワイド」インタープリターは内部に 32 ビット コード ユニット (UCS-4) を格納することです。コードポイントU+10000 以上 (基本多言語面の外側) は、len「狭い」インタープリターで 2 になります。これは、(サロゲートを使用して) それらを表現するために 2 つの UCS-2 コード単位lenが必要であるためです。「ワイド」ビルドでは、非 BMP コードポイントに必要な UCS-4 コードユニットは1 つだけです。len

unicode以下は、サロゲート ペアが含まれているかどうかに関係なくすべての文字列を処理し、ナロー ビルドとワイド ビルドの両方で CPython 2.7 で動作することを確認しました。(ほぼ間違いなく、ワイド インタープリターのように文字列を指定することは、完全なサロゲート コードポイントを部分文字コード単位とは異なるものとしてu'\ud83d\udc4d'表現したいという肯定的な欲求を反映しているため、自動的に修正されるエラーにはなりませんが、ここではそれを無視しています。これはエッジ ケースであり、通常は望ましいユース ケースではありません。)

以下@invokeで使用するトリックは、モジュールの に何も追加せずに計算の繰り返しを回避する方法です__dict__

invoke = lambda f: f()  # trick taken from AJAX frameworks

@invoke
def codepoint_count():
  testlength = len(u'\U00010000')  # pre-compute once
  assert (testlength == 1) or (testlength == 2)
  if testlength == 1:
    def closure(data):  # count function for "wide" interpreter
      u'returns the number of Unicode code points in a unicode string'
      return len(data.encode('UTF-16BE').decode('UTF-16BE'))
  else:
    def is_surrogate(c):
      ordc = ord(c)
      return (ordc >= 55296) and (ordc < 56320)
    def closure(data):  # count function for "narrow" interpreter
      u'returns the number of Unicode code points in a unicode string'
      return len(data) - len(filter(is_surrogate, data))
  return closure

assert codepoint_count(u'hello \U0001f44d') == 7
assert codepoint_count(u'hello \ud83d\udc4d') == 7
于 2012-03-30T17:46:02.913 に答える