3

David BeazleyによるPython Essential Reference(第4版)の29ページによると:

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 を表すことになっているためです。

これは 9 文字ではなく 8 文字であるべきではありませんか? 彼は言う:\xc3\xb1単一の文字を表すことになっています。

4

3 に答える 3

10

別の非常に包括的な回答: 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 を表すことになっているためです。

これは、基本的な概念の混乱を示していますが、基本的な事実に偶然出くわしています。それがあなたを混乱させるのも不思議ではありません、それは私も混乱させます! :-)

エンコードは文字列を生成するのではなく、バイトを生成します。したがって、あなたが引用している人は、 「エンコードされた文字列」について話すときに混乱を引き起こしています 。彼は、バイト文字列を意味することを明確にするか、文字列という単語にまったく言及しないでください。これらのいずれかが機能します。

  • UTF-8 でエンコードされたバイト文字列b'Jalape\xc3\xb1o'

  • UTF-8 でエンコードされたバイトb'Jalape\xc3\xb1o'

古いバージョンの 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 ( c316 進数) であるため \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\xb12 バイトを表します (まあ、当然です)。これは、ほとんど何でも意味します。

  • 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(文字) は、コンピュータがビッグ エンディアンかリトル エンディアンかに応じて、 A16 進バイト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+FFFF1 文字あたり最大 2 バイトを使用するコード ポイントを使用する文字列。上記のコード ポイントを使用する文字列のみが、1 文字あたり 4 バイトを使用します。

したがって、これが Python 3 であると仮定すると: 'Jalape \xYY \xZZ o'(読みやすくするためのスペース) DB が言っていることは、愚かなユーザーはハラペーニョに squiggly-n を期待するだろうが、代わりに次のようになるということです。 9 Unicode ポイントまたは 9-UTF8 文字。正しい?

すこし。上記を参照。

「これは、UTF-8 では、マルチバイト シーケンス\xc3\xb1U+00F12 つの文字U+00C3U+00B1"

彼は、 UTF-8 を使用してエンコードすると、単一のコード ポイントU+00F1(文字ñ、チルダ付きの n) が 2 バイト (16 進数) として格納されることを意味します。c3b1しかし、\xc3 \xb1文字を (バイトではなく) Unicode 文字列に詰め込むと、2 つの Unicode 文字U+00C3U+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 バイトを超えることはありません。

于 2013-07-14T09:12:24.273 に答える
3

いいえ、その主張は正しいです。

UTF-8 では\xc3\xb1、単一の文字を表すことになっています。つまり、文字列を UTF-8 からデコードすると、1 文字、つまり 8 文字になります。

ただし、特定の例では、文字列はUTF-8 ではなく生の文字シーケンスとして扱われます。したがって、2 つのオクテットは 2 つの文字になります。

少し先に進むこともできますが、ipython の次の出力を参照してください。

In [1]: b'Jalape\xc3\xb1o'
Out[1]: b'Jalape\xc3\xb1o'

In [2]: len(b'Jalape\xc3\xb1o')
Out[2]: 9

In [3]: b'Jalape\xc3\xb1o'.decode('utf8')
Out[3]: 'Jalapeño'

In [4]: len(b'Jalape\xc3\xb1o'.decode('utf8'))
Out[4]: 8

In [5]: 'Jalape\xf1o'
Out[5]: 'Jalapeño'

上記のコードは Python 3 用です。Python 2 の場合、バイト文字列 ( b'Jalape\xc3\xb1o') は通常の文字列 ( 'Jalape\xc3\xb1o') に置き換えられ、通常の文字列は Unicode 文字列 ( u'Jalape\xf1o') に置き換えられます。

于 2013-07-13T17:00:25.737 に答える
1

https://groups.google.com/forum/#!topic/comp.lang.python/1boxbYjhClg

Joshua Landau (私の質問に答えて書いた)

「'Jalape\xc3\xb1o' のような生の UTF-8 でエンコードされた文字列を直接書き込むと、単純に 9 文字の文字列 U+004A、U+0061、U+006C、U+0061、U+0070、U+0065、 U+00C3、U+00B1、U+006F、これはおそらく意図したものではありません。これは、UTF-8 では、マルチバイト シーケンス \xc3\xb1 が、 U+00C3 と U+00B1 の 2 文字です。」

正しい。

