web-dev-qa-db-ja.com

Pythonでファイルを読み書きするUnicode(UTF-8)

ファイルへのテキストの読み書きを理解するのに頭脳障害があります(Python 2.4)。

# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

( "u'Capit\xe1n '"、 "' Capit\xc3\xa1n '")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capit\xc3\xa1n\n'

だから私はCapit\xc3\xa1nを私のお気に入りのエディタにファイルf2でタイプインします。

その後:

>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'

私がここで理解していないのは何ですか?私が見逃しているのは明らかに重要な魔法(またはいい意味)がある。適切な変換を行うためにテキストファイルに何を入力していますか?

私がここでよく話さないのは、実際にPythonに認識させることができないのであれば、それがUTF-8表現のポイントである点です。おそらくJSONで文字列をダンプし、代わりにそれを使用するべきです。もっと言えば、このUnicodeオブジェクトのASCII表現は、Pythonがファイルから入ってきたときに認識してデコードするのでしょうか。もしそうなら、どうやって入手できますか?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
286
Gregg Lind

記法では

u'Capit\xe1n\n'

"\ xe1"は1バイトだけを表します。 "\ x"は "e1"が16進数であることを示しています。書くとき

Capit\xc3\xa1n

あなたのファイルには "\ xc3"が入っています。これらは4バイトであり、あなたのコードではあなたはそれら全てを読んでいます。表示するとこれがわかります。

>>> open('f2').read()
'Capit\\xc3\\xa1n\n'

バックスラッシュがバックスラッシュでエスケープされているのがわかります。したがって、文字列には "\"、 "x"、 "c"、 "3"の4バイトがあります。

編集する

他の人が彼らの答えで指摘したようにあなたはただエディタに文字を入力するべきであり、そしてあなたのエディタはそれからUTF-8への変換を処理しそしてそれを保存するべきです。

この形式の文字列が実際にある場合は、string_escapeコーデックを使用してそれを通常の文字列にデコードできます。

In [15]: print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
Capitán

結果はUTF-8でエンコードされた文字列で、アクセント記号付き文字は元の文字列で\\xc3\\xa1と書かれた2バイトで表されます。 Unicode文字列を使いたい場合は、UTF-8で再度デコードする必要があります。

あなたの編集に:あなたはあなたのファイルにUTF-8がありません。実際にそれがどのように見えるかを見るために:

s = u'Capit\xe1n\n'
sutf8 = s.encode('UTF-8')
open('utf-8.out', 'w').write(sutf8)

ファイルutf-8.outの内容と、エディタで保存したファイルの内容を比較します。

98
unbeknown

エンコードやデコードの方法をいじるよりも、ファイルを開くときにエンコードを指定する方が簡単だと思います。 ioモジュール (Python 2.6で追加された)はio.open関数を提供します。これはエンコーディングパラメータを持ちます。

ioモジュールからopenメソッドを使用してください。

>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")

そして、fのread()関数を呼び出した後、エンコードされたUnicodeオブジェクトが返されます。

>>>f.read()
u'Capit\xe1l\n\n'

Python 3では、io.open関数は組み込みのopen関数のエイリアスです。組み込みのopen関数はPython 3ではエンコーディング引数のみをサポートし、Python 2はサポートしません。

編集:以前はこの回答は コーデック モジュールを推奨していました。 codecsモジュールはread()readline() を混在させると問題を起こすことがあるので、この回答では代わりに io モジュールを推奨します。

コーデックモジュールからopenメソッドを使用してください。

>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")

そして、fのread()関数を呼び出した後、エンコードされたUnicodeオブジェクトが返されます。

>>>f.read()
u'Capit\xe1l\n\n'

あなたがファイルのエンコーディングを知っているなら、コーデックパッケージを使うことははるかに混乱を少なくするでしょう。

http://docs.python.org/library/codecs.html#codecs.open を参照してください。

655
Tim Swast

Python3に必要なのはopen(Filename, 'r', encoding='utf-8')だけです

