Unicode HOWTO —Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/2.7/howto/unicode
移動先:案内検索

Unicode HOWTO

リリース
1.03

このHOWTOでは、Python 2.xによるUnicodeのサポートについて説明し、Unicodeを使用しようとするときに一般的に遭遇するさまざまな問題について説明します。 Python 3バージョンについては、 https://docs.python.org/3/howto/unicode.html >。

Unicodeの概要

文字コードの歴史

1968年に、頭字語ASCIIでよく知られている情報交換のためのアメリカ標準コードが標準化されました。 さまざまな文字のASCII定義の数値コードで、数値は0から127まで実行されます。 たとえば、小文字の「a」には、コード値として97が割り当てられます。

ASCIIはアメリカで開発された標準であったため、アクセントのない文字のみを定義しました。 'e'はありましたが、 'é'または 'Í'はありませんでした。 これは、アクセント付き文字を必要とする言語をASCIIで忠実に表現できないことを意味しました。 (実際には、「naïve」や「cafe」などの単語を含む英語でも、欠落しているアクセントが重要です。一部の出版物には、「coöperate」などのスペルが必要なハウススタイルがあります。)

しばらくの間、人々はアクセントを表示しないプログラムを書いただけです。 1980年代半ばにフランス語の出版物に掲載されたApple] [BASICプログラムを見て、次のような行があったことを覚えています。

PRINT "MISE A JOUR TERMINEE"
PRINT "PARAMETRES ENREGISTRES"

これらのメッセージにはアクセントが含まれている必要があり、フランス語を読める人には見当違いです。

1980年代には、ほとんどすべてのパーソナルコンピュータが8ビットでした。つまり、バイトは0〜255の範囲の値を保持できました。 ASCIIコードは127までしか上がらなかったため、一部のマシンではアクセント付き文字に128〜255の値が割り当てられました。 ただし、マシンごとにコードが異なるため、ファイルの交換で問題が発生しました。 最終的に、128〜255の範囲で一般的に使用されるさまざまな値のセットが出現しました。 いくつかは国際標準化機構によって定義された真の標準であり、いくつかはある会社または別の会社によって発明され、なんとか追いつくことができた事実上の規則でした。

255文字はそれほど多くありません。 たとえば、西ヨーロッパで使用されているアクセント付き文字とロシア語で使用されているキリル文字の両方を128〜255の範囲に収めることはできません。これは、そのような文字が128を超えるためです。

さまざまなコードを使用してファイルを作成できます(KOI8と呼ばれるコーディングシステムのすべてのロシア語ファイル、Latin1と呼ばれる別のコーディングシステムのすべてのフランス語ファイル)が、ロシア語のテキストを引用するフランス語のドキュメントを作成する場合はどうでしょうか。 1980年代に人々はこの問題を解決したいと考え始め、Unicodeの標準化の取り組みが始まりました。

Unicodeは、8ビット文字の代わりに16ビット文字を使用することから始まりました。 16ビットは、2 ^ 16 = 65,536の異なる値が使用可能であることを意味し、多くの異なるアルファベットからの多くの異なる文字を表すことができます。 最初の目標は、Unicodeにすべての人間の言語のアルファベットを含めることでした。 16ビットでもその目標を達成するには不十分であることが判明し、最新のUnicode仕様では、0〜1,114,111(基数16では0x10ffff)というより広い範囲のコードが使用されています。

関連するISO規格、ISO10646があります。 UnicodeとISO10646は元々別々の取り組みでしたが、仕様はUnicodeの1.1リビジョンとマージされました。

(Unicodeの歴史に関するこの議論は非常に単純化されています。 平均的なPythonプログラマーは、歴史的な詳細について心配する必要はないと思います。 詳細については、リファレンスにリストされているユニコードコンソーシアムのサイトを参照してください。)


定義

文字は、テキストの可能な限り最小のコンポーネントです。 「A」、「B」、「C」などはすべて異なる文字です。 'È'と 'Í'もそうです。 文字は抽象化されており、話している言語やコンテキストによって異なります。 たとえば、オーム(Ω)の記号は通常、ギリシャ語のアルファベットの大文字のオメガ(Ω)のように描かれますが(一部のフォントでは同じ場合もあります)、これらは異なる意味を持つ2つの異なる文字です。

Unicode標準では、文字がコードポイントでどのように表されるかが説明されています。 コードポイントは整数値であり、通常は基数16で示されます。 標準では、コードポイントはU + 12caという表記を使用して記述され、値が0x12ca(10進数で4810)の文字を意味します。 Unicode標準には、文字とそれに対応するコードポイントを一覧表示する多くのテーブルが含まれています。

0061    'a'; LATIN SMALL LETTER A
0062    'b'; LATIN SMALL LETTER B
0063    'c'; LATIN SMALL LETTER C
...
007B    '{'; LEFT CURLY BRACKET

厳密には、これらの定義は、「これは文字U + 12caである」と言っても意味がないことを意味します。 U + 12caは、特定の文字を表すコードポイントです。 この場合、それは文字「ETHIOPICSYLLABLEWI」を表します。 非公式のコンテキストでは、コードポイントと文字のこの区別は時々忘れられます。

文字は、グリフと呼ばれる一連のグラフィック要素によって画面または紙に表されます。 たとえば、大文字のAのグリフは、2つの斜めのストロークと1つの水平のストロークですが、正確な詳細は使用されているフォントによって異なります。 ほとんどのPythonコードは、グリフについて心配する必要はありません。 表示する正しいグリフを見つけることは、通常、GUIツールキットまたは端末のフォントレンダラーの仕事です。


エンコーディング

前のセクションを要約すると、Unicode文字列は0から0x10ffffまでの数字であるコードポイントのシーケンスです。 このシーケンスは、メモリ内のバイトのセット(つまり、0〜255の値)として表す必要があります。 Unicode文字列をバイトシーケンスに変換するための規則は、エンコーディングと呼ばれます。

あなたが考えるかもしれない最初のエンコーディングは32ビット整数の配列です。 この表現では、文字列「Python」は次のようになります。

   P           y           t           h           o           n
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

この表現は単純ですが、それを使用すると多くの問題が発生します。

  1. ポータブルではありません。 プロセッサが異なれば、バイトの順序も異なります。
  2. それはスペースの非常に無駄です。 ほとんどのテキストでは、コードポイントの大部分が127未満または255未満であるため、多くのスペースがゼロバイトで占められています。 上記の文字列は、ASCII表現に必要な6バイトと比較して、24バイトかかります。 RAM使用量の増加はそれほど重要ではありませんが(デスクトップコンピューターにはメガバイトのRAMがあり、文字列は通常それほど大きくありません)、ディスクとネットワークの帯域幅の使用量を4倍に増やすことは耐えられません。
  3. strlen()などの既存のC関数とは互換性がないため、新しいファミリのワイド文字列関数を使用する必要があります。
  4. 多くのインターネット標準はテキストデータで定義されており、ゼロバイトが埋め込まれたコンテンツを処理できません。

一般に、人々はこのエンコーディングを使用せず、代わりに、より効率的で便利な他のエンコーディングを選択します。 UTF-8は、おそらく最も一般的にサポートされているエンコーディングです。 これについては以下で説明します。

エンコーディングは、考えられるすべてのUnicode文字を処理する必要はなく、ほとんどのエンコーディングは処理しません。 たとえば、Pythonのデフォルトのエンコーディングは「ascii」エンコーディングです。 Unicode文字列をASCIIエンコーディングに変換するためのルールは単純です。 コードポイントごとに:

  1. コードポイントが128未満の場合、各バイトはコードポイントの値と同じです。
  2. コードポイントが128以上の場合、Unicode文字列をこのエンコーディングで表すことはできません。 (この場合、PythonはUnicodeEncodeError例外を発生させます。)

Latin-1はISO-8859-1とも呼ばれ、同様のエンコーディングです。 Unicodeコードポイント0〜255はLatin-1値と同じであるため、このエンコーディングに変換するには、コードポイントをバイト値に変換するだけです。 255より大きいコードポイントが検出された場合、文字列をLatin-1にエンコードすることはできません。

エンコーディングは、Latin-1のような単純な1対1のマッピングである必要はありません。 IBMメインフレームで使用されたIBMのEBCDICについて考えてみます。 文字の値は1つのブロックにありませんでした。「a」から「i」の値は129から137でしたが、「j」から「r」の値は145から153でした。 EBCDICをエンコーディングとして使用したい場合は、おそらく何らかのルックアップテーブルを使用して変換を実行しますが、これは主に内部の詳細です。

UTF-8は、最も一般的に使用されるエンコーディングの1つです。 UTFは「UnicodeTransformationFormat」の略で、「8」は8ビットの数値がエンコーディングで使用されることを意味します。 (UTF-16エンコーディングもありますが、UTF-8よりも使用頻度は低くなります。)UTF-8は次のルールを使用します。

  1. コードポイントが128未満の場合、対応するバイト値で表されます。
  2. コードポイントが128〜0x7ffの場合、128〜255の2バイト値に変換されます。
  3. コードポイント> 0x7ffは、3バイトまたは4バイトのシーケンスに変換されます。シーケンスの各バイトは128〜255です。

UTF-8にはいくつかの便利なプロパティがあります。

  1. これは、任意のUnicodeコードポイントを処理できます。
  2. Unicode文字列は、ゼロバイトが埋め込まれていないバイト文字列に変換されます。 これにより、バイト順序の問題が回避され、UTF-8文字列をstrcpy()などのC関数で処理して、ゼロバイトを処理できないプロトコルを介して送信できるようになります。
  3. ASCIIテキストの文字列も有効なUTF-8テキストです。
  4. UTF-8はかなりコンパクトです。 コードポイントの大部分は2バイトに変換され、128未満の値は1バイトのみを占有します。
  5. バイトが破損または失われた場合、次のUTF-8でエンコードされたコードポイントの開始を判別して再同期することができます。 また、ランダムな8ビットデータが有効なUTF-8のように見える可能性はほとんどありません。


参考文献

http://www.unicode.org >文字チャート、用語集、およびUnicode仕様のPDFバージョンがあります。 読みにくいものに備えてください。 < http://www.unicode.org/history/ >はUnicodeの起源と発展の年表です。

標準を理解しやすくするために、Jukka Korpelaは、Unicode文字テーブルを読み取るための入門ガイドを作成しました。 https://www.cs.tut.fi/~jkorpela/unicode/guide.html >。

別の優れた紹介記事は、JoelSpolskyによって書かれました< http://www.joelonsoftware.com/articles/Unicode.html >。 この紹介で物事が明確にならなかった場合は、続行する前にこの代替記事を読んでみてください。

ウィキペディアのエントリはしばしば役に立ちます。 「文字エンコード」のエントリを参照してください< http://en.wikipedia.org/wiki/Character_encoding >およびUTF-8 < http://en.wikipedia.org/wiki/UTF-8 >、例えば。


Python2.xのUnicodeサポート

Unicodeの基本を学んだので、PythonのUnicode機能を見てみましょう。

Unicodeタイプ

Unicode文字列は、Pythonの組み込み型のレパートリーの1つである unicode 型のインスタンスとして表現されます。 これは、 basestring と呼ばれる抽象型から派生します。これは、 str 型の祖先でもあります。 したがって、値がisinstance(value, basestring)の文字列型であるかどうかを確認できます。 内部的には、Pythonは、Pythonインタープリターのコンパイル方法に応じて、Unicode文字列を16ビットまたは32ビット整数として表します。

unicode()コンストラクターには署名unicode(string[, encoding, errors])があります。 その引数はすべて8ビット文字列である必要があります。 最初の引数は、指定されたエンコーディングを使用してUnicodeに変換されます。 encoding引数を省略した場合、変換にはASCIIエンコードが使用されるため、127より大きい文字はエラーとして扱われます。

>>> unicode('abcdef')
u'abcdef'
>>> s = unicode('abcdef')
>>> type(s)
<type 'unicode'>
>>> unicode('abcdef' + chr(255))    
Traceback (most recent call last):
...
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 6:
ordinal not in range(128)

errors引数は、入力文字列がエンコーディングの規則に従って変換できない場合の応答を指定します。 この引数の有効な値は、「strict」(UnicodeDecodeError例外を発生させる)、「replace」(U + FFFD、「REPLACEMENT CHARACTER」を追加)、または「ignore」(Unicodeの結果から文字を除外するだけ)です。 )。 次の例は違いを示しています。

