Python, Unicode, dan konsol Windows

146

Ketika saya mencoba untuk mencetak string Unicode di konsol Windows, saya mendapatkan UnicodeEncodeError: 'charmap' codec can't encode character ....kesalahan. Saya berasumsi ini karena konsol Windows tidak menerima karakter Unicode-only. Apa cara terbaik untuk mengatasi ini? Apakah ada cara saya dapat membuat Python secara otomatis mencetak ?alih - alih gagal dalam situasi ini?

Sunting: Saya menggunakan Python 2.5.


Catatan: @ LasseV.Karlsen menjawab dengan tanda centang agak ketinggalan jaman (dari 2008). Silakan gunakan solusi / jawaban / saran di bawah ini dengan hati-hati !!

Jawaban @JFSebastian lebih relevan mulai hari ini (6 Jan 2016).

James Sulak
sumber
Kamu versi apa dari Python? Saya telah melihat referensi bahwa ini rusak di 2.4.3 dan diperbaiki di 2.4.4.
Stu
3
terkait: bugs.python.org/issue1602
jfs
lihat ini .
Soorena
1
jawaban paling sederhana yang saya temukan adalah mengetik: chcp 65001 sebelum menggunakan pyhton dalam cmd
Soorena
1
Maka Anda harus mengubah jawaban yang Anda terima ...
Mr_and_Mrs_D

Jawaban:

38

Catatan: Jawaban ini agak ketinggalan jaman (dari 2008). Silakan gunakan solusi di bawah ini dengan hati-hati !!


Berikut adalah halaman yang merinci masalah dan solusinya (cari halaman untuk teks Wrapping sys.stdout menjadi contoh ):

PrintFails - Python Wiki

Berikut kutipan kode dari halaman itu:

$ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line'
  UTF-8
  <type 'unicode'> 2
  Б
  Б

  $ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line' | cat
  None
  <type 'unicode'> 2
  Б
  Б

Ada beberapa informasi lebih lanjut di halaman itu, layak dibaca.

Lasse V. Karlsen
sumber
7
Tautan sudah mati dan inti dari jawaban tidak dikutip. -1
0xC0000022L
1
Ketika saya mencoba saran yang diberikan tentang pembungkus sys.stdout, itu mencetak hal-hal yang salah. Misalnya, u'\u2013'menjadi ûbukan en-dash.
user2357112 mendukung Monica
@ user2357112 Anda harus memposting pertanyaan baru tentang itu. Unicode dan konsol sistem belum tentu merupakan kombinasi terbaik, tetapi saya tidak cukup tahu tentang ini, jadi jika Anda membutuhkan jawaban yang pasti, posting pertanyaan di sini di SO tentang hal itu.
Lasse V. Karlsen
2
tautannya sudah mati. Contoh kode salah untuk konsol Windows di mana codepage (OEM) seperti cp437berbeda dari codepage Windows ANSI seperti cp1252. Kode tidak memperbaiki UnicodeEncodeError: 'charmap' codec can't encode characterkesalahan dan dapat menyebabkan mojibake misalnya, ا©diganti dengan diam-diam ╪º⌐.
jfs
73

Pembaruan: Python 3.6 mengimplementasikan PEP 528: Ubah pengkodean konsol Windows ke UTF-8 : konsol default pada Windows sekarang akan menerima semua karakter Unicode. Secara internal, ia menggunakan Unicode API yang sama dengan win-unicode-consolepaket yang disebutkan di bawah ini . print(unicode_string)seharusnya hanya bekerja sekarang.


Saya mendapatkan UnicodeEncodeError: 'charmap' codec can't encode character... kesalahan.

Kesalahan berarti bahwa karakter Unicode yang Anda coba cetak tidak dapat diwakili menggunakan chcppengkodean karakter konsol saat ini ( ). Codepage tersebut sering berupa penyandian 8-bit seperticp437 yang hanya bisa mewakili ~ 0x100 karakter dari ~ 1M karakter Unicode:

>>> u "\ N {TANDA EURO}". encode ('cp437')
Traceback (panggilan terakhir terakhir):
...
UnicodeEncodeError: 'charmap' codec tidak dapat menyandikan karakter '\ u20ac' di posisi 0:
peta karakter ke 

Saya berasumsi ini karena konsol Windows tidak menerima karakter Unicode-only. Apa cara terbaik untuk mengatasi ini?

Konsol Windows tidak menerima karakter Unicode dan bahkan dapat menampilkannya (hanya BMP) jika font yang sesuai dikonfigurasi . WriteConsoleW()API harus digunakan seperti yang disarankan dalam jawaban @Daira Hopwood . Itu bisa disebut transparan yaitu, Anda tidak perlu dan tidak boleh memodifikasi skrip Anda jika Anda menggunakan win-unicode-consolepaket :

