as3:~/ngokevin-site# nano content/blog/20140114_test-chinese.mkd
as3:~/ngokevin-site# wok
Traceback (most recent call last):
File "/usr/local/bin/wok", line 4, in
Engine()
File "/usr/local/lib/python2.7/site-packages/wok/engine.py", line 104, in init
self.load_pages()
File "/usr/local/lib/python2.7/site-packages/wok/engine.py", line 238, in load_pages
p = Page.from_file(os.path.join(root, f), self.options, self, renderer)
File "/usr/local/lib/python2.7/site-packages/wok/page.py", line 111, in from_file
page.meta['content'] = page.renderer.render(page.original)
File "/usr/local/lib/python2.7/site-packages/wok/renderers.py", line 46, in render
return markdown(plain, Markdown.plugins)
File "/usr/local/lib/python2.7/site-packages/markdown/init.py", line 419, in markdown
return md.convert(text)
File "/usr/local/lib/python2.7/site-packages/markdown/init.py", line 281, in convert
source = unicode(source)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position 1: ordinal not in range(128). -- Note: Markdown only accepts unicode input!
どうすれば修正できますか?
他の Python ベースの静的ブログ アプリでは、中国語の投稿を正常に公開できます。たとえば、次のアプリです。http://github.com/vrypan/bucket3私のサイトではhttp://bc3.brite.biz/、中国語の投稿は正常に公開できます。
ベストアンサー1
tl;dr / クイックフィックス
- むやみにデコード/エンコードしない
- 文字列がUTF-8でエンコードされていると想定しないでください
- コード内でできるだけ早く文字列をUnicode文字列に変換するようにしてください。
- ロケールを修正します:Python 3.6 で UnicodeDecodeError を解決するにはどうすればよいですか?
reload
簡単なハックを試してみよう
Python 2.x の Unicode Zen - ロングバージョン
原因を見なければ根本的な原因を知るのは難しいので、一般的な話をしなければなりません。
UnicodeDecodeError: 'ascii' codec can't decode byte
str
これは通常、元の文字列のエンコーディングを指定せずに、非 ASCII を含むPython 2.x を Unicode 文字列に変換しようとした場合に発生します。
簡単に言うと、Unicode 文字列は、エンコードを含まない完全に別の種類の Python 文字列です。Unicodeポイント コードのみを保持するため、スペクトル全体の任意の Unicode ポイントを保持できます。文字列には、UTF-8、UTF-16、ISO-8895-1、GBK、Big5 などのエンコードされたテキストが含まれます。文字列は Unicode にデコードされ、Unicode は文字列にエンコードされます。ファイルとテキスト データは常にエンコードされた文字列で転送されます。
Markdown モジュールの作成者は、おそらくunicode()
(例外がスローされる場所) を残りのコードの品質ゲートとして使用します。これは、ASCII を変換するか、既存の Unicode 文字列を新しい Unicode 文字列に再ラップします。Markdown の作成者は、受信文字列のエンコードを認識できないため、Markdown に渡す前に文字列を Unicode 文字列にデコードすることをユーザーに頼ることになります。
Unicode文字列は、文字列のプレフィックスを使用してコード内で宣言できますu
。例:
>>> my_u = u'my ünicôdé strįng'
>>> type(my_u)
<type 'unicode'>
Unicode 文字列は、ファイル、データベース、ネットワーク モジュールから取得される場合もあります。この場合、エンコードについて心配する必要はありません。
落とし穴
str
を明示的に呼び出さなくても、から Unicode への変換が行われることがありますunicode()
。
次のシナリオではUnicodeDecodeError
例外が発生します。
# Explicit conversion without encoding
unicode('€')
# New style format string into Unicode string
# Python will try to convert value string to Unicode first
u"The currency is: {}".format('€')
# Old style format string into Unicode string
# Python will try to convert value string to Unicode first
u'The currency is: %s' % '€'
# Append string to Unicode
# Python will try to convert string to Unicode first
u'The currency is: ' + '€'
例
café
次の図では、端末の種類に応じて単語が「UTF-8」または「Cp1252」エンコードでどのようにエンコードされているかがわかります。どちらの例でも、caf
は通常の ASCII です。UTF-8 では、 はé
2 バイトを使用してエンコードされます。「Cp1252」では、é は 0xE9 です (これは偶然にも Unicode ポイント値でもあります (偶然ではありません))。正しいコードdecode()
が呼び出され、Python Unicode への変換が成功します。
この図では、decode()
が で呼び出されますascii
(これは、エンコードを指定せずに を呼び出すのと同じですunicode()
)。ASCII には より大きいバイトを含めることができないため0x7F
、例外がスローされますUnicodeDecodeError
。
ユニコードサンドイッチ
コード内に Unicode サンドイッチを形成するのは良い習慣です。つまり、すべての受信データを Unicode 文字列にデコードし、Unicode で作業し、最後にstr
s にエンコードして出力します。これにより、コード内の文字列のエンコードについて心配する必要がなくなります。
入力/デコード
ソースコード
ソースコードに非ASCII文字を組み込む必要がある場合は、文字列の前にu
. を付けてUnicode文字列を作成してください。例:
u'Zürich'
Python がソース コードをデコードできるようにするには、ファイルの実際のエンコーディングと一致するエンコーディング ヘッダーを追加する必要があります。たとえば、ファイルが 'UTF-8' としてエンコードされている場合は、次のようにします。
# encoding: utf-8
これは、ソース コードに非 ASCII が含まれている場合にのみ必要です。
ファイル
通常、ファイルから非 ASCII データが受信されます。io
モジュールは、指定されたを使用してファイルをオンザフライでデコードする TextWrapper を提供しますencoding
。ファイルには正しいエンコーディングを使用する必要があります。簡単に推測することはできません。たとえば、UTF-8 ファイルの場合:
import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
my_unicode_string = my_file.read()
my_unicode_string
は Markdown に渡すのに適しています。行UnicodeDecodeError
に がある場合はread()
、おそらく間違ったエンコーディング値を使用しています。
CSV ファイル
Python 2.7のCSVモジュールは非ASCII文字をサポートしていません。ただし、バックポート。
上記のように使用しますが、開いたファイルを渡します。
from backports import csv
import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
for row in csv.reader(my_file):
yield row
データベース
ほとんどの Python データベース ドライバーは Unicode でデータを返すことができますが、通常は少し設定が必要です。SQL クエリには常に Unicode 文字列を使用してください。
マイグレーション接続文字列に以下を追加します:
charset='utf8',
use_unicode=True
例えば
>>> db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
PostgreSQL
追加:
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
ウェブ
ウェブページは、ほぼあらゆるエンコードでエンコードできます。Content-type
ヘッダーにはエンコードを示すフィールドを含める必要がありますcharset
。その後、この値に基づいてコンテンツを手動でデコードできます。または、Pythonリクエストの Unicode を返しますresponse.text
。
手動で
文字列を手動でデコードする必要がある場合は、 を実行するだけです。my_string.decode(encoding)
ここencoding
で は適切なエンコーディングです。Python 2.x でサポートされているコーデックは次のとおりです。標準エンコーディングもう一度言いますが、 が表示される場合は、UnicodeDecodeError
おそらくエンコードが間違っています。
サンドイッチの肉
通常の文字列と同じように Unicode を操作します。
出力
標準出力 / 印刷
print
stdout ストリームを通じて書き込みます。Python は、Unicode がコンソールのエンコーディングにエンコードされるように、stdout にエンコーダーを設定しようとします。たとえば、Linux シェルの が の場合locale
、en_GB.UTF-8
出力は にエンコードされますUTF-8
。Windows では、8 ビット コード ページに制限されます。
ロケールの破損など、コンソールの設定が間違っていると、予期しない印刷エラーが発生する可能性があります。PYTHONIOENCODING
環境変数によって、stdout のエンコードを強制できます。
ファイル
入力と同様に、io.open
Unicode をエンコードされたバイト文字列に透過的に変換するために使用できます。
データベース
読み取り用の同じ構成により、Unicode を直接書き込むことができます。
Python3 について
Python 3 は Python 2.x よりも Unicode に対応しているわけではありませんが、このトピックに関する混乱は少し少なくなっています。たとえば、regular はstr
現在 Unicode 文字列であり、oldstr
は現在 ですbytes
。
デフォルトのエンコーディングは UTF-8 なので、.decode()
エンコーディングを指定せずにバイト文字列を入力すると、Python 3 は UTF-8 エンコーディングを使用します。これにより、おそらく Unicode に関する問題の 50% が解決されます。
さらに、open()
デフォルトではテキスト モードで動作するため、デコードされた値str
(Unicode 値) が返されます。エンコードはロケールから取得されます。通常、Un*x システムでは UTF-8、Windows ボックスでは windows-1251 などの 8 ビット コード ページになります。
使用すべきでない理由sys.setdefaultencoding('utf8')
これは厄介なハックです( を使用しなければならない理由がありますreload
)。問題を隠蔽し、Python 3.xへの移行を妨げるだけです。問題を理解し、根本的な原因を修正して、Unicode zenを楽しんでください。py スクリプトで sys.setdefaultencoding("utf-8") を使用しないのはなぜですか?詳細は