>>> unicode('\x80abc', errors='strict')     
Traceback (most recent call last):
    ...
UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0:
ordinal not in range(128)
>>> unicode('\x80abc', errors='replace')
u'\ufffdabc'
>>> unicode('\x80abc', errors='ignore')
u'abc'

エンコーディングは、エンコーディングの名前を含む文字列として指定されます。 Python 2.7には、約100の異なるエンコーディングが付属しています。 リストについては、 Standard Encodings のPythonライブラリリファレンスを参照してください。 一部のエンコーディングには複数の名前があります。 たとえば、「latin-1」、「iso_8859_1」、「8859」はすべて同じエンコーディングの同義語です。

1文字のUnicode文字列は、 unichr()組み込み関数を使用して作成することもできます。この関数は、整数を受け取り、対応するコードポイントを含む長さ1のUnicode文字列を返します。 逆の操作は、組み込みの ord()関数であり、1文字のUnicode文字列を受け取り、コードポイント値を返します。

>>> unichr(40960)
u'\ua000'
>>> ord(u'\ua000')
40960

unicode タイプのインスタンスには、検索やフォーマットなどの操作のために、8ビット文字列タイプと同じメソッドが多数あります。

>>> s = u'Was ever feather so lightly blown to and fro as this multitude?'
>>> s.count('e')
5
>>> s.find('feather')
9
>>> s.find('bird')
-1
>>> s.replace('feather', 'sand')
u'Was ever sand so lightly blown to and fro as this multitude?'
>>> s.upper()
u'WAS EVER FEATHER SO LIGHTLY BLOWN TO AND FRO AS THIS MULTITUDE?'