T:\> py -mpip install win-unicode-console
T:\> py -mrun your_script.py

Lihat Apa masalahnya dengan Python 3.4, Unicode, berbagai bahasa dan Windows?

Apakah ada cara saya dapat membuat Python secara otomatis mencetak ?alih - alih gagal dalam situasi ini?

Jika cukup dengan mengganti semua karakter yang tidak dapat dihapus dengan ?dalam kasus Anda maka Anda dapat mengatur PYTHONIOENCODINGenvvar :

T:\> set PYTHONIOENCODING=:replace
T:\> python3 -c "print(u'[\N{EURO SIGN}]')"
[?]

Dalam Python 3.6+, pengkodean yang ditentukan oleh PYTHONIOENCODINGenvvar diabaikan untuk buffer konsol interaktif kecuali PYTHONLEGACYWINDOWSIOENCODINGenvvar diatur ke string yang tidak kosong.

jfs
sumber
3
"konsol default di Windows sekarang akan menerima semua karakter Unicode" TAPI Anda perlu mengkonfigurasi konsol: klik kanan di bagian atas jendela (dari cmd atau python IDLE), secara default / font pilih "Lucida console". (Jepang dan Cina tidak bekerja untuk saya, tetapi saya harus bertahan hidup tanpanya ...)
JinSnow
2
@Guillaume: jawabannya berisi kalimat tebal tentang konsol Windows: "jika font yang sesuai dikonfigurasi." Jawaban ini tidak menyebutkan IDLE tetapi Anda tidak perlu mengkonfigurasi font di dalamnya (saya melihat karakter Jepang dan Cina baik-baik saja di IDLE secara default. Coba print('\u4E01'), print('\u6b63')).
jfs
2
@Guillaume Anda bahkan bisa mendapatkan bahasa Mandarin jika Anda menginstal paket bahasa di Windows 10. Ia menambahkan font konsol yang mendukung bahasa Mandarin.
Mark Tolonen
28

Meskipun jawaban terdengar masuk akal lainnya yang menyarankan mengubah halaman kode ke 65001, itu tidak bekerja . (Juga, mengubah default encoding menggunakan sys.setdefaultencodingadalah bukan ide yang baik .)

Lihat pertanyaan ini untuk detail dan kode yang berfungsi.

Daira Hopwood
sumber
2
win-unicode-consolePaket python (berdasarkan kode Anda) memungkinkan untuk menghindari memodifikasi skrip Anda jika mencetak Unicode langsung menggunakan py -mrun your_script.pyperintah .
jfs
12

Jika Anda tidak tertarik untuk mendapatkan representasi karakter buruk yang dapat diandalkan, Anda dapat menggunakan sesuatu seperti ini (bekerja dengan python> = 2.6, termasuk 3.x):

from __future__ import print_function
import sys

def safeprint(s):
    try:
        print(s)
    except UnicodeEncodeError:
        if sys.version_info >= (3,):
            print(s.encode('utf8').decode(sys.stdout.encoding))
        else:
            print(s.encode('utf8'))

safeprint(u"\N{EM DASH}")

Karakter buruk dalam string akan dikonversi dalam representasi yang dapat dicetak oleh konsol Windows.

Giampaolo Rodolà
sumber
.encode('utf8').decode(sys.stdout.encoding)mengarah ke mojibake misalnya, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs
Sederhananya print(s.encode('utf-8'))mungkin cara yang lebih baik untuk menghindari kesalahan kompiler. Sebagai gantinya, Anda mendapatkan \ xNN output untuk karakter yang tidak patut, yang cukup untuk pesan diagnostik saya.
CODE-REaD
4
Ini sangat, sangat salah. Pengkodean ke UTF-8 kemudian decoding sebagai charset 8-bit akan a) sering gagal, tidak semua codepage memiliki karakter untuk semua nilai 256 byte, dan b) selalu interpretasi data yang salah, menghasilkan kekacauan Mojibake sebagai gantinya.
Martijn Pieters
10

Kode di bawah ini akan membuat output Python ke konsol sebagai UTF-8 bahkan di Windows.

Konsol akan menampilkan karakter dengan baik di Windows 7 tetapi pada Windows XP tidak akan menampilkannya dengan baik, tetapi setidaknya itu akan berfungsi dan yang paling penting Anda akan memiliki output yang konsisten dari skrip Anda di semua platform. Anda akan dapat mengarahkan output ke file.

Kode di bawah ini diuji dengan Python 2.6 pada Windows.


#!/usr/bin/python
# -*- coding: UTF-8 -*-

import codecs, sys

reload(sys)
sys.setdefaultencoding('utf-8')

print sys.getdefaultencoding()

