Python, Unicode và bảng điều khiển Windows


146

Khi tôi cố in một chuỗi Unicode trong bảng điều khiển Windows, tôi gặp UnicodeEncodeError: 'charmap' codec can't encode character ....lỗi. Tôi cho rằng điều này là do bảng điều khiển Windows không chấp nhận các ký tự chỉ Unicode. Cách tốt nhất xung quanh này là gì? Có cách nào để tôi có thể khiến Python tự động in ?thay vì thất bại trong tình huống này không?

Chỉnh sửa: Tôi đang sử dụng Python 2.5.


Lưu ý: @ LasseV.Karlsen trả lời với dấu kiểm là loại lỗi thời (từ năm 2008). Vui lòng sử dụng các giải pháp / câu trả lời / đề xuất dưới đây một cách cẩn thận !!

Câu trả lời @JFSebastian có liên quan hơn vào ngày hôm nay (6 tháng 1 năm 2016).


Bạn đang dùng phiên bản Python nào? Tôi đã thấy các tài liệu tham khảo rằng nó đã bị hỏng trong 2.4.3 và đã được sửa trong 2.4.4.
Stu


kiểm tra này ra.
Soorena

1
câu trả lời đơn giản nhất mà tôi tìm thấy là gõ: chcp 65001 trước khi sử dụng pyhton trong cmd
Soorena

1
Sau đó, bạn nên thay đổi câu trả lời được chấp nhận của mình ...
Mr_and_Mrs_D

Câu trả lời:


38

Lưu ý: Câu trả lời này là loại lỗi thời (từ năm 2008). Vui lòng sử dụng các giải pháp dưới đây một cách cẩn thận !!


Đây là một trang mô tả chi tiết vấn đề và giải pháp (tìm kiếm trang cho văn bản Gói sys.stdout vào một ví dụ ):

PrintFails - Python Wiki

Đây là đoạn trích từ trang đó:

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

Có một số thông tin trên trang đó, rất đáng để đọc.


7
Liên kết đã chết và ý chính của câu trả lời không được trích dẫn. -1
0xC0000022L

1
Khi tôi thử lời khuyên đưa ra về việc gói sys.stdout, nó in những thứ sai. Ví dụ, u'\u2013'trở thành ûthay vì một en-dash.
user2357112 hỗ trợ Monica

@ user2357112 Bạn sẽ phải đăng một câu hỏi mới về điều đó. Bảng điều khiển hệ thống và Unicode không nhất thiết là sự kết hợp tốt nhất, nhưng tôi không biết đủ về điều này, vì vậy nếu bạn cần một câu trả lời chắc chắn, hãy đăng câu hỏi ở đây lên SO về nó.
Lasse V. Karlsen

2
liên kết đã chết. Ví dụ mã là sai đối với bảng điều khiển Windows nơi mã hóa (OEM) như cp437khác với mã bảng mã ANSI của Windows, chẳng hạn như cp1252. Mã không sửa UnicodeEncodeError: 'charmap' codec can't encode characterlỗi và có thể dẫn đến mojibake, ví dụ, ا©được âm thầm thay thế bằng ╪º⌐.
JFS

73

Cập nhật: Python 3.6 triển khai PEP 528: Thay đổi mã hóa bảng điều khiển Windows thành UTF-8 : bảng điều khiển mặc định trên Windows hiện sẽ chấp nhận tất cả các ký tự Unicode. Bên trong, nó sử dụng các API Unicode tương tự như các win-unicode-consolegói đề cập dưới đây . print(unicode_string)chỉ nên làm việc bây giờ


Tôi nhận được một UnicodeEncodeError: 'charmap' codec can't encode character... lỗi.

Lỗi có nghĩa là các ký tự Unicode mà bạn đang cố in không thể được biểu diễn bằng chcpmã hóa ký tự bảng điều khiển ( ) hiện tại . Bảng mã thường là mã hóa 8 bit cp437, chỉ có thể biểu thị ~ 0x100 ký tự từ ~ 1M ký tự Unicode:

>>> u "\ N {EURO ĐĂNG KÝ". mã hóa ('cp437')
TracBack (cuộc gọi gần đây nhất vừa qua):
...
UnicodeEncodeError: codec 'charmap' không thể mã hóa ký tự '\ u20ac' ở vị trí 0:
bản đồ nhân vật để 

Tôi cho rằng điều này là do bảng điều khiển Windows không chấp nhận các ký tự chỉ Unicode. Cách tốt nhất xung quanh này là gì?