[要求された明確化のために2016-02-10に編集]

Python 3では、open関数に encoding パラメータが追加されました。 open関数に関する以下の情報がここから集められます: https://docs.python.org/3/library/functions.html#open

open(file, mode='r', buffering=-1, 
      encoding=None, errors=None, newline=None, 
      closefd=True, opener=None)

エンコードは、ファイルをデコードまたはエンコードするために使用されるエンコードの名前です。これはテキストモードでのみ使われるべきです。デフォルトのエンコーディングはプラットフォームに依存します( locale.getpreferredencoding() が返すものは何でも)。ただし、Pythonでサポートされている textエンコーディング はすべて使用できます。サポートされているエンコーディングのリストについては codecs モジュールを見てください。

そのため、open関数のパラメータとしてencoding='utf-8'を追加することで、ファイルの読み書きはすべてutf8として行われます(これは現在Pythonで行われているすべてのデフォルトのエンコーディングです)。

30
Dakusan

だから、私は私が探しているものの解決策を見つけました、それはそれです:

print open('f2').read().decode('string-escape').decode("utf-8")

ここで役に立ついくつかの珍しいコーデックがあります。この特別な読み方では、Python内からUTF-8表現を取り出し、それらをASCIIファイルにコピーし、それらをUnicodeに読み込ませることができます。 "string-escape"デコードの下では、スラッシュは2倍にはなりません。

これは私が想像していた種類の往復を可能にします。

17
Gregg Lind
# -*- encoding: utf-8 -*-

# converting a unknown formatting file in utf-8

import codecs
import commands

file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)

file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')

for l in file_stream:
    file_output.write(l)

file_stream.close()
file_output.close()
14
Ricardo

実際、これは私にとってPython 3.2でUTF-8エンコーディングのファイルを読むのに役立ちました。

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)
14
Sina

Unicode文字列を読み込んでHTMLに送信するには、次のようにしました。

fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')

Pythonを使ったhttpサーバに便利です。

6
praj

codecs.open()を除いて、ユニコードファイルを読み書きするためにio.open()を使ってPython2またはPython3で作業することができます。

import io

text = u'á'
encoding = 'utf8'

with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout:
    fout.write(text)

with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin:
    text2 = fin.read()

assert text == text2
6
Ryan

あなたは、エンコーディングに関する一般的な問題について遭遇しました:ファイルがどのエンコーディングであるかをどうやって知ることができますか?

答え:あなたはできません そうでない限り ファイルフォーマットはこれを提供します。たとえば、XMLは次のように始まります。

<?xml encoding="utf-8"?>

このヘッダーは、エンコーディングに関係なく読み取ることができるように慎重に選択されています。あなたのケースでは、そのようなヒントはありません。ですから、あなたの編集者もPythonも何が起こっているのか、まったくわかりません。したがって、codecsモジュールを使用し、Pythonで欠けている部分を提供するcodecs.open(path,mode,encoding)を使用する必要があります。

エディタに関しては、ファイルのエンコーディングを設定する方法があるかどうかを確認する必要があります。

UTF-8のポイントは、21ビット文字(Unicode)を8ビットデータストリームとしてエンコードできることです(これが世界中のすべてのコンピュータが処理できる唯一のものであるため)。しかし、ほとんどのOSはUnicode時代を迎えているため、ハードディスク上のファイルにエンコード情報を添付するのに適したツールはありません。

次の問題はPythonでの表現です。これはheikogerlachの コメントで完全に説明されています 。コンソールにはASCIIしか表示できないことを理解しておく必要があります。 Unicodeまたは>> charcode 128を表示するためには、何らかのエスケープ手段を使用する必要があります。エディタでは、エスケープされた表示文字列を入力するのではなく、文字列が意味するものを入力する必要があります(この場合は、ウムラウトを入力してファイルを保存する必要があります)。

つまり、Pythonの関数eval()を使用して、エスケープした文字列を文字列に変換できます。