if sys.platform == 'win32':
    try:
        import win32console 
    except:
        print "Python Win32 Extensions module is required.\n You can download it from https://sourceforge.net/projects/pywin32/ (x86 and x64 builds are available)\n"
        exit(-1)
    # win32console implementation  of SetConsoleCP does not return a value
    # CP_UTF8 = 65001
    win32console.SetConsoleCP(65001)
    if (win32console.GetConsoleCP() != 65001):
        raise Exception ("Cannot set console codepage to 65001 (UTF-8)")
    win32console.SetConsoleOutputCP(65001)
    if (win32console.GetConsoleOutputCP() != 65001):
        raise Exception ("Cannot set console output codepage to 65001 (UTF-8)")

#import sys, codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr)

print "This is an Е乂αmp١ȅ testing Unicode support using Arabic, Latin, Cyrillic, Greek, Hebrew and CJK code points.\n"
Sorin
sumber
1
Apakah ada cara untuk menghindari ini hanya dengan menggunakan konsol yang berbeda?
endolith
@sorin: Mengapa Anda pertama kali import win32consolekeluar trydan kemudian melakukannya secara kondisional di dalam a try? Bukankah itu tidak ada gunanya (yang pertama import)
0xC0000022L
Untuk apa nilainya, yang disediakan oleh David-Sarah Hopwood bekerja (saya tidak mendapatkan yang ini berjalan bahkan karena saya belum repot-repot menginstal modul ekstensi win32)
Jaykul
4
Jangan mengubah pengkodean default sistem; perbaiki nilai Unicode Anda sebagai gantinya. Mengubah pengkodean default dapat merusak perpustakaan yang bergantung pada, Anda tahu, perilaku default . Ada alasan Anda harus memaksakan pemuatan modul sebelum Anda dapat melakukan ini.
Martijn Pieters
7

Cukup masukkan kode ini di baris perintah sebelum menjalankan skrip python:

chcp 65001 & set PYTHONIOENCODING=utf-8
c97
sumber
5

Seperti jawaban Giampaolo Rodolà, tetapi bahkan lebih kotor: Saya benar-benar bermaksud menghabiskan waktu lama (segera) untuk memahami seluruh subjek pengkodean dan bagaimana mereka berlaku untuk konsol Windoze,

Untuk saat ini saya hanya ingin sthg yang berarti program saya TIDAK akan rusak, dan yang saya mengerti ... dan juga yang tidak melibatkan mengimpor terlalu banyak modul eksotis (khususnya saya menggunakan Jython, jadi separuh waktu Python Modul ternyata tidak tersedia).

def pr(s):
    try:
        print(s)
    except UnicodeEncodeError:
        for c in s:
            try:
                print( c, end='')
            except UnicodeEncodeError:
                print( '?', end='')

NB "pr" lebih pendek daripada "print" (dan sedikit lebih pendek untuk mengetik daripada "safeprint") ...!

mike rodent
sumber
Pintar, cara cepat dan kotor untuk mengatasi masalah ini. Saya pikir ini bagus untuk solusi intermiten.
JFA
3

Untuk Python 2 coba:

print unicode(string, 'unicode-escape')

Untuk Python 3 coba:

import os
string = "002 Could've Would've Should've"
os.system('echo ' + string)

Atau coba win-unicode-console:

pip install win-unicode-console
py -mrun your_script.py
shubaly
sumber
2

TL; DR:

print(yourstring.encode('ascii','replace'));

Saya mengalami ini sendiri, mengerjakan bot Twitch chat (IRC). (Python 2.7 terbaru)

Saya ingin menguraikan pesan obrolan untuk menanggapi ...

msg = s.recv(1024).decode("utf-8")

tetapi juga mencetaknya dengan aman ke konsol dalam format yang dapat dibaca manusia:

print(msg.encode('ascii','replace'));

Ini memperbaiki masalah UnicodeEncodeError: 'charmap'kesalahan melempar bot dan mengganti karakter unicode dengan ?.

Matthew Estock
sumber
2

Penyebab masalah Anda BUKAN konsol Win tidak mau menerima Unicode (seperti ini karena saya kira Win2k secara default). Ini adalah penyandian sistem default. Coba kode ini dan lihat apa yang memberi Anda:

import sys
sys.getdefaultencoding()

jika dikatakan ascii, itu penyebab Anda ;-) Anda harus membuat file bernama sitecustomize.py dan meletakkannya di bawah jalur python (saya meletakkannya di bawah /usr/lib/python2.5/site-packages, tapi itu berbeda pada Menang - ini adalah c: \ python \ lib \ situs-paket atau sesuatu), dengan konten berikut:

import sys
sys.setdefaultencoding('utf-8')