Bảng điều khiển Windows không chấp nhận các ký tự Unicode và thậm chí nó có thể hiển thị chúng (chỉ BMP) nếu phông chữ tương ứng được cấu hình . WriteConsoleW()API nên được sử dụng như được đề xuất trong câu trả lời của @Daira Hopwood . Nó có thể được gọi một cách minh bạch, tức là bạn không cần và không nên sửa đổi tập lệnh của mình nếu bạn sử dụng win-unicode-consolegói :

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

Xem Thỏa thuận với Python 3.4, Unicode, các ngôn ngữ khác nhau và Windows là gì?

Có cách nào để tôi có thể khiến Python tự động in ?thay vì thất bại trong tình huống này không?

Nếu nó đủ để thay thế tất cả các ký tự không thể sửa đổi ?trong trường hợp của bạn thì bạn có thể đặt PYTHONIOENCODINGenvvar :

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

Trong Python 3.6+, mã hóa được chỉ định bởi PYTHONIOENCODINGenvvar bị bỏ qua cho bộ đệm bảng điều khiển tương tác trừ khi PYTHONLEGACYWINDOWSIOENCODINGenvvar được đặt thành một chuỗi không trống.


3
"Bảng điều khiển mặc định trên Windows hiện sẽ chấp nhận tất cả các ký tự Unicode" NHƯNG bạn cần định cấu hình bảng điều khiển: nhấp chuột phải vào đầu cửa sổ (của cmd hoặc python IDLE), mặc định / phông chữ chọn "Bảng điều khiển Lucida". (Nhật Bản và Trung Quốc không làm việc cho tôi, nhưng tôi nên tồn tại mà không có nó ...)
JinSnow

2
@Guillaume: câu trả lời chứa cụm từ in đậm về bảng điều khiển Windows: "nếu phông chữ tương ứng được định cấu hình." Câu trả lời này không đề cập đến IDLE nhưng bạn không cần định cấu hình phông chữ trong đó (tôi thấy các ký tự tiếng Nhật và tiếng Trung chỉ tốt trong IDLE theo mặc định. Hãy thử print('\u4E01'), print('\u6b63')).
jfs

2
@Guillaume Bạn thậm chí có thể có được tiếng Trung nếu bạn cài đặt gói ngôn ngữ trong Windows 10. Nó đã thêm phông chữ bảng điều khiển hỗ trợ tiếng Trung.
Đánh dấu Tolonen

28

Mặc dù các câu trả lời nghe có vẻ hợp lý khác đề nghị thay đổi trang mã thành 65001, nhưng điều đó không hiệu quả . (Ngoài ra, việc thay đổi mặc định mã hóa sử dụng sys.setdefaultencodingkhông phải là một ý tưởng tốt .)

Xem câu hỏi này để biết chi tiết và mã không hoạt động.


2
win-unicode-consoleGói Python (dựa trên mã của bạn) cho phép tránh sửa đổi tập lệnh của bạn nếu nó in Unicode trực tiếp bằng py -mrun your_script.pylệnh .
jfs

12

Nếu bạn không quan tâm đến việc có được một đại diện đáng tin cậy của (các) nhân vật xấu, bạn có thể sử dụng một cái gì đó như thế này (làm việc với python> = 2.6, bao gồm 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}")

(Các) ký tự xấu trong chuỗi sẽ được chuyển đổi thành biểu diễn có thể in được bởi bảng điều khiển Windows.


.encode('utf8').decode(sys.stdout.encoding)dẫn đến mojibake, ví dụ: u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs

Đơn giản print(s.encode('utf-8'))có thể là một cách tốt hơn để tránh lỗi trình biên dịch. Thay vào đó, bạn nhận được \ xNN đầu ra cho các ký tự không thể in được, đủ cho các thông báo chẩn đoán của tôi.
MÃ-REaD

4
Điều này là rất lớn, sai lầm ngoạn mục . Mã hóa thành UTF-8 sau đó giải mã dưới dạng bộ ký tự 8 bit sẽ a) thường thất bại, không phải tất cả các bộ mã đều có ký tự cho tất cả các giá trị 256 byte và b) luôn luôn hiểu sai dữ liệu, thay vào đó tạo ra một mớ hỗn độn Mojibake .
Martijn Pieters

10

Đoạn mã dưới đây sẽ làm cho Python xuất ra giao diện điều khiển dưới dạng UTF-8 ngay cả trên Windows.

