PythonでのUnicode(UTF-8)ファイルの読み取りと書き込み


330

ファイルへのテキストの読み取りと書き込み(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'

回答:


110

表記で

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の内容を、エディターで保存したファイルの内容と比較します。


それでは、Pythonがそれを使用してファイルを読み取ることができる場合、utf-8エンコード形式のポイントは何ですか?言い換えれば、Pythonが\ xc3を1バイトとして読み取るASCII表現はありますか?
グレッグ・リンド

4
あなたの「だから、何がポイントなのか」という質問への答えは「ムー」です。(PythonはUTF-8でエンコードされたファイルを読み取ることができるため)。2番目の質問:\ xc3はASCIIセットの一部ではありません。おそらく、代わりに「8ビットエンコーディング」を意味します。Unicodeとエンコーディングについて混乱しています。大丈夫です、多くは大丈夫です。
tzot 2009年

8
これを入門書として読んでみてください:joelonsoftware.com/articles/Unicode.html
tzot

注:u'\xe1'は、文字エンコーディングに応じU+00e1て1 バイト以上を使用して表すことができる1つのUnicodeコードポイントです(utf-8では2バイトです)。b'\xe1'1バイト(番号225)は、それが表現できる任意の文字を、例えば、それを復号化するために使用されるエンコーディングに依存している場合どのような手紙、それは、あるбU+0431、CP1251でсU+0441など、CP866に
JFSは、

11
多くの英国のコーダーが「ASCIIを使用するだけ」と言ってから、£記号がそれではないことに気付かないのは驚くべきことです。ほとんどの人は、ascii!= localコードページ(つまり、latin1)であることを認識していません。
Danny Staple 2013

712

エンコードとデコードの方法をいじるよりも、ファイルを開くときにエンコードを指定する方が簡単だと思います。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 2ではなく、Python 3のエンコーディング引数のみをサポートします。

編集:以前、この回答はコーデックモジュールを推奨していました。コーデックモジュールは、混合時に問題を引き起こす可能性があります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を参照してください


74
解決するのでopen(file,'w')はなく、ファイルの書き込みにも完全に対応codecs.open(file,'w','utf-8')
Matt Connolly

1
これは私が探していた答えです:)
Justin

6
ないcodecs.open(...)方法はまた、完全に準拠しwith open(...):たスタイル、withすべての後にファイルを閉じる気が行われていますか?とにかく動作するようです。
try-catch-finally

2
@ try-catch-finallyはい。私はwith codecs.open(...) as f:いつも使っています。
Tim Swast 2013

6
これを100回賛成できればいいのに。多数の混合データによって引き起こされるエンコードの問題に数日間苦しみ、エンコードについて目を凝らして読んだ後、この答えは砂漠の水のようです。もっと早く見たかったのに。
マイクジラール2013

45

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

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

Python3は、open関数にエンコーディングパラメータを追加しました。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でサポートされている任意のテキストエンコーディングを使用できます。サポートされているエンコーディングのリストについては、コーデックモジュールを参照してください。

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


提供するソリューションについてもう少し説明を追加して、回答を詳しく説明していただけませんか?
abarisone 2016

2
これはコーデックモジュールを使ってPython 2で利用可能で見える- codecs.open('somefile', encoding='utf-8') stackoverflow.com/a/147756/149428
テイラーEdmiston

18

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

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

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

これにより、私が想像していたような往復が可能になります。


1
良い応答、私は両方のソリューション(codecs.open(file,"r","utf-8")をテストしました、そして単純にopen(file,"r").read().decode("utf-8")、そして両方とも完全に機能しました。
イーグル

「TypeError:期待されるstr、bytes、またはos.PathLikeオブジェクトであり、_io.TextIOWrapperではありません」という理由が何かわかりますか?
JinSnow 2017年

賛成票の数を考えると、2番目の回答を受け入れるのは素晴らしいアイデアだと思います:)
Jacquot

14
# -*- 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

実際、これはPython 3.2でUTF-8エンコーディングのファイルを読み取るために私にとってはうまくいきました:

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

6

Unicode文字列を読み取ってHTMLに送信するには、次のようにしました。

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

Pythonで動くhttpサーバーに役立ちます。


6

エンコーディングに関する一般的な問題に遭遇しました。ファイルがどのエンコーディングであるかをどのように確認できますか?

回答:ファイル形式がこれを提供しない限り、それはできません。たとえば、XMLは次のように始まります。

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

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

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

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

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

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

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

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

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

グレッグ・リンドが尋ねました:ここに欠けている部分があると思います:ファイル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」は、4文字を使用して同じことを行い、Unicodeを最大0xFFFF(65535)までエンコードします。

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

を使用decode('string-escape')したソリューションは機能しますが、使用するメモリ量に注意する必要がありますcodecs.open()。使用量の3倍。

ファイルは、8ビットのバイトのシーケンスにすぎないことに注意してください。ビットもバイトも意味がありません。「65は「A」を意味する」と言うのはあなたです。以来\xc3\xa1、「A」になる必要がありますが、コンピュータが知る手段がない、あなたは、ファイルを書き込むときに使用されたエンコーディングを指定することで、それを伝える必要があります。


ここに欠けている部分があると思います:ファイルf2には以下が含まれます:hex:0000000:4361 7069 745c 7863 335c 7861 316e 0a Capit \ xc3 \ xa1n。たとえば、codecs.open( 'f2'、 'rb'、 'utf-8')は、それらをすべて別の文字で読み取ります(予想)。ASCIIでファイルに書き込む方法はありますか?
グレッグ・リンド

6

を除いて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


はい、ioを使用する方が良いです。しかし、私はwith文を次のように記述しwith io.open('data.txt', 'w', 'utf-8') as file:てエラーが発生しましたTypeError: an integer is required。に変更した後with io.open('data.txt', 'w', encoding='utf-8') as file:、うまくいきました。
Evan Hu

5

まあ、あなたのお気に入りのテキストエディターは\xc3\xa1、文字リテラルであることに気づいていませんが、テキストとして解釈します。そのため、最後の行に二重のバックスラッシュが表示されxc3ます。これは、ファイルでは実際のバックスラッシュ+ などになります。

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

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

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

次に、この文字列をエディターに貼り付け、Latin-1を使用して保存することを確認します。クリップボードが文字列を文字化けしないという前提の下で、往復は機能するはずです。


4

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

実際にUTF-8でエンコードされた非ASCIIで入力する方法は、OSやエディターによって異なります。これがWindowsでの方法です。OS Xを入力するために急性アクセントとあなただけヒットすることができます+ 、その後、OS XのサポートUTF-8で、ほとんどすべてのテキストエディタ。optionEA


3

open()関数を使用して、元の関数を適切な場所に置き換えることにより、Unicodeファイルで動作するように改善することもできpartialます。このソリューションの優れた点は、古いコードを変更する必要がないことです。透明です。

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

1

私は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を印刷できます。)


0

スクリプト全体のデフォルトのエンコーディングを「UTF-8」に変更することで、最も簡単なアプローチを見つけました。

import sys
reload(sys)
sys.setdefaultencoding('utf8')

任意のopenprintまたは他のステートメントは、単に使用しますutf8

少なくとも動作しPython 2.7.9ます。

Thxはhttps://markhneedham.com/blog/2015/05/21/python-unicodeencodeerror-ascii-codec-cant-encode-character-uxfc-in-position-11-ordinal-not-in-range128/(最後を見てください)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.