これらのメソッドの引数は、Unicode文字列または8ビット文字列である可能性があることに注意してください。 8ビット文字列は操作を実行する前にUnicodeに変換されます。 PythonのデフォルトのASCIIエンコーディングが使用されるため、127を超える文字は例外を引き起こします。

>>> s.find('Was\x9f')                   
Traceback (most recent call last):
    ...
UnicodeDecodeError: 'ascii' codec can't decode byte 0x9f in position 3:
ordinal not in range(128)
>>> s.find(u'Was\x9f')
-1

したがって、文字列を操作するPythonコードの多くは、コードを変更しなくてもUnicode文字列で機能します。 (入力コードと出力コードは、Unicode用にさらに更新する必要があります。これについては後で詳しく説明します。)

もう1つの重要なメソッドは、.encode([encoding], [errors='strict'])です。これは、要求されたエンコーディングでエンコードされたUnicode文字列の8ビット文字列バージョンを返します。 errorsパラメーターは、unicode()コンストラクターのパラメーターと同じですが、1つの追加の可能性があります。 'strict'、 'ignore'、および 'replace'に加えて、XMLの文字参照を使用する 'xmlcharrefreplace'を渡すこともできます。 次の例は、さまざまな結果を示しています。

>>> u = unichr(40960) + u'abcd' + unichr(1972)
>>> u.encode('utf-8')
'\xea\x80\x80abcd\xde\xb4'
>>> u.encode('ascii')                       
Traceback (most recent call last):
    ...