私の最初の質問は、「これは 9 文字ではなく 8 文字であるべきではありませんか?」というものでした。

いいえ、Python はこれらの点で正しい傾向があります。

彼は次のように述べています: \xc3\xb1 は単一の文字を表すはずです。しかし、仲間のPythonistasとのやり取りの後、私はさらに混乱しました。

彼の言い方を考えると、あなたはそうなるでしょう。

上記のパラグラフを参照してください。

まあ、それは彼が与えたような文脈がなければあまり意味がありません.

Python2では、1回で「ハラペおかしなの」ができます。これは「バイト」文字列であり、各グリフは内部に格納されると 1 バイトの長さになるため、各グリフは文字セット ASCII または Latin-1 に従って整数に関連付けられます。これらの文字セットに面白い n グリフがある場合は、イェーイ! そうでなければいや!ここにはUTF-8はありません!! またはUTF-16!! これらはプレーン バイト (8 ビット) です。

Unicode は、グリフと整数の間の非常に大きなマッピング テーブルであり、Uxxxx または Uxxxx-xxxx として示されます。

あなたが実際に間違っている理由を、常駐のユニコード専門家が説明するのを待ちます

UTF-8 UTF-16 は、これらの大きな整数を効率的に格納するためのエンコーディングです。したがって、DB が「生の UTF-8 でエンコードされた文字列を書き込む」と言った場合、これを行う唯一の方法は、デフォルトの文字列リテラルが Unicode で格納されている Python3 を使用することです。それぞれの構造のバイト。または、両方の言語でユニコードである u'Jalape' を使用することもできます (先頭の 'u' に注意してください)。

正しい。

  1. したがって、これが Python 3 であると仮定すると: 'Jalape \xYY \xZZ o' (読みやすくするためのスペース) DB が言っていることは、愚かなユーザーはハラペーニョに squiggly-n を期待するだろうが、代わりに彼が得るのは: Jalape funny1 funny2 o (読みやすくするためのスペース) -9 個のグリフまたは 9 個の Unicode ポイントまたは 9 個の UTF8 文字。正しい?

そう思います。

  1. 「これは、UTF-8 では、マルチバイト シーケンス \xc3\xb1 が、U+00C3 と U+00B1 の 2 つの文字ではなく、単一の文字 U+00F1 を表すことになっているためです」

彼はいくつかのことを混同しています、AFAICT。

誰かが時間をかけて注意深く読んで、DBが何を言っているのかを明確にすることができますか??

これは簡単な説明です: あなたはどちらも間違っています (またはどちらもほぼ正しいです):

Python 3 以降:

>>> "\xc3\xb1"
'ñ'
>>> b"\xc3\xb1".decode()
'ñ'

"何?!" あなたは「それは間違っている!」と叫びます。しかし、そうではありません。説明させてください。

Python 3 の文字列では、各文字を個別に指定する必要があります ( *私が間違っている場合はひるみます * )。"\xc3"Python はas"\N{LATIN CAPITAL LETTER A WITH TILDE}"および"\xb1"as "\N{PLUS-MINUS SIGN}"¹を解釈しています。これは、Python に2 つの文字が与えられることを意味します。Pythonは基本的にこれを行っています:

number = int("c3", 16) # Convert from base16
chr(number) # Turn to the character from the Unicode mapping

Python raw bytesを指定すると、これはエンコードされたときに文字列がどのように見えるかを示しています。Python Unicode ではなく、エンコードされた Unicodeを指定しています。これは、デコード (.decode()) するときに、マルチバイト セクションを関連する文字に自由に変換できることを意味します。

エンコードされた文字列が文字列自体とどのように異なるかを確認するには、次を参照してください。

>>> "Jalepeño".encode("ASCII", errors="xmlcharrefreplace")
b'Jalepeño'

これらは同じものを表していますが、最初の (Python によると) は事であり、2 番目はデコードする必要があります。

さて、これを元に戻します:

>>> "\xc3\xb1".encode()
b'\xc3\x83\xc2\xb1'

エンコードされたバイトが2 つの文字を表していることがわかります。上記の文字列はエンコードされたものではありません。エンコーディングは Python の内部です

お役に立てば幸いです。幸運を。

¹"\N{...}"フォームの方がはるかに読みやすいので、お勧めします。

于 2013-07-14T08:06:08.753 に答える