Python, Unicode ve Windows konsolu


146

Windows konsolunda bir Unicode dizesi yazdırmaya çalıştığımda UnicodeEncodeError: 'charmap' codec can't encode character ....hata alıyorum. Bunun Windows konsolu yalnızca Unicode karakterleri kabul etmediği için varsayıyorum. Bunun en iyi yolu nedir? Python'un ?bu durumda başarısız olmak yerine otomatik olarak yazdırmasını sağlamanın herhangi bir yolu var mı ?

Düzenleme: Python 2.5 kullanıyorum.


Not: @ LasseV.Karlsen onay işareti ile cevap bir çeşit eski (2008'den itibaren). Lütfen aşağıdaki çözümleri / cevapları / önerileri dikkatli kullanın !!

@JFSebastian yanıtı bugün itibariyle daha ilgili (6 Ocak 2016).


Hangi Python sürümünü kullanıyorsunuz? Bunun 2.4.3'te kırıldığı ve 2.4.4'te düzeltildiğine dair referanslar gördüm.
Stu


kontrol bu out.
Soorena

1
bulduğum en basit cevap yazmak: chcp 65001 cmd'de pyhton kullanmadan önce
Soorena

1
O zaman kabul edilen cevabınızı değiştirmelisiniz ...
Mr_and_Mrs_D

Yanıtlar:


38

Not: Bu cevap biraz eski (2008'den itibaren). Lütfen aşağıdaki çözümü dikkatli kullanın!


İşte sorunu ve bir çözümü ayrıntılandıran bir sayfa ( bir örnekte sys.stdout kaydırma metnini arayın ):

PrintFails - Python Wiki

İşte o sayfadan bir kod alıntısı:

$ 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
  Б
  Б

Bu sayfada daha fazla bilgi var, okumaya değer.


7
Bağlantı öldü ve cevabın özü alıntılanmadı. -1
0xC0000022L

1
Sarma hakkında verilen tavsiyeleri denediğimde sys.stdoutyanlış şeyler yazdırıyor. Örneğin, en-dash yerine u'\u2013'olur û.
user2357112

@ user2357112 Bununla ilgili yeni bir soru göndermeniz gerekecek. Unicode ve sistem konsolu mutlaka en iyi kombinasyon değildir, ancak bu konuda yeterince bilgim yok, bu yüzden kesin bir cevaba ihtiyacınız varsa, bu konuda SO'ya bir soru gönderin.
Lasse V. Karlsen

2
bağlantı öldü. Kod örneği, örneğin kod sayfasının (OEM) gibi cp437Windows ANSI kod sayfasından farklı olduğu Windows konsolu için yanlıştır cp1252. Kod UnicodeEncodeError: 'charmap' codec can't encode characterhatayı düzeltmez ve örneğin ا©sessizce değiştirilen mojibake'e neden olabilir ╪º⌐.
jfs

73

Güncelleştirme: Python 3.6 PEP 528 uygular : Windows konsol kodlamasını UTF-8 olarak değiştirin : Windows'daki varsayılan konsol artık tüm Unicode karakterlerini kabul edecektir. Dahili olarak , win-unicode-consoleaşağıda belirtilen paketle aynı Unicode API'sını kullanır . print(unicode_string)şimdi çalışmalı.


Bir UnicodeEncodeError: 'charmap' codec can't encode character... hata alıyorum.

Hata, yazdırmaya çalıştığınız Unicode karakterlerin geçerli ( chcp) konsol karakter kodlaması kullanılarak temsil edilemeyeceği anlamına gelir . cp437Kod sayfası genellikle ~ 1M Unicode karakterlerden yalnızca ~ 0x100 karakterleri temsil edebilen 8 bit kodlamadır :

>>> u "\ N {EURO SIGN}". kodlama ('cp437')
Geri izleme (en son son arama):
...
UnicodeEncodeError: 'charmap' codec bileşeni '\ u20ac' karakterini 0 konumunda kodlayamıyor:
karakter eşleşir 

Bunun Windows konsolu yalnızca Unicode karakterleri kabul etmediği için varsayıyorum. Bunun en iyi yolu nedir?

Windows konsolu Unicode karakterleri kabul eder ve karşılık gelen yazı tipi yapılandırılmışsa bunları (yalnızca BMP) görüntüleyebilir . @Daira Hopwood'un cevabındaWriteConsoleW() önerildiği gibi API kullanılmalıdır . Saydam olarak çağrılabilir, yani paket kullanıyorsanız komut dosyalarınızı değiştirmeniz gerekmez ve değiştirmeniz gerekmez :win-unicode-console

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

Bkz. Python 3.4, Unicode, farklı diller ve Windows ile anlaşma nedir?

Python'un ?bu durumda başarısız olmak yerine otomatik olarak yazdırmasını sağlamanın herhangi bir yolu var mı ?

O bütün unencodable karakterleri değiştirmek için yeterliyse ?sizin durumda o zaman ayarlayabilirsiniz PYTHONIOENCODINGenvvar :

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

Python 3.6+ PYTHONIOENCODINGsürümünde, PYTHONLEGACYWINDOWSIOENCODINGenvvar boş olmayan bir dizeye ayarlanmadığı sürece envvar tarafından belirtilen kodlama etkileşimli konsol arabellekleri için yok sayılır .


3
"Windows'daki varsayılan konsol artık tüm Unicode karakterleri kabul edecek" AMA konsolu yapılandırmanız gerekiyor: pencerelerin üst kısmına sağ tıklayın (cmd veya python IDLE), varsayılan / yazı tipinde "Lucida konsolu" nu seçin. (Japonca ve Çince benim için çalışmıyor, ama onsuz hayatta
kalmalıyım

2
@Guillaume: Yanıt, Windows konsolu hakkında kalın harflerle yazılmış : "karşılık gelen yazı tipi yapılandırılmışsa." Bu cevap IDLE'den bahsetmiyor, ancak içindeki yazı tipini yapılandırmanıza gerek yok (Japonca ve Çince karakterlerin IDLE'de varsayılan olarak iyi olduğunu görüyorum. Dene print('\u4E01'), print('\u6b63')).
jfs

2
@Guillaume Windows 10'da dil paketini yüklerseniz Çince bile alabilirsiniz. Çince'yi destekleyen konsol yazı tipleri eklendi.
Mark Tolonen


12

Kötü karakterlerin güvenilir bir şekilde temsil edilmesini istemiyorsanız, bunun gibi bir şey kullanabilirsiniz (python> = 2.6 ile çalışmak, 3.x dahil):

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}")

Dizedeki bozuk karakterler, Windows konsolu tarafından yazdırılabilir bir gösterimde dönüştürülecektir.


.encode('utf8').decode(sys.stdout.encoding)mojibake yol açar örneğin, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs

Basitçe print(s.encode('utf-8'))derleyici hatalarını önlemek için daha iyi bir yolu olabilir. Bunun yerine, tanılama mesajlarım için yeterli olan yazdırılamayan karakterler için \ xNN çıktısı alıyorsunuz.
KOD-READ

4
Bu son derece, olağanüstü yanlış. UTF-8'e kodlama ve ardından 8 bitlik bir karakter kümesi olarak kod çözme a) çoğu zaman başarısız olur, tüm kod sayfalarının 256 bayt değerinin tümü için karakterleri yoktur ve b) her zaman verilerin yanlış yorumlanması, bunun yerine Mojibake karışıklığı üretir .
Martijn Pieters

10

Aşağıdaki kod, Python'un Windows'ta bile UTF-8 olarak konsola çıkmasını sağlayacaktır.

Konsol karakterleri Windows 7'de iyi gösterecek, ancak Windows XP'de onları iyi göstermeyecek, ancak en azından işe yarayacak ve en önemlisi tüm platformlarda komut dosyanızdan tutarlı bir çıktıya sahip olacaksınız. Çıktıyı bir dosyaya yeniden yönlendirebilirsiniz.

Aşağıdaki kod Windows'ta Python 2.6 ile test edilmiştir.


#!/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"

1
Sadece farklı bir konsol kullanarak bundan kaçınmanın bir yolu var mı?
endolith

@sorin: Neden önce a import win32consoledışında, trysonra bunu koşullu olarak a içinde yapıyorsun try? Bu tür anlamsız değil mi (ilk import)
0xC0000022L

Değeri için, David-Sarah Hopwood tarafından sağlanan bir çalışır (win32 uzantıları modülünü takma zahmetine girmediğim için bunu bile çalıştırmadım)
Jaykul

4
Sistem varsayılan kodlamasını değiştirmeyin; bunun yerine Unicode değerlerinizi düzeltin. Varsayılan kodlamanın değiştirilmesi, varsayılan davranışa dayanan kitaplıkları bozabilir . Bunu yapmadan önce bir modülün yeniden yüklenmesini zorlamanız gerekir.
Martijn Pieters

7

Python betiğini çalıştırmadan önce bu kodu komut satırına girmeniz yeterlidir:

chcp 65001 & set PYTHONIOENCODING=utf-8

5

Giampaolo Rodolà'nın yanıtı gibi, ama daha da kirli: Gerçekten, kodlamaların tüm konusunu ve Windoze konsollarına nasıl uygulandıklarını anlamak için uzun bir süre (gerçekten) harcamak niyetindeyim,

Şimdilik programımın ezilmeyeceği ve anladığım ... ve ayrıca çok fazla egzotik modül (özellikle Jython kullanıyorum, yani yarısı Python kullanıyorum) anlamına gelmeyen sthg'yi istedim modül aslında mevcut değildir).

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

NB "pr" yazmak için "print" den daha kısa (ve yazmak için "safeeprint" den biraz daha kısa) ...!


Zeki, sorunu çözmek için hızlı ve kirli bir yol. Bence bu aralıklı bir çözüm için harika.
JFA

3

Python 2 için şunu deneyin:

print unicode(string, 'unicode-escape')

Python 3 için şunu deneyin:

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

Veya kazan-unicode-konsolunu deneyin:

pip install win-unicode-console
py -mrun your_script.py

2

TL; DR:

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

Bir Twitch sohbet (IRC) botu üzerinde çalıştım. (Python 2.7 son sürüm)

Yanıt vermek için sohbet mesajlarını ayrıştırmak istedim ...

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

aynı zamanda bunları insan tarafından okunabilir biçimde konsola güvenli bir şekilde yazdırın:

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

Bu, bot atma UnicodeEncodeError: 'charmap'hataları sorununu düzeltti ve unicode karakterlerin yerini aldı ?.


2

Sorunuzun nedeni, Unicode'u kabul etmek istemeyen Win konsolu DEĞİLDİR (varsayılan olarak Win2k'yi tahmin ettiğim için bunu yapar). Varsayılan sistem kodlamasıdır. Bu kodu deneyin ve size ne verdiğini görün:

