Thay đổi mã hóa mặc định của Python?


143

Tôi có nhiều vấn đề "không thể mã hóa" và "không thể giải mã" với Python khi tôi chạy các ứng dụng của mình từ bảng điều khiển. Nhưng trong IDE PyDev của Eclipse , mã hóa ký tự mặc định được đặt thành UTF-8 và tôi vẫn ổn.

Tôi đã tìm kiếm xung quanh để thiết lập mã hóa mặc định và mọi người nói rằng Python xóa sys.setdefaultencodingchức năng khi khởi động và chúng tôi không thể sử dụng nó.

Vậy đâu là giải pháp tốt nhất cho nó?


1
Xem bài đăng trên blog The Illusive setdefaultencoding .
djc

3
The best solution is to learn to use encode and decode correctly instead of using hacks.Điều này chắc chắn là có thể với python2 với chi phí luôn luôn nhớ phải làm như vậy / nhất quán sử dụng giao diện của riêng bạn. Kinh nghiệm của tôi cho thấy rằng điều này trở nên rất có vấn đề khi bạn đang viết mã mà bạn muốn làm việc với cả python2 và python3.
Att Righ

Câu trả lời:


159

Đây là một phương pháp đơn giản hơn (hack) cung cấp cho bạn setdefaultencoding()chức năng đã bị xóa khỏi sys:

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

(Lưu ý cho Python 3,4+: reload()có trong importlibthư viện.)

Đây không phải là một điều an toàn để làm , mặc dù: đây rõ ràng là một vụ hack, vì sys.setdefaultencoding()đã bị xóa hoàn toàn từ syskhi Python bắt đầu. Việc kích hoạt lại nó và thay đổi mã hóa mặc định có thể phá vỡ mã dựa trên ASCII là mặc định (mã này có thể là bên thứ ba, thường sẽ khiến việc sửa nó trở nên bất khả thi hoặc nguy hiểm).


5
Tôi đánh giá thấp, bởi vì câu trả lời đó không giúp ích gì cho việc chạy các ứng dụng hiện có (đó là một cách để giải thích câu hỏi), là sai khi bạn viết / bảo trì một ứng dụng và nguy hiểm khi viết thư viện. Cách đúng là đặt LC_CTYPE(hoặc trong ứng dụng, kiểm tra xem nó có được đặt đúng không và hủy bỏ với thông báo lỗi có ý nghĩa).
ibotty

@ibotty Tôi đồng ý rằng câu trả lời này là hack và thật nguy hiểm khi sử dụng nó. Nó trả lời câu hỏi, mặc dù ("Thay đổi mã hóa mặc định của Python?"). Bạn có tham khảo về ảnh hưởng của biến môi trường LC_CTYPE đối với trình thông dịch Python không?
Eric O Lebigot

tốt, nó đã không đề cập đến, đó là một hack lúc đầu. ngoài ra, những câu trả lời nguy hiểm mà không có bất kỳ đề cập nào về chúng là không hữu ích.
ibotty

1
@EOL bạn nói đúng. Nó không ảnh hưởng đến mã ưu tiên mặc dù (trong python 2 và 3):LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
ibotty

1
@ user2394901 Việc sử dụng sys.setdefaultencoding () luôn được khuyến khích !! Và mã hóa của py3k được kết nối cứng thành "utf-8" và việc thay đổi nó sẽ gây ra lỗi.
Marlon Abeykoon

70

Nếu bạn gặp lỗi này khi bạn cố gắng chuyển / chuyển hướng đầu ra của tập lệnh của mình

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

Chỉ cần xuất PYTHONIOENCODING trong bảng điều khiển và sau đó chạy mã của bạn.

export PYTHONIOENCODING=utf8


3
Đây là giải pháp duy nhất tạo ra bất kỳ sự khác biệt cho tôi. - Tôi đang sử dụng Debian 7, với các cài đặt ngôn ngữ bị hỏng. Cảm ơn.
Pryo

4
Đặt LC_CTYPEthành một cái gì đó hợp lý thay thế. Nó làm cho tất cả các chương trình khác là hạnh phúc.
ibotty

5
Một lỗi lớn hơn trong Python3 là, đó PYTHONIOENCODING=utf8không phải là mặc định. Điều này làm cho các kịch bản bị hỏng chỉ vìLC_ALL=C
Tino