UnicodeEncodeError: 'ascii' codec can't encode character u'\ua000' in
position 0: ordinal not in range(128)
>>> u.encode('ascii', 'ignore')
'abcd'
>>> u.encode('ascii', 'replace')
'?abcd?'
>>> u.encode('ascii', 'xmlcharrefreplace')
'&#40960;abcd&#1972;'

Pythonの8ビット文字列には、指定されたエンコーディングを使用して文字列を解釈する.decode([encoding], [errors])メソッドがあります。

>>> u = unichr(40960) + u'abcd' + unichr(1972)   # Assemble a string
>>> utf8_version = u.encode('utf-8')             # Encode as UTF-8
>>> type(utf8_version), utf8_version
(<type 'str'>, '\xea\x80\x80abcd\xde\xb4')
>>> u2 = utf8_version.decode('utf-8')            # Decode using UTF-8
>>> u == u2                                      # The two strings match
True

使用可能なエンコーディングを登録およびアクセスするための低レベルのルーチンは、コーデックモジュールにあります。 ただし、このモジュールによって返されるエンコードおよびデコード関数は、通常、快適なものよりも低レベルであるため、ここではコーデックモジュールについては説明しません。 まったく新しいエンコーディングを実装する必要がある場合は、コーデックモジュールインターフェイスについて学習する必要がありますが、エンコーディングの実装は特殊なタスクであり、ここでは取り上げません。 このモジュールの詳細については、Pythonのドキュメントを参照してください。

