Tại sao phải khai báo unicode theo chuỗi trong python?


122

Tôi vẫn đang học python và tôi nghi ngờ:

Trong python 2.6.x, tôi thường khai báo mã hóa trong tiêu đề tệp như thế này (như trong PEP 0263 )

# -*- coding: utf-8 -*-

Sau đó, các chuỗi của tôi được viết như bình thường:

a = "A normal string without declared Unicode"

Nhưng mỗi khi tôi thấy mã dự án python, mã hóa không được khai báo ở tiêu đề. Thay vào đó, nó được khai báo ở mọi chuỗi như thế này:

a = u"A string with declared Unicode"

Có gì khác biệt? Mục đích của việc này là gì? Tôi biết Python 2.6.x đặt mã hóa ASCII theo mặc định, nhưng nó có thể bị ghi đè bởi khai báo tiêu đề, vậy điểm khai báo mỗi chuỗi là gì?

Phụ lục: Có vẻ như tôi đã trộn lẫn mã hóa tệp với mã hóa chuỗi. Cảm ơn vì đã giải thích nó :)


6
# coding: utf8là đủ tốt, không cần-*-
con sứa

1
@jellyfish Tôi cho rằng bạn muốn nhập # coding: utf-8.
Samuel Harmer

Câu trả lời:


167

Đó là hai điều khác nhau, như những người khác đã đề cập.

Khi bạn chỉ định# -*- coding: utf-8 -*- , bạn đang cho Python biết tệp nguồn mà bạn đã lưu utf-8. Mặc định cho Python 2 là ASCII (đối với Python 3 là utf-8). Điều này chỉ ảnh hưởng đến cách trình thông dịch đọc các ký tự trong tệp.

Nói chung, có lẽ không phải là ý tưởng tốt nhất để nhúng các ký tự unicode cao vào tệp của bạn bất kể mã hóa là gì; bạn có thể sử dụng thoát chuỗi unicode, hoạt động ở một trong hai kiểu mã hóa.


Khi bạn khai báo một chuỗi có dấu uở phía trước , chẳng hạn như u'This is a string', nó cho trình biên dịch Python biết rằng chuỗi đó là Unicode, không phải byte. Điều này hầu hết được xử lý minh bạch bởi thông dịch viên; sự khác biệt rõ ràng nhất là bây giờ bạn có thể nhúng các ký tự unicode vào chuỗi (nghĩa u'\u2665'là bây giờ là hợp pháp). Bạn có thể sử dụng from __future__ import unicode_literalsđể đặt nó làm mặc định.

Điều này chỉ áp dụng cho Python 2; trong Python 3, mặc định là Unicode và bạn cần phải chỉ định một bphía trước (như b'These are bytes'khai báo một chuỗi các byte).


Cảm ơn vì lời giải thích! Tôi sẽ thiết lập này là chấp nhận kể từ khi là một hoàn thiện nhất :)
Oscar Carballal

2
Mã nguồn mặc định cho Python 2 là ascii .
Mark Tolonen

27
Thực sự là một ý tưởng tuyệt vời khi nhúng các ký tự unicode cao vào tệp của bạn. Tôi nghi ngờ những người không nói tiếng Anh muốn đọc các thoát unicode trong chuỗi của họ.
Mark Tolonen

@Mark: Cảm ơn bạn đã chỉnh sửa ASCII; Tôi nhanh chóng lướt qua PEP ( python.org/dev/peps/pep-0263 ) và nó nói về tiếng Latin-1 trong phần mở đầu. Tôi không nghĩ rằng đó là một ý tưởng tuyệt vời khi nhúng các ký tự unicode cao vào tệp của bạn trong hầu hết các trường hợp. Chắc chắn, nếu bạn đang mã hóa nhiều chuỗi không phải tiếng Anh trong tệp nguồn của mình, điều đó có thể làm cho nó dễ dàng hơn, nhưng bạn thường làm điều đó để hiển thị cho người dùng và dù sao thì bạn cũng nên xác định những chuỗi đó ở một nơi riêng biệt. Và một trình soạn thảo văn bản được định cấu hình sai có thể làm hỏng tất cả các ký tự đó.
Chris B.

4
đã đồng ý nếu bạn đang lập trình một ứng dụng i18nalized, nhưng hãy cân nhắc nếu bạn là lập trình viên người Trung Quốc hay người Pháp. Nó không chỉ là chuỗi, mà còn cả những nhận xét. Thật tuyệt khi Python linh hoạt với các mã nguồn. Python 3 thậm chí có thể có các ký tự không phải ASCII trong tên biến.
Mark Tolonen

