2

ファイルから SQL ステートメントを読み取り、MySQL データベースで実行する小さな Python プログラムがあります。ファイルは UTF-8 でエンコードされ、データベースも UTF-8 を使用します。

データベースのエンコーディングを設定しないと、「'latin-1' コーデックは文字をエンコードできません...」というよくあるエラーが表示されます。だから私はデータベースとファイルのエンコーディングを設定します

con.set_character_set('utf8')
fh = codecs.open(fname,'r','utf8')

今では動作しますが、ファイルエンコーディングを設定しない場合 (または組み込みの open を使用する場合) にも、データベース上で動作します。「動作する」とは、結果のデータベース レコードが、UTF-8 を前提とする WordPress で適切に表示されることを意味します。

魔法が欲しければ、Ruby でコーディングします。この場合、Python は何をしているのでしょうか。ファイルのエンコーディングを伝える必要がなかったのはなぜですか?

言うまでもなく、私はこれについて多くの検索を行ってきましたが、通常、私の Google-foo はかなり優れています。ここやブログには、エンコーディングを設定する必要がある理由とその方法に関する投稿がたくさんありますが、なぜそれが機能するのかについては何も見つかりませんでした.

編集: 「ありがとう」を含むファイルを使用して、これについて簡単なテストを実行しました。</p>

file
  E2 80 9C 54 68 61 6E 6B 20 79 6F 75 2E E2 80 9D
codecs utf8
  201C 54 68 61 6E 6B 20 79 6F 75 2E 201D

codecs.open(myfile,'r','ascii') で読み取ろうとすると、「UnicodeDecodeError: 'ascii' コーデックはバイト 0xe2 をデコードできません」が返されました

ファイルからの読み取りでバイト文字列が生成されたため、データベースへの挿入で魔法が発生しているように見えます。

4

2 に答える 2

1

PythonでのUnicodeに関するこのチュートリアルの4番目の段落では、codecs.open(filename, mode, [encoding])使用している関数を説明するよりも書かれています。

encoding使用するエンコーディングを指定する文字列です。Noneのままにすると、8ビットの文字列を受け入れる通常のPythonファイルオブジェクトが返されます。

また、Fileオブジェクトのリファレンスでは

file.encoding)はNoneの場合もあります。この場合、ファイルはシステムのデフォルトのエンコーディングを使用してUnicode文字列を変換します。

エンコーディングパラメータなしで呼び出すcodecs.open()と、Fileオブジェクトは(テスト済み)のエンコーディング属性で返されますNone。したがって、Unicodeのシステムデフォルトを使用します。この場合、UTF-8である必要があります。これは、明示的でないときにそれがとてもうまく機能している理由を説明しています。

于 2013-01-19T16:01:26.917 に答える
1

使用するとき

fh = codecs.open(fname,'r','utf8')

fh.read()ユニコードを返します。このユニコードを取得し、データベース ドライバー (mysql-python など) を使用してデータベースにデータを挿入する場合、ドライバーはユニコードをバイトに変換する必要があります。ドライバーは、によって設定されたエンコードを使用しています

con.set_character_set('utf8')

使用する場合

fh = open(fname, 'r')

次にfh.read()、バイト文字列を返します。たまたま入っていたバイトに翻弄されfnameます。幸いなことに、あなたの投稿によると、ファイルは UTF-8 でエンコードされています。データはすでにバイト文字列であるため、ドライバーはエンコードを実行せず、単にバイト文字列をそのままデータベースに通信します。

いずれにしても、UTF-8 でエンコードされたバイトの同じ文字列がデータベースに挿入されます。


codecs.openを定義するソース コードを見てみましょう。

def open(filename, mode='rb', encoding=None, errors='strict', buffering=1):

    if encoding is not None:
        if 'U' in mode:
            # No automatic conversion of '\n' is done on reading and writing
            mode = mode.strip().replace('U', '')
            if mode[:1] not in set('rwa'):
                mode = 'r' + mode
        if 'b' not in mode:
            # Force opening of the file in binary mode
            mode = mode + 'b'
    file = __builtin__.open(filename, mode, buffering)
    if encoding is None:
        return file
    info = lookup(encoding)
    srw = StreamReaderWriter(file, info.streamreader, info.streamwriter, errors)
    # Add attributes to simplify introspection
    srw.encoding = encoding
    return srw

encoding特に、noが設定されている場合に何が起こるかに注意してください。

file = __builtin__.open(filename, mode, buffering)
if encoding is None:
     return file

エンコーディングが設定されていない場合はcodecs.open、基本的にビルトインと同じです。openビルトインは、メソッドがstropenオブジェクトを返すファイル オブジェクトを返します。デコードは一切行いません。read

対照的に、エンコーディングを指定すると、に設定されcodecs.openた が返されます。のメソッドを呼び出すと、通常、 Unicodeオブジェクトが返されます。最初に、指定されたエンコーディングを使用してstrオブジェクトをデコードする必要があります。StreamReaderWritersrw.encodingencodingStreamReaderWriterread

あなたの例では、strオブジェクトは

In [19]: content
Out[19]: '\xe2\x80\x9cThank you.\xe2\x80\x9d'

エンコーディングを として指定すると、 はエンコーディング'ascii'を使用しStreamReaderWriterてデコードを試みます。content'ascii'

In [20]: content.decode('ascii')

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)

asciiエンコーディングでは 0 ~ 127 の範囲のバイトしかデコードできず'\xe2'、 の最初のバイトでcontentある にはその範囲外の序数値があるため、これは驚くべきことではありません。


具体的には:エンコーディングを指定しない場合

In [13]: with codecs.open(filename, 'r') as f:
   ....:     content = f.read() 

In [14]: content
Out[14]: '\xe2\x80\x9cThank you.\xe2\x80\x9d'

contentですstr

有効なエンコーディングを指定すると:

In [22]: with codecs.open(filename, 'r', encoding = 'utf-8') as f:
   ....:     content = f.read()


In [23]: content
Out[23]: u'\u201cThank you.\u201d'

contentですunicode

無効なエンコーディングを指定した場合:

In [25]: with codecs.open(filename, 'r', 'ascii') as f:
   ....:     content = f.read()
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)

を取得しUnicodeDecodeErrorます。

于 2013-01-19T15:51:35.233 に答える