Bảng điều khiển sẽ hiển thị tốt các ký tự trên Windows 7 nhưng trên Windows XP, nó sẽ không hiển thị chúng tốt, nhưng ít nhất nó sẽ hoạt động và quan trọng nhất là bạn sẽ có đầu ra nhất quán từ tập lệnh của mình trên tất cả các nền tảng. Bạn sẽ có thể chuyển hướng đầu ra thành một tập tin.

Mã dưới đây đã được thử nghiệm với Python 2.6 trên 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"

1
Có cách nào để tránh điều này bằng cách chỉ sử dụng một giao diện điều khiển khác không?
endolith

@sorin: Tại sao bạn đầu tiên import win32consolebên ngoài tryvà sau đó bạn làm điều kiện bên trong a try? Không phải là vô nghĩa (lần đầu tiên import)
0xC0000022L

Để biết giá trị của nó, cái được cung cấp bởi David-Sarah Hopwood hoạt động (Tôi thậm chí không chạy cái này vì tôi không bận tâm cài đặt mô-đun mở rộng win32)
Jaykul

4
Đừng thay đổi mã hóa mặc định của hệ thống; sửa các giá trị Unicode của bạn thay vào đó. Thay đổi mã hóa mặc định có thể phá vỡ các thư viện dựa trên hành vi mặc định . Có một lý do bạn phải buộc một mô-đun tải lại trước khi bạn có thể làm điều này.
Martijn Pieters

7

Chỉ cần nhập mã này vào dòng lệnh trước khi thực thi kịch bản python:

chcp 65001 & set PYTHONIOENCODING=utf-8

5

Giống như câu trả lời của Giampaolo Rodolà, nhưng thậm chí còn bẩn hơn: Tôi thực sự, thực sự có ý định dành một thời gian dài (sớm) để hiểu toàn bộ chủ đề của mã hóa và cách chúng áp dụng cho bảng điều khiển Windoze,

Hiện tại tôi chỉ muốn sthg, điều đó có nghĩa là chương trình của tôi sẽ KHÔNG RÚT TIỀN, và tôi đã hiểu ... và cũng không liên quan đến việc nhập quá nhiều mô-đun kỳ lạ (đặc biệt là tôi đang sử dụng Jython, nên một nửa thời gian là Python mô-đun hóa ra không có trong thực tế là có sẵn).

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

NB "pr" ngắn hơn để gõ so với "in" (và khá ngắn để gõ so với "safeprint") ...!


Khéo léo, một cách nhanh chóng và bẩn thỉu để giải quyết vấn đề. Tôi nghĩ rằng điều này là tuyệt vời cho một giải pháp không liên tục.
JFA

3

Đối với Python 2 hãy thử:

print unicode(string, 'unicode-escape')

Đối với Python 3 hãy thử:

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

Hoặc thử win-unicode-console:

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

2

TL; DR:

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

Tôi đã tự mình chạy vào đây, làm việc trên bot trò chuyện Twitch (IRC). (Python 2.7 mới nhất)

Tôi muốn phân tích các tin nhắn trò chuyện để trả lời ...

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

nhưng cũng in chúng một cách an toàn lên bàn điều khiển ở định dạng có thể đọc được:

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

Điều này đã khắc phục vấn đề UnicodeEncodeError: 'charmap'lỗi ném bot và thay thế các ký tự unicode bằng ?.


2

Nguyên nhân của vấn đề của bạn KHÔNG phải là bảng điều khiển Win không sẵn sàng chấp nhận Unicode (vì nó làm điều này vì tôi đoán Win2k theo mặc định). Đây là mã hóa hệ thống mặc định. Hãy thử mã này và xem những gì nó mang lại cho bạn:

import sys
sys.getdefaultencoding()

nếu nó nói ascii, có nguyên nhân của bạn ;-) Bạn phải tạo một tệp có tên sitecustomize.py và đặt nó dưới đường dẫn python (Tôi đặt nó dưới /usr/lib/python2.5/site-packages, nhưng điều đó khác Giành chiến thắng - đó là c: \ python \ lib \ site-gói hoặc một cái gì đó), với các nội dung sau:

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

và có lẽ bạn cũng muốn chỉ định mã hóa trong các tệp của mình:

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

Chỉnh sửa: thông tin thêm có thể được tìm thấy trong cuốn sách Đi sâu vào Python


2
setdefaultencoding () dài hơn trong sys (kể từ v2.0 theo các tài liệu mô-đun).
Jon Lồng