codecs モジュールで最も一般的に使用される部分は、 codecs.open()関数です。これについては、入力と出力のセクションで説明します。


PythonソースコードのUnicodeリテラル

Pythonソースコードでは、Unicodeリテラルは「u」または「U」文字のプレフィックスが付いた文字列として記述されます:u'abcdefghijk'。 特定のコードポイントは、\uエスケープシーケンスを使用して記述できます。その後に、コードポイントを示す4桁の16進数が続きます。 \Uエスケープシーケンスも同様ですが、4桁ではなく8桁の16進数が必要です。

Unicodeリテラルは、\xを含む8ビット文字列と同じエスケープシーケンスを使用することもできますが、\xは2桁の16進数しか使用しないため、任意のコードポイントを表すことはできません。 8進数のエスケープは、8進数の777であるU + 01ffまで上がる可能性があります。

>>> s = u"a\xac\u1234\u20ac\U00008000"
... #      ^^^^ two-digit hex escape
... #          ^^^^^^ four-digit Unicode escape
... #                      ^^^^^^^^^^ eight-digit Unicode escape
>>> for c in s:  print ord(c),
...
97 172 4660 8364 32768

127を超えるコードポイントにエスケープシーケンスを使用することは、少量では問題ありませんが、フランス語やその他のアクセントを使用する言語のメッセージを使用するプログラムのように、アクセント付きの文字を多数使用する場合は煩わしくなります。 unichr()組み込み関数を使用して文字列をアセンブルすることもできますが、これはさらに面倒です。

理想的には、言語の自然なエンコーディングでリテラルを記述できるようにする必要があります。 次に、アクセント付き文字を自然に表示し、実行時に適切な文字を使用するお気に入りのエディターを使用して、Pythonソースコードを編集できます。

Pythonは、任意のエンコーディングでのUnicodeリテラルの記述をサポートしていますが、使用されているエンコーディングを宣言する必要があります。 これは、ソースファイルの1行目または2行目に特別なコメントを含めることによって行われます。

#!/usr/bin/env python
# -*- coding: latin-1 -*-

u = u'abcdé'
print ord(u[-1])

構文は、ファイルにローカルな変数を指定するためのEmacsの表記法に触発されています。 Emacsは多くの異なる変数をサポートしていますが、Pythonは「コーディング」のみをサポートしています。 -*-記号は、コメントが特別であることをEmacsに示します。 これらはPythonにとって重要ではありませんが、慣例です。 Pythonは、コメントでcoding: nameまたはcoding=nameを探します。

このようなコメントを含めない場合、使用されるデフォルトのエンコーディングはASCIIになります。 2.4より前のバージョンのPythonはユーロ中心であり、文字列リテラルのデフォルトのエンコーディングとしてLatin-1を想定していました。 Python 2.4では、127を超える文字は引き続き機能しますが、警告が表示されます。 たとえば、次のプログラムにはエンコード宣言がありません。

#!/usr/bin/env python
u = u'abcdé'
print ord(u[-1])

Python 2.4で実行すると、次の警告が出力されます。

amk:~$ python2.4 p263.py
sys:1: DeprecationWarning: Non-ASCII character '\xe9'
     in file p263.py on line 2, but no encoding declared;
     see https://www.python.org/peps/pep-0263.html for details

Python 2.5以降はより厳密であり、構文エラーが発生します。