dan mungkin Anda mungkin ingin menentukan pengkodean dalam file Anda juga:

# -*- coding: UTF-8 -*-
import sys,time

Sunting: info lebih lanjut dapat ditemukan di buku Dive into Python

Bartosz Radaczyński
sumber
2
setdefaultencoding () adalah nolonger di sys (pada v2.0 sesuai dengan modul dokumen).
Jon Cage
Saya tidak dapat membuktikannya sekarang, tetapi saya tahu saya telah menggunakan trik ini pada versi yang lebih baru - 2.5 di Windows.
Bartosz Radaczyński
6
OK, setelah beberapa saat saya mengetahui bahwa: "Fungsi ini hanya dimaksudkan untuk digunakan oleh implementasi modul situs dan, jika perlu, oleh sitecustomize. Setelah digunakan oleh modul situs, ia dihapus dari namespace modul sys. "
Bartosz Radaczyński
4
sebenarnya Anda dapat mengatur konsol windows menjadi utf-8. Anda perlu mengatakan chcp 65001 dan itu akan menjadi unicode.
Bartosz Radaczyński
4
Untuk membuatnya benar-benar jelas: itu adalah ide yang sangat buruk untuk mengubah pengkodean default. Ini mirip dengan spalking kaki Anda yang patah dan berjalan terus seolah-olah tidak terjadi apa-apa, daripada meminta dokter mengatur tulang dengan benar. Semua kode yang menangani teks Unicode harus melakukannya secara konsisten alih-alih mengandalkan penyandian / penguraian secara implisit.
Martijn Pieters
1

Agak terkait dengan jawaban oleh JF Sebastian, tetapi lebih langsung.

Jika Anda mengalami masalah ini saat mencetak ke konsol / terminal, maka lakukan ini:

>set PYTHONIOENCODING=UTF-8
Kinjal Dixit
sumber
3
set PYTHONIOENCODING=UTF-8dapat menyebabkan mojibake jika konsol menggunakan penyandian yang berbeda seperti cp437. cp65001memiliki berbagai masalah . Untuk mencetak Unicode ke konsol Windows, Unicode API harus digunakan ( WriteConsoleW()) seperti yang disarankan dalam jawaban saya di mana PYTHONIOENCODINGhanya digunakan untuk mengganti karakter yang tidak dapat diwakili dalam halaman kode OEM saat ini dengan ?( WriteConsoleW()berfungsi bahkan untuk karakter tersebut). PYTHONIOENCODINGdapat digunakan jika output diarahkan ke file.
jfs
1

Python 3.6 windows7: Ada beberapa cara untuk meluncurkan python Anda bisa menggunakan konsol python (yang memiliki logo python di atasnya) atau konsol windows (ada tertulis cmd.exe di atasnya).

Saya tidak dapat mencetak karakter utf8 di konsol windows. Mencetak utf-8 karakter membuat saya kesalahan ini:

OSError: [winError 87] The paraneter is incorrect 
Exception ignored in: (_io-TextIOwrapper name='(stdout)' mode='w' ' encoding='utf8') 
OSError: [WinError 87] The parameter is incorrect 

Setelah mencoba dan gagal untuk memahami jawaban di atas saya menemukan itu hanya masalah pengaturan. Klik kanan di bagian atas jendela konsol cmd, pada tab fontpilih konsol lucida.

J. Apakah
sumber
0

James Sulak bertanya,

Apakah ada cara saya dapat membuat Python mencetak secara otomatis? bukannya gagal dalam situasi ini?

Solusi lain menyarankan agar kami mencoba mengubah lingkungan Windows atau mengganti print()fungsi Python . Jawaban di bawah ini semakin mendekati untuk memenuhi permintaan Sulak.

Di bawah Windows 7, Python 3.5 dapat dibuat untuk mencetak Unicode tanpa melempar UnicodeEncodeErrorsebagai berikut:

    Di tempat:     print(text)
    pengganti:     print(str(text).encode('utf-8'))

Alih-alih melempar pengecualian, Python sekarang menampilkan karakter Unicode yang tidak dapat dicetak sebagai \ xNN kode hex, misalnya:

  Halmalo n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait plus qu \ xe2 \ x80 \ x99un point noir

Dari pada

  Halmalo n'était plus qu'un point noir

Memang, yang terakhir lebih disukai ceteris paribus , tetapi sebaliknya yang pertama sepenuhnya akurat untuk pesan diagnostik. Karena menampilkan Unicode sebagai nilai byte literal, yang pertama juga dapat membantu dalam mendiagnosis masalah penyandian / penyandian sandi.

Catatan: The str()panggilan di atas diperlukan karena jika tidak encode()menyebabkan Python untuk menolak karakter Unicode sebagai tupel nomor.

KODE-REaD
sumber