別の非常に包括的な回答: Steven D'Aprano による comp.lang.python (stackoverflow 用にフォーマットしようとしました):
9 文字の文字列 U+004A、U+0061、U+ 006C 'Jalape\xc3\xb1o'
、U+0061、U+0070、U+0065、U+00C3、U+00B1、 U+006F は、おそらく意図したものではありません。これは、UTF-8 では、マルチバイト シーケンス
\xc3\xb1
が、U+00C3 と U+00B1 の 2 つの文字ではなく、単一の文字 U+00F1 を表すことになっているためです。
これは、基本的な概念の混乱を示していますが、基本的な事実に偶然出くわしています。それがあなたを混乱させるのも不思議ではありません、それは私も混乱させます! :-)
エンコードは文字列を生成するのではなく、バイトを生成します。したがって、あなたが引用している人は、 「エンコードされた文字列」について話すときに混乱を引き起こしています
。彼は、バイト文字列を意味することを明確にするか、文字列という単語にまったく言及しないでください。これらのいずれかが機能します。
古いバージョンの Python (2.5 以前) では、残念ながらこのb''
表記は機能しないため、b
.
Python がASCII文字をバイトと混同せず、次のようなバイト文字列を書くように強制した場合は、さらに良いでしょう:
- UTF-8 でエンコードされたバイト文字列
b'\x4a\x61\x6c\x61\x70\x65\xc3\xb1\x6f'
したがって、ASCII 文字とバイトの区別を明確に保ちます。しかし、それは後方互換性をあまりにも壊してしまうので、Python は Python でも ASCII 文字をバイトと混同し続けています。
ここで重要なことはb'Jalape\xc3\xb1o'
、上に示したように、バイトが 9 つの 16 進数値で構成されていることです。それらのうちの 7 つは ASCII 文字Jalape
を表し、そのo
うちの 2 つは ASCII ではありません。それらの意味は、使用しているエンコーディングによって異なります。
(正確には、残りの 7 バイトの意味でさえエンコーディングに依存します。幸いなことに、または残念なことに、すべてのエンコーディングではなく、ほとんどのエンコーディングが ASCII 自体と同じ 16 進値を ASCII 文字に使用するため、ここではやめます。これに言及して、その文字J
が常に hex byte に等しいふりをする4A
. しかし、今あなたは真実を知っています.)
UTF-8 エンコーディングを使用しているため、2 バイトはとしても知られる\xc3\xb1
文字を表します。他のエンコーディングでは、これらの 2 バイトは別のものを表します。ñ
LATIN SMALL LETTER N WITH TILDE
したがって、元の人の意図は Unicode テキスト文字列を取得することであったと推測されます'Jalapeño'
。彼らが Unicode のやり方に賢明であれば、次のいずれかを書くでしょう。
'Jalape\N{LATIN SMALL LETTER N WITH TILDE}o'
'Jalape\u00F1o'
'Jalape\U000000F1o'
'Jalape\xF1o' # hex
'Jalape\361o' # octal
そして、幸せになります。u
(Python 2では、バイト文字列の代わりに Unicode 文字列を使用するために、これらすべての前に を付ける必要があり
ます。)
しかし残念なことに、Unicode に関する神話、誤解、誤解をインターネット全体に広める人々に惑わされてしまったので、彼らはñ
どこかを調べて、UTF-8 に 2 バイトの 16 進値があることを発見し、c3b1
これを記述できると考えました。 :
'Jalape\xc3\xb1o'
これは、彼らが考えていることをしません。9 文字のテキスト文字列(Unicode 文字列)を作成します。
J a l a p e à ± o
なんで?character のÃ
序数値は 195 ( c3
16 進数) であるため
\xc3
、文字はÃ
; 同様に、序数値 177 ( 16 進数)を持つ\xb1
文字です。そして、彼らはモジバケという邪悪さを発見しました。±
b1
代わりに、 byte-stringで開始し、それを UTF-8 として明示的にデコードしていれば問題ありませんでした。
# I manually encoded 'Jalapeño' to get the bytes below:
bytes = b'Jalape\xc3\xb1o'
print(bytes.decode('utf-8'))
私の最初の質問は、「これは 9 文字ではなく 8 文字であるべきではありませんか?」というものでした。彼は言う:\xc3\xb1
単一の文字を表すことになっています。しかし、仲間のPythonistasとやり取りした後、私はさらに混乱しました。
コンテキストに依存します。\xc3\xb1
は Unicode 文字列
'\xc3\xb1'
(Python 2 では と書かれu'\xc3\xb1'
ています) を意味する場合もあれば、バイト文字列b'\xc3\xb1'
(Python 2.5 以前では なしで書かれた場合) を意味する場合もありb
ます。
文字列として、\xc3\xb1
は序数値0xC3
(または 10 進数 195) と0xB1
(または 10 進数 177)を持つ 2 つの文字、つまり'Ã'
とを意味し'±'
ます。
バイトとして、\xc3\xb1
2 バイトを表します (まあ、当然です)。これは、ほとんど何でも意味します。
16 ビットのビッグ エンディアン整数 50097
16 ビットのリトルエンディアン整数 45507
4x4 の白黒ビットマップ
Big5 でエンコードされたバイトの文字'簽'
(CJK UNIFIED IDEOGRAPH-7C3D)
'뇃'
(HANGUL SYLLABLE NWAES) (UTF-16 (ビッグ エンディアン) でエンコードされたバイト)
'ñ'
UTF-8 でエンコードされたバイト
'ñ'
Latin-1 でエンコードされたバイトの 2 文字
'ñ'
MacRoman でエンコードされたバイト数
'Γ±'
ISO-8859-7 でエンコードされたバイト
など。コンテキストを知らなければ、これらの 2 バイトが何を表しているのか、またはそれらをペアとしてまとめる必要があるのか、それとも 2 つの別個のものとして扱う必要があるのかを判断する方法はありません。
上記のパラグラフを参照すると、「UTF-8でエンコードされた生の文字列を書く」とはどういう意味ですか??
彼は混乱していることを意味します。エンコードによってテキスト文字列を取得するのではなく、バイトを取得します (「バイト文字列」を受け入れます)。この文脈では、形容詞「生」は実際には何の意味もありません。エンコードされたバイトがあるか、文字を含む文字列があります。生は、「ねえ、注意してください、これは低レベルのものです」(「低レベル」の定義について)以外は何も意味しません。
Python2では、1回で「ハラペおかしなの」ができます。
スペイン語を話す人にとって、それについて面白いことは何もありません。
個人的には、私はいつも「お」が面白いと思っていました。「女性」と「女性」を声に出して言います。最初は「ウーマン」のように聞こえ、2 番目は「ウィメン」のように聞こえます。今、それは面白いです。しかし、私は脱線します。
'Jalapeño'
Python 2 を (プレフィックスの有無にかかわらず)入力した場合b
、得られる結果は端末の設定によって異なりますが、端末が文字列を内部的に UTF-8 として表現する可能性が高く、バイトが得られます。
b'Jalape\xc3\xb1o'
これは9バイトです。印刷すると、端末は各バイトを個別に印刷しようとし、次のようになります。
- バイト
\x4a
は次のように印刷されますJ
- バイト
\x61
は次のように印刷されますa
- バイト
\x6c
は次のように印刷されますl
- ...
など。運が悪けれ\xc3\xb1
ば、端末は 2 バイトを 1 文字として出力するほどスマートで、ñ
期待どおりの結果が得られるかもしれません。なぜ不運なのですか?たまたま正しい結果を得たからです。次に同じことを、別の端末で、または別のエンコーディングに設定された同じ端末で行うと、まったく異なる結果が得られ、Unicode がめちゃくちゃすぎて使用できないと考えるでしょう。
Python 2.5 を使用して、同じ文字列を 3 回続けて出力し、毎回端末のエンコーディングを変更します。
py> print 'Jalape\xc3\xb1o' # terminal set to UTF-8
Jalapeño
py> print 'Jalape\xc3\xb1o' # and ISO-8859-6 (Arabic)
Jalapeأ�o
py> print 'Jalape\xc3\xb1o' # and ISO-8859-5 (Cyrillic)
JalapeУБo
「正しい」のはどれ?回答: どれもありません。たまたま私たちが望んでいたものでした。
本当に、混乱していることを気にしないでください。Python 2 と、正しいことをしようと必死に努力している端末との間では、何か正しいことが起こったり起こらなかったりするため、混乱しがちです。
これは、各グリフの長さが 1 バイトの「バイト」文字列です。
いいえ。文字列です。グリフは入りません。グリフとは、画面に表示される、または紙に印刷された文字の小さな絵です。それらは、ビットマップまたは派手なベクター グラフィックスである可能性があります。それぞれが 1 バイトである可能性は低く、非常に大まかな計算1に基づくとグリフあたり 200 バイトである可能性が高くなりますが、それがビットマップ、Postscript フォント、OpenType フォント、またはその他のものであるかどうかによって異なります。
内部に格納されている場合、各グリフは文字セット ASCII または Latin-1 に従って整数に関連付けられます。これらの文字セットに面白い n グリフがある場合は、イェーイ! そうでなければいや!ここにはUTF-8はありません!! またはUTF-16!! これらはプレーン バイト (8 ビット) です。
あなたは近づいています。しかし、あなたの言うとおりです。Python 2 の「文字列」はバイト文字列です。つまり、UTF-8 は含まれません。しかし、あなたの端末はそれらのバイトを UTF-8 として扱うかもしれません。
Unicode は、グリフと整数の間の非常に大きなマッピング テーブルであり、
グリフではありません。コードポイントと呼ばれる抽象的な「文字」と整数の間。Unicode には以下が含まれます。
- 明確な文字、数字、文字
- アクセントのある文字
- 独自のアクセント
- 記号、顔文字
- 文字の合字と異体字
- 古いエンコーディングとの下位互換性のためにのみ必要な文字
- 空白
- 制御文字
- 私的使用のために予約されたコードポイント。これは好きなものを意味します
- 「決して使用されない」として予約されているコードポイント
- 「文字ではない」と明示的にラベル付けされたコード ポイント
そしておそらく私が忘れた他の人。
Uxxxx
またはとして示されUxxxx-xxxx
ます。
公式の Unicode 表記は次のとおりです。
U+xxxx
U+xxxxx
U+xxxxxx
U+
その後に、正確に 4、5、または 6 桁の 16 進数が続きます。はU
常に大文字です。残念ながら、Python はその表記法をサポートしていないため、4 桁または 8 桁の 16 進数を使用する必要があります。
\uFFFF
\U0010FFFF
255 までのコード ポイント (序数) の場合、16 進数または 8 進数のエスケープも使用できます。\xFF
\3FF
UTF-8 UTF-16 は、これらの大きな整数を効率的に格納するためのエンコーディングです。
ほぼ正しい。それらは必ずしも効率的ではありません。
Unicode コード ポイントは、何らかの意味を与える単なる抽象的な数値です。コード ポイント 65 ( U+0041
、16 進数の 41 == 10 進数の 65 であるため) は文字A
を意味し、以下同様です。これらの抽象的なコード ポイントが頭の中に浮かんでいると想像してみてください。コードポイントの抽象的な概念をコンピューター上で具体的な形にするにはどうすればよいでしょうか? すべてがコンピューターに入れられるのと同じように、バイトとして、各抽象コードポイント (数値) を一連のバイトに変換する必要があります。
Unicode コード ポイントの範囲は からU+0000
までU+10FFFF
です。つまり、16 進数で 000000 から 10FFFF の値を取るちょうど 3 バイトを使用できます。この範囲外の値、たとえば 110000 はエラーになります。4バイトのうちの 1 つは常にゼロの値を持ちますが、効率の理由から、4バイトを使用する方が高速で優れています。
簡単に言うと、これが UTF-32 エンコーディングです。すべての文字が正確に 4 バイトを使用します。たとえば、コード ポイントU+0041
(文字) は、コンピュータがビッグ エンディアンかリトル エンディアンかに応じて、 A
16 進バイト00000041
または可能性があります。41000000
ほとんどのテキストは非常に低い序数の値を使用するため、これは非常にメモリを浪費します。そのため、UTF-16 は 1 文字につき 2 バイトしか使用せず、2 バイトに収まらないすべてのものに対して、いわゆる「サロゲート ペア」を使用する奇妙なスキームを使用します。「機能」の定義によっては機能しますが、複雑であり、上記のコードポイントが必要な場合は、UTF-16 を避けたいと考えていますU+FFFF
。
UTF-8 は、序数の値が小さい文字が 1 バイトとしてエンコードされるきちんとした変数エンコーディングを使用します (さらに良いことに、これは ASCII が使用するのと同じバイトです。つまり、世界中のすべてが ASCII であると想定している古いソフトウェアは引き続き機能します。主に働いています)。上位の序数は、2、3、または 4 バイトとしてエンコードされます2。何よりも、歴史的な可変幅エンコーディングとは異なり、UTF-8 は自己同期します。従来のエンコーディングでは、1 バイトが破損すると
、その時点からすべてが台無しになる可能性があります。UTF-8 では、1 つの破損したバイトは、それを含む 1 つのコード ポイントのみを破壊し、それ以降はすべて問題ありません。
したがって、DB が「生の UTF-8 でエンコードされた文字列を書き込む」と言った場合、これを行う唯一の方法は、デフォルトの文字列リテラルが Unicode で格納されている Python3 を使用することです。それぞれの構造のバイト。または、u'Jalape'
両方の言語でどちらが unicode であるかを使用できます (先頭の に注意してくださいu
)。
Python は、文字列をメモリに格納するために内部的に UTF-8 を使用することはありません。これは可変幅エンコーディングであるため、ストレージに UTF-8 を使用すると文字列を効率的にインデックス化できません。
代わりに、Python は 3 つの異なるシステムのいずれかを使用します。
Python 3.3 までは選択肢があります。Python インタープリターをコンパイルするときに、メモリ内ストレージに UTF-16 または UTF-32 のどちらを使用するかを選択できます。この選択は、「狭い」または「広い」ビルドと呼ばれます。ナロー ビルドはメモリの使用量が少なくなりますが、上記のコード ポイントをうまく処理できませんU+FFFF
。ワイド ビルドはより多くのメモリを使用しますが、コード ポイントの全範囲を完全に処理します。
Python 3.3 以降、文字列をメモリに格納する方法の選択は、Python インタープリターをビルドするときに事前に決定されなくなりました。代わりに、Python は個々の文字列ごとに最も効率的な内部表現を自動的に選択します。ASCII または Latin-1 文字のみを使用する文字列は、1 文字につき 1 バイトを使用します。U+FFFF
1 文字あたり最大 2 バイトを使用するコード ポイントを使用する文字列。上記のコード ポイントを使用する文字列のみが、1 文字あたり 4 バイトを使用します。
したがって、これが Python 3 であると仮定すると: 'Jalape \xYY \xZZ o'
(読みやすくするためのスペース) DB が言っていることは、愚かなユーザーはハラペーニョに squiggly-n を期待するだろうが、代わりに次のようになるということです。 9 Unicode ポイントまたは 9-UTF8 文字。正しい?
すこし。上記を参照。
「これは、UTF-8 では、マルチバイト シーケンス\xc3\xb1
がU+00F1
2 つの文字U+00C3
とU+00B1
"
彼は、 UTF-8 を使用してエンコードすると、単一のコード ポイントU+00F1
(文字ñ
、チルダ付きの n) が 2 バイト (16 進数) として格納されることを意味します。c3b1
しかし、\xc3
\xb1
文字を (バイトではなく) Unicode 文字列に詰め込むと、2 つの Unicode 文字U+00C3
とU+00B1
.
別の言い方をすれば、文字列内では、Python は 16 進エスケープ\xC3
を Unicode コード ポイント\u00C3
または
\U000000C3
.
ただし、バイト文字列を作成する場合:
b'Jalape\xc3\xb1o'
おそらく元の投稿者が行ったように、UTF-8エンコーディングのテーブルを検索し、それらのバイトを文字列にデコードすると、期待どおりの結果が得られます。b
プレフィックスが不要なPython 2.5 を使用する場合:
py> tasty = 'Jalape\xc3\xb1o' # actually bytes
py> tasty.decode('utf-8')
u'Jalape\xf1o'
py> print tasty.decode('utf-8') # oops I forgot to reset my terminal
JalapeУБo
py> print tasty.decode('utf-8') # terminal now set to UTF-8
Jalapeño
1フォント ファイルのサイズが 100K で、256 文字のグリフがあるとします。これは、グリフあたり 195 バイトになります。
2技術的には、UTF-8 スキームは、コード ポイントあたり最大 6 バイトを使用して、(仮想の) コード ポイント U+7FFFFFFF までの 31 ビット コード ポイントを処理できます。しかし、Unicode は公式には U+10FFFF を超えることはありません。したがって、UTF-8 もコード ポイントあたり 4 バイトを超えることはありません。