amk:~$ python2.5 p263.py
File "/tmp/p263.py", line 2
SyntaxError: Non-ASCII character '\xc3' in file /tmp/p263.py
  on line 2, but no encoding declared; see
  https://www.python.org/peps/pep-0263.html for details

Unicodeプロパティ

Unicode仕様には、コードポイントに関する情報のデータベースが含まれています。 定義されたコードポイントごとに、情報には文字の名前、カテゴリ、該当する場合は数値が含まれます(Unicodeには、ローマ数字と3分の1や5分の4などの分数を表す文字があります)。 双方向テキストでのコードポイントの使用に関連するプロパティやその他の表示関連のプロパティもあります。

次のプログラムは、いくつかの文字に関する情報を表示し、特定の1文字の数値を出力します。

import unicodedata

u = unichr(233) + unichr(0x0bf2) + unichr(3972) + unichr(6000) + unichr(13231)

for i, c in enumerate(u):
    print i, '%04x' % ord(c), unicodedata.category(c),
    print unicodedata.name(c)

# Get numeric value of second character
print unicodedata.numeric(u[1])

実行すると、次のように出力されます。

0 00e9 Ll LATIN SMALL LETTER E WITH ACUTE
1 0bf2 No TAMIL NUMBER ONE THOUSAND
2 0f84 Mn TIBETAN MARK HALANTA
3 1770 Lo TAGBANWA LETTER SA
4 33af So SQUARE RAD OVER S SQUARED
1000.0

カテゴリコードは、文字の性質を説明する略語です。 これらは、「文字」、「数字」、「句読点」、「記号」などのカテゴリにグループ化され、サブカテゴリに分類されます。 上記の出力からコードを取得するには、'Ll'は「文字、小文字」、'No'は「数字、その他」、'Mn'は「マーク、非間隔」、[ X148X] は「シンボル、その他」です。 http://www.unicode.org/reports/tr44/#General_Category_Values >カテゴリコードのリストについては。


参考文献

Unicodeおよび8ビットの文字列型については、Pythonライブラリリファレンスのシーケンスタイプ— str、unicode、list、tuple、bytearray、buffer、xrange で説明されています。

unicodedata モジュールのドキュメント。

コーデックモジュールのドキュメント。

Marc-AndréLemburgは、EuroPython2002で「PythonとUnicode」というタイトルのプレゼンテーションを行いました。 彼のスライドのPDF版は https://downloads.egenix.com/python/Unicode-EPC2002-Talk.pdf >であり、PythonのUnicode機能の設計の優れた概要です。


Unicodeデータの読み取りと書き込み

Unicodeデータで機能するコードを記述したら、次の問題は入出力です。 Unicode文字列をプログラムに取り込むにはどうすればよいですか?また、Unicodeを保存または送信に適した形式に変換するにはどうすればよいですか?

入力ソースと出力先によっては、何もする必要がない場合があります。 アプリケーションで使用されているライブラリがUnicodeをネイティブにサポートしているかどうかを確認する必要があります。 たとえば、XMLパーサーはUnicodeデータを返すことがよくあります。 多くのリレーショナルデータベースもUnicode値の列をサポートしており、SQLクエリからUnicode値を返すことができます。

Unicodeデータは通常、ディスクに書き込まれる前、またはソケットを介して送信される前に、特定のエンコーディングに変換されます。 すべての作業を自分で行うことができます。ファイルを開き、そこから8ビットの文字列を読み取り、unicode(str, encoding)を使用して文字列を変換します。 ただし、手動によるアプローチはお勧めしません。

1つの問題は、エンコーディングのマルチバイトの性質です。 1つのUnicode文字は数バイトで表すことができます。 ファイルを任意のサイズのチャンク(たとえば、1Kまたは4K)で読み取りたい場合は、エラー処理コードを記述して、単一のUnicode文字をエンコードするバイトの一部のみが最後に読み取られる場合をキャッチする必要があります。チャンク。 1つの解決策は、ファイル全体をメモリに読み込んでからデコードを実行することですが、これにより、非常に大きなファイルを操作できなくなります。 2Gbファイルを読み取る必要がある場合は、2GbのRAMが必要です。 (実際には、少なくともしばらくの間、エンコードされた文字列とそのUnicodeバージョンの両方をメモリに保持する必要があるためです。)