Set LC_CTYPE to something sensible insteadĐây là một gợi ý hợp lý. Điều này không hoạt động tốt khi bạn đang cố gắng phân phối mã chỉ hoạt động trên hệ thống của người khác.
Att Righ

Các hệ điều hành Debian và Redhat sử dụng một C.utf8miền địa phương để cung cấp dòng C. glibc hợp lý hơn đang hoạt động để thêm nó, vì vậy có lẽ chúng ta không nên đổ lỗi cho Python vì tôn trọng cài đặt ngôn ngữ địa phương \ Lỗi?
Arthur2e5

52

A) Để kiểm soát sys.getdefaultencoding()đầu ra:

python -c 'import sys; print(sys.getdefaultencoding())'

ascii

Sau đó

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

Bạn có thể đặt sitecustomize.py của bạn cao hơn trong PYTHONPATH.

Ngoài ra, bạn có thể muốn thử reload(sys).setdefaultencodingbởi @EOL

B) Để kiểm soát stdin.encodingstdout.encodingbạn muốn đặt PYTHONIOENCODING:

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

Sau đó

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

Cuối cùng: bạn có thể sử dụng A) hoặc B) hoặc cả hai!


(chỉ python2) riêng biệt nhưng thú vị đang mở rộng ở trên với from __future__ import unicode_literalsxem thảo luận
lukmdo

17

Bắt đầu với PyDev 3.4.1, mã hóa mặc định sẽ không bị thay đổi nữa. Xem vé này để biết chi tiết.

Đối với các phiên bản trước, một giải pháp là đảm bảo PyDev không chạy với UTF-8 làm mã hóa mặc định. Trong Eclipse, chạy cài đặt hộp thoại ("chạy cấu hình", nếu tôi nhớ chính xác); bạn có thể chọn mã hóa mặc định trên tab chung. Thay đổi nó thành US-ASCII nếu bạn muốn có những lỗi này 'sớm' (nói cách khác: trong môi trường PyDev của bạn). Cũng xem một bài viết blog ban đầu cho cách giải quyết này .


1
Cảm ơn Chris. Đặc biệt là xem xét nhận xét của Mark T ở trên, câu trả lời của bạn có vẻ phù hợp nhất với tôi. Và đối với ai đó không phải là người dùng Eclipse / PyDev, tôi sẽ không bao giờ tự mình tìm ra điều đó.
Sean

Tôi muốn thay đổi điều này trên toàn cầu (thay vì một lần cho mỗi lần chạy cấu hình), nhưng chưa tìm ra cách - đã hỏi một q: stackoverflow.com/questions/9394277/ Lỗi
Tim Diggins 22/212

13

Liên quan đến python2 (và chỉ python2), một số câu trả lời trước đây dựa vào việc sử dụng hack sau:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

Không khuyến khích sử dụng nó (kiểm tra cái này hay cái này )

Trong trường hợp của tôi, nó đi kèm với một hiệu ứng phụ: Tôi đang sử dụng sổ ghi chép ipython và một khi tôi chạy mã, hàm inprintprint không còn hoạt động. Tôi đoán sẽ có giải pháp cho nó, nhưng tôi vẫn nghĩ rằng việc sử dụng hack không phải là lựa chọn chính xác.

Sau khi thử nhiều tùy chọn, cái phù hợp với tôi là sử dụng cùng một mã trong sitecustomize.pyđó, đoạn mã đó có nghĩa là gì . Sau khi đánh giá mô-đun đó, hàm setdefaultencoding sẽ bị xóa khỏi sys.

Vì vậy, giải pháp là nối thêm /usr/lib/python2.7/sitecustomize.pymã:

import sys
sys.setdefaultencoding('UTF8')

Khi tôi sử dụng virtualenvwrapper, tập tin tôi chỉnh sửa là ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py.

Và khi tôi sử dụng với máy tính xách tay python và conda, nó là ~/anaconda2/lib/python2.7/sitecustomize.py


8

Có một bài viết blog sâu sắc về nó.

Xem https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/ .

Tôi diễn giải nội dung của nó dưới đây.

Trong python 2, không được gõ mạnh về mã hóa chuỗi, bạn có thể thực hiện các thao tác trên các chuỗi được mã hóa khác nhau và đã thành công. Ví dụ như sau sẽ trở lại True.

u'Toshio' == 'Toshio'

