質問: やりたいこと: 4byte文字と3byte文字が混在しているutf8の文字列の場合、4byte文字が混ざっていることを検出したい (文字コード等への理解が浅い者です) 例えば「𠮷田直樹」という文字列があったとします。 一文字目が異体字で4byte、他の3文字はそれぞれ3byteです。こういった入力を許容できない文字として判別したいです。 一文字ずつループしてlen()などで見たらいいのかと思いきや、 ``` s="𠮷田直樹" for x in s.decode("utf8"):print(x) ??? ??? 田 直 樹 ``` こうなってしまい、一文字ずつ調べるといったことができずに困っています。 文字列をunicodeに変換した際のことですが、下記のように問題の一文字目だけが\Uで開始していることに気づきました。これを判別の材料にできないかと思っているのですが、どうするとこの記号を検出できるのかがわかっておりません。(この判別方法が正しいのかも自信がありません) ``` s.decode("utf8") >>> u'\U00020bb7\u7530\u76f4\u6a39' ``` \Uで始まる文字があったらそれは許容できない文字として判定しようとした場合、どのようにすると実現可能でしょうか。
回答: すでに解決されているそうですが、個人的にはもっとスマートにできると思います。 が、その前に最初の問題について。 自分も最初、unicodeに変換すればいいでしょ、と思いましたが、 * Python/C API リファレンス - Unicodeオブジェクトとcodec - Unicode型 > > Py_UNICODE > > この型は Unicode 序数 (Unicode ordinal) を保持するための基礎単位として、 Python が内部的に使います。 Python のデフォルトのビルドでは、 Py_UNICODE として 16-bit 型を利用し、 Unicode の値を内部では UCS-2 で保持します。 UCS4 版の Python をビルドすることもできます。(最近の多くの Linux ディストリビューションでは UCS4 版の Python がついてきます) UCS4 版ビルドでは Py_UNICODE に 32-bit 型を利用し、内部では Unicode データを UCS4 で保持します。 wchar_t が利用できて、 Python の Unicode に関するビルドオプションと一致するときは、 Py_UNICODE は wchar_t を typedef でエイリアスされ、ネイティブプラットフォームに対する互換性を高めます。それ以外のすべてのプラットフォームでは、 Py_UNICODE は unsigned short (UCS2) か unsigned long (UCS4) の typedef によるエイリアスになります。 > > > との事で、utf-8の4バイト文字はUnicodeでは0x10000-0x1ffffの範囲になるので、UCS2版ビルドであれば表現しきれないためではないかと思います。 というわけで、PythonをUCS4版にビルドし直してください。というわけには行きませんね。 代わりにPythonのUnicodeオブジェクトは使わず、自力でエンコーディングするしかありません。 ただ、utf-8のエンコードロジックはシンプルです。 ですので、ちょっとご自身で調べてみてください。 一応簡単に説明します。 1バイトづつ調べると、それが何バイト文字か、簡単に調べることができます。 具体的には、 1. 0x80とビットandした結果が0x00であれば、1バイト文字 2. 0xe0とビットandした結果が0xc0であれば、2バイト文字の先頭バイト 3. 0xf0とビットandした結果が0xe0であれば、3バイト文字の先頭バイト 4. 0xf8とビットandした結果が0xf0であれば、4バイト文字の先頭バイト 5. (5バイト文字、6バイト文字はめんどくさいので省略) 6. 0xc0とビットandした結果が0x80であれば、複数バイトの2バイト目以降(つまり、2-4の文字の後に続く) となります。 例えば「𠮷」の字を見てみると、utf-8では[0xf0, 0xa0, 0xae, 0xb0]になりますが、上の条件と照らし合わせてみます。 0xf0 & 0xf8 = 0xf0 0xa0 & 0xc0 = 0x80 0xae & 0xc0 = 0x80 0xb0 & 0xc0 = 0x80 4バイト文字が含まれているかどうか判定するのであれば、上記の「4」の条件を満たすものがあるかどうかで判断できます。 ``` def check(s): return len([c for c in s if ord(c) & 0xf8 == 0xf0]) > 0 print(check('𠮷田直樹')) # True print(check('吉田直樹')) # False ```