Tôi không thể chứng minh điều đó ngay bây giờ, nhưng tôi biết rằng tôi đã sử dụng thủ thuật này trên phiên bản mới hơn - 2.5 trên Windows.
Bartosz Radaczyński

6
OK, sau một thời gian tôi đã phát hiện ra rằng: "Hàm này chỉ được sử dụng cho việc triển khai mô-đun trang và, khi cần, bằng cách sử dụng. Sau khi được sử dụng bởi mô-đun trang, nó sẽ bị xóa khỏi không gian tên của mô-đun sys. "
Bartosz Radaczyński

4
thực tế bạn có thể đặt giao diện điều khiển windows thành utf-8. bạn cần nói chcp 65001 và nó sẽ là unicode.
Bartosz Radaczyński

4
Để làm cho nó hoàn toàn rõ ràng: đó là một ý tưởng rất xấu để thay đổi mã hóa mặc định. Điều này giống như làm hỏng chân bị gãy của bạn và đi bộ như thể không có gì xảy ra, thay vì bác sĩ đặt xương đúng cách. Tất cả các mã xử lý văn bản Unicode phải thực hiện một cách nhất quán thay vì dựa vào mã hóa / giải mã ngầm.
Martijn Pieters

1

Loại liên quan đến câu trả lời của JF Sebastian, nhưng trực tiếp hơn.

Nếu bạn gặp vấn đề này khi in ra bàn điều khiển / thiết bị đầu cuối, thì hãy làm điều này:

>set PYTHONIOENCODING=UTF-8

3
set PYTHONIOENCODING=UTF-8có thể dẫn đến mojibake nếu bảng điều khiển sử dụng một mã hóa khác như cp437. cp65001có vấn đề về khác nhau . Để in Unicode sang bảng điều khiển Windows, nên sử dụng API Unicode ( WriteConsoleW()) như được đề xuất trong câu trả lời của tôi , nơi PYTHONIOENCODINGchỉ được sử dụng để thay thế các ký tự không thể được trình bày trong trang mã OEM hiện tại bằng ?( WriteConsoleW()hoạt động ngay cả đối với các ký tự đó). PYTHONIOENCODINGcó thể được sử dụng nếu đầu ra được chuyển hướng đến một tập tin.
jfs

1

Python 3.6 windows7: Có một số cách để khởi chạy một python bạn có thể sử dụng bảng điều khiển python (có logo python trên đó) hoặc giao diện điều khiển windows (được viết là cmd.exe trên đó).

Tôi không thể in các ký tự utf8 trong bảng điều khiển windows. In các ký tự utf-8 ném cho tôi lỗi này:

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 

Sau khi thử và không hiểu câu trả lời ở trên, tôi phát hiện ra đó chỉ là một vấn đề thiết lập. Nhấp chuột phải vào đầu cửa sổ bảng điều khiển cmd, trên tab fontđã chọn bảng điều khiển lucida.


0

James Sulak hỏi,

Có cách nào để tôi có thể làm cho Python tự động in không? thay vì thất bại trong tình huống này?

Các giải pháp khác khuyên chúng tôi cố gắng sửa đổi môi trường Windows hoặc thay thế print()chức năng của Python . Câu trả lời dưới đây đến gần hơn để đáp ứng yêu cầu của Sulak.

Trong Windows 7, Python 3.5 có thể được tạo để in Unicode mà không cần ném UnicodeEncodeErrornhư sau:

    Thay     print(text)
    thế:     print(str(text).encode('utf-8'))

Thay vì ném một ngoại lệ, Python hiện hiển thị các ký tự Unicode không thể in dưới dạng mã hex \ xNN , ví dụ:

  Halmalo n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait cộng với qu \ xe2 \ x80 \ x99un điểm noir

Thay vì

  Halmalo n'était cộng với qu'un điểm noir

Cấp, cái sau tốt hơn là ceteris paribus , nhưng nếu không thì cái trước là hoàn toàn chính xác cho các thông điệp chẩn đoán. Bởi vì nó hiển thị Unicode dưới dạng các giá trị byte theo nghĩa đen, trước đây cũng có thể hỗ trợ chẩn đoán các vấn đề mã hóa / giải mã.

Lưu ý: Cuộc str()gọi ở trên là cần thiết vì nếu không, encode()sẽ khiến Python từ chối một ký tự Unicode dưới dạng một bộ số.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.