Điều đó sẽ giữ cho mọi chuỗi (bình thường, không được trộn) được mã hóa sys.getdefaultencoding(), mặc định ascii, nhưng không phải là chuỗi khác.

Mã hóa mặc định có nghĩa là được thay đổi toàn hệ thống site.py, nhưng không phải ở nơi nào khác. Các hack (cũng được trình bày ở đây) để đặt nó trong các mô-đun người dùng chỉ là: hack, không phải là giải pháp.

Python 3 đã thay đổi mã hóa hệ thống thành mặc định thành utf-8 (khi LC_CTYPE nhận biết unicode), nhưng vấn đề cơ bản đã được giải quyết với yêu cầu mã hóa rõ ràng các chuỗi "byte" bất cứ khi nào chúng được sử dụng với chuỗi unicode.


4

Đầu tiên: reload(sys)và thiết lập một số mã hóa mặc định ngẫu nhiên chỉ liên quan đến nhu cầu của một luồng đầu cuối đầu ra là thực tế xấu. reloadthường thay đổi mọi thứ trong các hệ thống đã được đặt đúng chỗ tùy thuộc vào môi trường - ví dụ: các luồng sys.stdin / stdout, sys.ex805thook, v.v.

Giải quyết vấn đề mã hóa trên thiết bị xuất chuẩn

Giải pháp tốt nhất mà tôi biết để giải quyết vấn đề mã hóa của print'chuỗi unicode và ngoài chuỗi ascii str(ví dụ từ chữ) trên sys.stdout là: chăm sóc một sys.stdout (đối tượng giống như tệp) có khả năng và tùy chọn khoan dung liên quan đến nhu cầu:

  • Khi sys.stdout.encodingNoneđối với một số lý do nào, hoặc không tồn tại, hoặc sai lầm sai hay "ít" so với những gì nhà ga stdout hoặc dòng thực sự có khả năng, sau đó cố gắng cung cấp một đúng .encodingthuộc tính. Cuối cùng bằng cách thay thế sys.stdout & sys.stderrbằng một đối tượng giống như tập tin dịch.

  • Khi thiết bị đầu cuối / luồng vẫn không thể mã hóa tất cả các ký tự unicode xảy ra và khi bạn không muốn phá vỡ printchỉ vì điều đó, bạn có thể đưa ra một hành vi mã hóa thay thế trong đối tượng giống như tệp dịch.

Dưới đây là một ví dụ:

#!/usr/bin/env python
# encoding: utf-8
import sys

class SmartStdout:
    def __init__(self, encoding=None, org_stdout=None):
        if org_stdout is None:
            org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
        self.org_stdout = org_stdout
        self.encoding = encoding or \
                        getattr(org_stdout, 'encoding', None) or 'utf-8'
    def write(self, s):
        self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
    def __getattr__(self, name):
        return getattr(self.org_stdout, name)

if __name__ == '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

    us = u'aouäöüфżß²'
    print us
    sys.stdout.flush()

Sử dụng các chuỗi ký tự đơn giản ngoài chuỗi ascii trong mã Python 2/2 + 3

Lý do chính đáng duy nhất để thay đổi mã hóa mặc định toàn cầu (chỉ thành UTF-8) Tôi nghĩ là liên quan đến quyết định mã nguồn ứng dụng - và không phải do vấn đề mã hóa luồng I / O: Để viết các chuỗi ký tự ngoài chuỗi ascii thành mã mà không bị ép buộc để luôn luôn sử dụng u'string'kiểu unicode thoát. Điều này có thể được thực hiện khá nhất quán (mặc dù bài báo của anonbadger nói) bằng cách chăm sóc cơ sở mã nguồn Python 2 hoặc Python 2 + 3 sử dụng các chuỗi ký tự đơn giản ascii hoặc UTF-8 - theo như các chuỗi đó có khả năng im lặng chuyển đổi unicode và di chuyển giữa các mô-đun hoặc có khả năng đi đến thiết bị xuất chuẩn. Cho rằng, thích "# encoding: utf-8"Hoặc ascii (không khai báo). Thay đổi hoặc loại bỏ các thư viện vẫn phụ thuộc rất nhiều vào các lỗi mã hóa mặc định của ascii ngoài chr # 127 (ngày nay rất hiếm).

Và làm như thế này khi bắt đầu ứng dụng (và / hoặc thông qua sitecustomize.py) ngoài SmartStdoutsơ đồ trên - mà không cần sử dụng reload(sys):