23

Như những người khác đã nói, # coding:chỉ định mã hóa tệp nguồn được lưu trong. Dưới đây là một số ví dụ để minh họa điều này:

Tệp được lưu trên đĩa dưới dạng cp437 (mã hóa bảng điều khiển của tôi), nhưng không có mã hóa nào được khai báo

b = 'über'
u = u'über'
print b,repr(b)
print u,repr(u)

Đầu ra:

  File "C:\ex.py", line 1
SyntaxError: Non-ASCII character '\x81' in file C:\ex.py on line 1, but no
encoding declared; see http://www.python.org/peps/pep-0263.html for details

Đầu ra của tệp có # coding: cp437thêm:

über '\x81ber'
über u'\xfcber'

Lúc đầu, Python không biết mã hóa và phàn nàn về ký tự không phải ASCII. Khi nó đã biết mã hóa, chuỗi byte nhận các byte thực sự có trên đĩa. Đối với chuỗi Unicode, Python đọc \ x81, biết rằng trong cp437 đó là ü và giải mã nó thành mã Unicode cho ü là U + 00FC. Khi chuỗi byte được in, Python đã gửi 81trực tiếp giá trị hex đến bảng điều khiển. Khi chuỗi Unicode được in, Python đã phát hiện chính xác bảng mã của bảng điều khiển của tôi là cp437 và dịch Unicode ü sang giá trị cp437 cho ü .

Đây là những gì xảy ra với một tệp được khai báo và lưu trong UTF-8:

├╝ber '\xc3\xbcber'
über u'\xfcber'

Trong UTF-8, ü được mã hóa dưới dạng các byte hex C3 BC, do đó chuỗi byte chứa các byte đó, nhưng chuỗi Unicode giống với ví dụ đầu tiên. Python đọc hai byte và giải mã nó một cách chính xác. Python đã in sai chuỗi byte, vì nó đã gửi hai byte UTF-8 đại diện cho ü trực tiếp tới bảng điều khiển cp437 của tôi.

Ở đây tệp được khai báo là cp437, nhưng được lưu trong UTF-8:

├╝ber '\xc3\xbcber'
├╝ber u'\u251c\u255dber'

Chuỗi byte vẫn có các byte trên đĩa (UTF-8 hex byte C3 BC), nhưng diễn giải chúng thành hai ký tự cp437 thay vì một ký tự được mã hóa UTF-8. Hai ký tự đó được dịch sang các điểm mã Unicode và mọi thứ đều in không chính xác.


10

Điều đó không đặt định dạng của chuỗi; nó thiết lập định dạng của tệp. Ngay cả với tiêu đề đó, "hello"là một chuỗi byte, không phải là một chuỗi Unicode. Để biến nó thành Unicode, bạn sẽ phải sử dụng u"hello"ở mọi nơi. Tiêu đề chỉ là một gợi ý về định dạng sẽ sử dụng khi đọc .pytệp.


Khi đó tôi đã nhầm, tôi nghĩ chúng giống nhau. Vậy việc sử dụng cho chuỗi unicode là i18n?
Oscar Carballal

@Oscar: Có, phần lớn. Nếu bạn đang tạo một trang web bằng Django hoặc thứ gì đó và nó phải xử lý những người có ký tự không phải ASCII, thì đó là một cách sử dụng khả thi khác.
icktoofay

7

Định nghĩa tiêu đề là xác định mã hóa của chính mã, không phải các chuỗi kết quả trong thời gian chạy.

đặt một ký tự không phải ascii như ۲ trong tập lệnh python mà không có định nghĩa tiêu đề utf-8 sẽ đưa ra cảnh báo

lỗi


-1

Tôi đã tạo mô-đun sau được gọi là unicoder để có thể thực hiện chuyển đổi trên các biến:

import sys
import os

def ustr(string):

    string = 'u"%s"'%string

    with open('_unicoder.py', 'w') as script:

        script.write('# -*- coding: utf-8 -*-\n')
        script.write('_ustr = %s'%string)

    import _unicoder
    value = _unicoder._ustr

    del _unicoder
    del sys.modules['_unicoder']

    os.system('del _unicoder.py')
    os.system('del _unicoder.pyc')

    return value

Sau đó, trong chương trình của bạn, bạn có thể làm như sau:

# -*- coding: utf-8 -*-

from unicoder import ustr

txt = 'Hello, Unicode World'
txt = ustr(txt)

print type(txt) # <type 'unicode'>
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.