解決策は、低レベルのデコードインターフェイスを使用して、部分的なコーディングシーケンスのケースをキャッチすることです。 これを実装する作業はすでに完了しています。 codecs モジュールには、 open()関数のバージョンが含まれており、ファイルの内容が指定されたエンコーディングであり、.read().write()などのメソッドのUnicodeパラメーターを受け入れます。

関数のパラメーターはopen(filename, mode='rb', encoding=None, errors='strict', buffering=1)です。 modeは、通常の組み込みopen()関数に対応するパラメーターと同様に、'r''w'、または'a'にすることができます。 '+'を追加してファイルを更新します。 bufferingも同様に、標準関数のパラメーターと並列です。 encodingは、使用するエンコーディングを指定する文字列です。 Noneのままにすると、8ビット文字列を受け入れる通常のPythonファイルオブジェクトが返されます。 それ以外の場合は、ラッパーオブジェクトが返され、ラッパーオブジェクトに書き込まれたデータまたはラッパーオブジェクトから読み取られたデータは、必要に応じて変換されます。 errorsは、エラーをエンコードするためのアクションを指定し、「strict」、「ignore」、および「replace」の通常の値の1つにすることができます。

したがって、ファイルからUnicodeを読み取るのは簡単です。

import codecs
f = codecs.open('unicode.rst', encoding='utf-8')
for line in f:
    print repr(line)

更新モードでファイルを開くことも可能で、読み取りと書き込みの両方が可能です。

f = codecs.open('test', encoding='utf-8', mode='w+')
f.write(u'\u4500 blah blah blah\n')
f.seek(0)
print repr(f.readline()[:1])
f.close()

Unicode文字U + FEFFは、バイト順マーク(BOM)として使用され、ファイルのバイト順の自動検出を支援するために、ファイルの最初の文字として書き込まれることがよくあります。 UTF-16などの一部のエンコーディングでは、ファイルの先頭にBOMが存在することを想定しています。 このようなエンコーディングを使用すると、BOMは最初の文字として自動的に書き込まれ、ファイルが読み取られるときにサイレントにドロップされます。 これらのエンコーディングには、リトルエンディアンおよびビッグエンディアンエンコーディングの「utf-16-le」や「utf-16-be」など、1つの特定のバイト順序を指定し、BOMをスキップしないバリアントがあります。

Unicodeファイル名

現在一般的に使用されているオペレーティングシステムのほとんどは、任意のUnicode文字を含むファイル名をサポートしています。 通常、これはUnicode文字列をシステムによって異なるエンコーディングに変換することによって実装されます。 たとえば、Mac OS XはUTF-8を使用しますが、Windowsは構成可能なエンコーディングを使用します。 Windowsでは、Pythonは「mbcs」という名前を使用して、現在構成されているエンコーディングを参照します。 Unixシステムでは、LANGまたはLC_CTYPE環境変数を設定した場合にのみファイルシステムエンコーディングが行われます。 そうでない場合、デフォルトのエンコーディングはASCIIです。

sys.getfilesystemencoding()関数は、手動でエンコードを実行する場合に備えて、現在のシステムで使用するエンコードを返しますが、気にする理由はあまりありません。 読み取りまたは書き込み用にファイルを開くときは、通常、ファイル名としてUnicode文字列を指定するだけで、適切なエンコーディングに自動的に変換されます。

filename = u'filename\u4500abc'
f = open(filename, 'w')
f.write('blah\n')
f.close()

os.stat()などの os モジュールの関数も、Unicodeファイル名を受け入れます。

ファイル名を返す os.listdir()は、問題を引き起こします。Unicodeバージョンのファイル名を返す必要がありますか、それともエンコードされたバージョンを含む8ビット文字列を返す必要がありますか? os.listdir()は、ディレクトリパスを8ビット文字列として指定したかUnicode文字列として指定したかに応じて、両方を実行します。 パスとしてUnicode文字列を渡すと、ファイルシステムのエンコーディングを使用してファイル名がデコードされ、Unicode文字列のリストが返されます。一方、8ビットパスを渡すと、ファイル名の8ビットバージョンが返されます。 たとえば、デフォルトのファイルシステムエンコーディングがUTF-8であると仮定して、次のプログラムを実行します。