...
def set_defaultencoding_globally(encoding='utf-8'):
    assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
    import imp
    _sys_org = imp.load_dynamic('_sys_org', 'sys')
    _sys_org.setdefaultencoding(encoding)

if __name__ == '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

Bằng cách này, chuỗi ký tự và hầu hết các hoạt động (ngoại trừ lặp ký tự) hoạt động thoải mái mà không cần suy nghĩ về chuyển đổi unicode như thể chỉ có Python3. Tất nhiên, tệp I / O luôn cần được chăm sóc đặc biệt về mã hóa - như trong Python3.

Lưu ý: chuỗi đồng bằng sau đó được chuyển đổi hoàn toàn từ utf-8 sang unicode SmartStdouttrước khi được chuyển đổi sang luồng đầu ra.


4

Đây là cách tiếp cận tôi đã sử dụng để tạo mã tương thích với cả python2python3 và luôn tạo ra đầu ra utf8 . Tôi tìm thấy câu trả lời này ở nơi khác, nhưng tôi không thể nhớ nguồn.

Cách tiếp cận này hoạt động bằng cách thay thế sys.stdoutbằng thứ gì đó không giống tệp (nhưng vẫn chỉ sử dụng những thứ trong thư viện chuẩn). Điều này cũng có thể gây ra sự cố cho các thư viện cơ bản của bạn, nhưng trong trường hợp đơn giản khi bạn có quyền kiểm soát tốt đối với cách sys.stdout out được sử dụng thông qua khung của bạn thì đây có thể là một cách tiếp cận hợp lý.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')

3

Điều này đã khắc phục vấn đề cho tôi.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"

1

Đây là một cách nhanh chóng cho bất kỳ ai (1) Trên nền tảng Windows (2) chạy Python 2.7 và (3) bực mình vì một phần mềm đẹp (nghĩa là không được bạn viết nên không phải là ứng cử viên để mã hóa / giải mã in thao tác) sẽ không hiển thị "các ký tự unicode đẹp" trong môi trường IDLE (Pythonwin in unicode tốt), ví dụ, các ký hiệu Logic thứ tự gọn gàng mà Stephan Boyer sử dụng trong đầu ra từ câu tục ngữ sư phạm của anh ấy trong First Order Logic Prover .

Tôi không thích ý tưởng buộc tải lại sys và tôi không thể khiến hệ thống hợp tác với việc thiết lập các biến môi trường như PYTHONIOENCODING (đã thử biến môi trường Windows trực tiếp và cũng bỏ nó trong gói sitecustomize.py trong gói trang web làm một lót = 'utf-8').

Vì vậy, nếu bạn sẵn sàng hack theo cách của mình để thành công, hãy truy cập thư mục IDLE của bạn, thường là: "C: \ Python27 \ Lib \ idlelib" Xác định vị trí tệp IOBinding.py. Tạo một bản sao của tập tin đó và lưu trữ nó ở một nơi khác để bạn có thể trở lại hành vi ban đầu khi bạn chọn. Mở tệp trong idlelib bằng trình chỉnh sửa (ví dụ: IDLE). Đi đến khu vực mã này:

# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()

encoding = "ascii"
if sys.platform == 'win32':
    # On Windows, we could use "mbcs". However, to give the user
    # a portable encoding name, we need to find the code page 
    try:
        # --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
        # --> encoding = locale.getdefaultlocale()[1]
        encoding = 'utf-8'
        codecs.lookup(encoding)
    except LookupError:
        pass

Nói cách khác, nhận xét dòng mã gốc theo ' thử ' đã biến biến mã hóa bằng locale.getdefaultlocale (vì điều đó sẽ cung cấp cho bạn cp1252 mà bạn không muốn) và thay vào đó, buộc nó phải 'utf-8 '(bằng cách thêm dòng' mã hóa = 'utf-8 ' như được hiển thị).

Tôi tin rằng điều này chỉ ảnh hưởng đến hiển thị IDLE đến thiết bị xuất chuẩn và không mã hóa được sử dụng cho tên tệp, v.v. (được lấy trong hệ thống mã hóa trước đó). Nếu bạn gặp vấn đề với bất kỳ mã nào khác mà bạn chạy trong IDLE sau này, chỉ cần thay thế tệp IOBinding.py bằng tệp gốc chưa sửa đổi.


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.