>>> x = eval("'Capit\\xc3\\xa1n\\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1

ご覧のとおり、文字列 "\ xc3"は1文字に変換されています。これは現在UTF-8でエンコードされた8ビットの文字列です。 Unicodeを取得するには

>>> x.decode('utf-8')
u'Capit\xe1n\n'

Gregg Lind 尋ねた:私はここで抜けているいくつかの部分があると思う:ファイルf2は含んでいる:hex:

0000000: 4361 7069 745c 7863 335c 7861 316e  Capit\xc3\xa1n

たとえばcodecs.open('f2','rb', 'utf-8')は、それらをすべて別々の文字で読み取ります(予想される)ASCII内のファイルに書き込む方法はありますか?

答え:それはあなたが何を意味しているかによって異なります。 ASCIIは127以上の文字を表すことはできません。したがって、 "次の数文字は特別な意味を持つ"と言う方法が必要です。これが "\ x"のシーケンスの動作です。それは言う:次の2文字は1文字のコードです。 "\ u"はUnicodeを最大0xFFFF(65535)までエンコードするために4文字を使用して同じことを行います。

そのため、ASCIIに直接Unicodeを記述することはできません(ASCIIには単に同じ文字が含まれていないためです)。あなたはそれを文字列エスケープとして書くことができます(f2のように)。この場合、ファイルはASCIIとして表すことができます。あるいは、UTF-8として書くこともできます。その場合は、8ビットの安全なストリームが必要です。

decode('string-escape')を使用した解決策は機能しますが、使用しているメモリの量に注意する必要があります。codecs.open()の使用量の3倍。

ファイルは8ビットの単なるバイト列であることを忘れないでください。ビットもバイトも意味がありません。 「65は 'A'を意味します」と言うのはあなたです。 \xc3\xa1は "à"になるはずですが、コンピュータには知る手段がないので、ファイルを書くときに使われたエンコーディングを指定することによってそれを伝えなければなりません。

6
Aaron Digulla

さて、あなたの大好きなテキストエディタは\xc3\xa1が文字リテラルであるべきであることに気づいていません、しかしそれはそれらをテキストとして解釈します。それが、最後の行に二重のバックスラッシュを入れる理由です - あなたのファイルでは、それは本当のバックスラッシュ+ xc3などです。

Pythonでエンコードされたファイルを読み書きしたい場合は、 codecs モジュールを使用するのが最善です。

端末とアプリケーションの間でテキストを貼り付けるのは困難です。なぜなら、どのプログラムがどのエンコーディングを使ってテキストを解釈するのかわからないからです。あなたは以下を試すことができます:

>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán

次に、この文字列をエディタに貼り付けて、Latin-1を使用して格納していることを確認してください。クリップボードが文字列を文字化けしないという仮定の下では、ラウンドトリップはうまくいくはずです。

5
Torsten Marek

\ x ..シーケンスはPython特有のものです。ユニバーサルバイトエスケープシーケンスではありません。

あなたが実際にどのようにUTF-8でエンコードされた非ASCIIで入力するかはあなたのOSやあなたのエディタによります。 これがWindowsでのやり方です 。 OS Xで鋭いアクセントを付けてaを入力するには、ただヒットするだけです。 option + Eそれから AそしてOS XのほとんどすべてのテキストエディタはUTF-8をサポートしています。

4
ʞɔıu

partial関数を使用して、元のopen()関数を置き換えてUnicodeファイルを処理できるようにすることもできます。このソリューションの利点は、古いコードを変更する必要がないということです。透明です。

import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')
3
hipertracker

私はPython 2.7.9を使って iCal を解析しようとしていました。

icalendarからのインポートカレンダー

しかし、私は得ていました:

 Traceback (most recent call last):
 File "ical.py", line 92, in parse
    print "{}".format(e[attr])
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128)

そしてそれだけで修正されました:

print "{}".format(e[attr].encode("utf-8"))

(今ではそれはlikéábössを印刷することができます。)

1
Alexx Roche