fn = u'filename\u4500abc'
f = open(fn, 'w')
f.close()

import os
print os.listdir('.')
print os.listdir(u'.')

次の出力が生成されます。

amk:~$ python t.py
['.svn', 'filename\xe4\x94\x80abc', ...]
[u'.svn', u'filename\u4500abc', ...]

最初のリストにはUTF-8でエンコードされたファイル名が含まれ、2番目のリストにはUnicodeバージョンが含まれています。


Unicode対応プログラムを作成するためのヒント

このセクションでは、Unicodeを扱うソフトウェアの作成に関するいくつかの提案を提供します。

最も重要なヒントは次のとおりです。

ソフトウェアは内部でUnicode文字列のみを処理し、出力時に特定のエンコーディングに変換する必要があります。


Unicode文字列と8ビット文字列の両方を受け入れる処理関数を作成しようとすると、2つの異なる種類の文字列を組み合わせると、プログラムがバグに対して脆弱になることがわかります。 PythonのデフォルトのエンコーディングはASCIIであるため、ASCII値が127を超える文字が入力データに含まれている場合、その文字はASCIIエンコーディングで処理できないため、UnicodeDecodeErrorが表示されます。

アクセントを含まないデータでソフトウェアをテストするだけでは、このような問題を見逃しがちです。 すべてが機能しているように見えますが、実際には、127を超える文字を使用しようとする最初のユーザーを待っているプログラムにバグがあります。 したがって、2番目のヒントは次のとおりです。

テストデータには、127を超える文字、さらには255を超える文字を含めます。


Webブラウザーまたはその他の信頼できないソースからのデータを使用する場合、一般的な手法は、生成されたコマンドラインで文字列を使用したりデータベースに保存したりする前に、文字列内の不正な文字をチェックすることです。 これを行う場合は、使用または保存される形式になったら、文字列を注意深く確認してください。 文字を偽装するためにエンコーディングを使用することは可能です。 これは、入力データがエンコーディングも指定している場合に特に当てはまります。 多くのエンコーディングでは、一般的にチェックされる文字だけが残されていますが、Pythonには、すべての文字を変更する'base64'などのエンコーディングが含まれています。

たとえば、Unicodeファイル名を使用するコンテンツ管理システムがあり、「/」文字を含むパスを禁止するとします。 あなたはこのコードを書くかもしれません:

def read_file (filename, encoding):
    if '/' in filename:
        raise ValueError("'/' not allowed in filenames")
    unicode_name = filename.decode(encoding)
    f = open(unicode_name, 'r')
    # ... return contents of file ...

ただし、攻撃者が'base64'エンコーディングを指定できる場合は、文字列'/etc/passwd'のbase-64エンコード形式である'L2V0Yy9wYXNzd2Q='を渡してシステムファイルを読み取る可能性があります。 上記のコードは、エンコードされた形式で'/'文字を検索し、結果のデコードされた形式で危険な文字を見逃します。


参考文献

Marc-AndréLemburgのプレゼンテーション「PythonでのUnicode対応アプリケーションの作成」のPDFスライドは、 https://downloads.egenix.com/python/LSM2005-Developing-Unicode-aware-applications-in-Python.pdf >そして、文字エンコードの質問と、アプリケーションを国際化およびローカライズする方法について話し合います。


改訂履歴と謝辞

この記事でエラーを指摘したり提案を提供した次の人々に感謝します:ニコラス・バスティン、マリウス・ゲドミナス、ケント・ジョンソン、ケン・クルーグラー、マーク・アンドレ・レンバーグ、マーティン・フォン・レーウィス、チャド・ウィタクレ。

バージョン1.0:2005年8月5日投稿。

バージョン1.01:2005年8月7日投稿。 事実とマークアップのエラーを修正します。 いくつかのリンクを追加します。

バージョン1.02:2005年8月16日投稿。 事実上のエラーを修正します。

バージョン1.03:2010年6月20日投稿。 Python 3.xはカバーされておらず、HOWTOは2.xのみをカバーしていることに注意してください。