import sys
sys.getdefaultencoding()

ascii diyorsa, sebebiniz var ;-) sitecustomize.py adlı bir dosya oluşturmanız ve python yolunun altına koymanız gerekir (/usr/lib/python2.5/site-packages altına koydum, ancak bu farklı Win - c: \ python \ lib \ site-paketleri ya da bir şey), aşağıdaki içeriklerle:

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

ve belki de dosyalarınızdaki kodlamayı belirtmek isteyebilirsiniz:

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

Edit: daha fazla bilgi mükemmel Python kitap içine dalış mükemmel bulunabilir


2
setdefaultencoding () sistemde daha uzun değildir (modül belgelerine göre v2.0'dan itibaren).
Jon Cage

Şu anda kanıtlayamıyorum, ancak bu hile daha sonraki bir sürümde - Windows'ta 2.5 kullandığımı biliyorum.
Bartosz Radaczyński

6
Tamam, oldukça uzun bir süre sonra şunu öğrendim: "Bu işlev yalnızca site modülü uygulaması ve gerektiğinde sitecustomize tarafından kullanılmak üzere tasarlanmıştır. Site modülü tarafından kullanıldıktan sonra, sys modülünün ad alanından kaldırılır. "
Bartosz Radaczyński

4
aslında windows konsolunu utf-8 olarak ayarlayabilirsiniz. chcp 65001 demelisiniz ve unicode olacaktır.
Bartosz Radaczyński

4
Kesinlikle netleştirmek için: varsayılan kodlamayı değiştirmek çok kötü bir fikirdir . Bu, bir doktorun kemiği düzgün bir şekilde ayarlaması yerine bacağınızı kırmaya ve hiçbir şey olmamış gibi yürümeye benzer. Tüm kod işleme Unicode metni, örtülü kodlama / kod çözme işlemine güvenmek yerine bunu tutarlı bir şekilde yapmalıdır.
Martijn Pieters

1

JF Sebastian'ın cevabı ile ilgili bir tür, ama daha doğrudan.

Konsola / terminale yazdırırken bu sorunu yaşıyorsanız, bunu yapın:

>set PYTHONIOENCODING=UTF-8

3
set PYTHONIOENCODING=UTF-8konsol cp437 gibi farklı bir kodlama kullanıyorsa mojibake'e neden olabilir . cp65001çeşitli sorunları var . , Windows konsoluna Unicode yazdırmak için, Unicode API (kullanılmalıdır WriteConsoleW()önerildiği gibi) Cevabıma neredePYTHONIOENCODING mevcut OEM kod sayfası temsil edilemeyen karakterleri değiştirmek için sadece kullanılır ?( WriteConsoleW()örneğin karakterler için bile çalışır). PYTHONIOENCODINGçıktı bir dosyaya yeniden yönlendirilirse kullanılabilir.
jfs

1

Python 3.6 pencereleri7: Python konsolunu (üzerinde bir python logosu olan) veya windows konsolunu (cmd.exe üzerinde yazılmış) kullanabileceğiniz bir python başlatmanın birkaç yolu vardır.

Windows konsolunda utf8 karakterleri yazdıramadım. Utf-8 karakterleri yazdırmak bana bu hatayı veriyor:

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 

Yukarıdaki cevabı anlamaya çalıştıktan ve başarısız olduktan sonra bunun sadece bir ayar sorunu olduğunu keşfettim. Cmd konsol pencerelerinin üst tarafına sağ tıklayın, sekmede fontlucida konsolunu seçin.


0

James Sulak,

Python'u otomatik olarak yazdırmamın bir yolu var mı? bu durumda başarısız olmak yerine?

Diğer çözümler, Windows ortamını değiştirmeyi veya Python'ları değiştirmeyi denememizi önerir. print() işlevini . Aşağıdaki cevap Sulak'ın talebini yerine getirmeye daha da yaklaşıyor.

Windows 7 altında, Python 3.5, Unicode'u UnicodeEncodeError aşağıdaki gibi :

    Yerine:    print(text)
    yerine:     print(str(text).encode('utf-8'))

Bir istisna atmak yerine, Python yazdırılamaz Unicode karakterlerini \ xNN onaltılı kodları olarak görüntüler, ör:

  Halmalo n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait artı qu \ xe2 \ x80 \ x99un noktası noir

Onun yerine

  Halmalo n'était plus qu'un point noir

Verilen, ikincisi tercih edilebilir ceteris paribus , ancak aksi takdirde eski, teşhis mesajları için tamamen doğrudur. Unicode'u gerçek bayt değerleri olarak görüntülediğinden, önceki kodlama / kod çözme sorunlarını tanılamaya da yardımcı olabilir.

Not: Yukarıdaki str()çağrı gereklidir, çünkü aksi halde encode()Python bir Unicode karakterini bir sayı dizisi olarak reddetmesine neden olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.