ทำไมประกาศ Unicode ด้วยสตริงใน python


122

ฉันยังเรียน python อยู่และมีข้อสงสัย:

ใน python 2.6.x ฉันมักจะประกาศการเข้ารหัสในส่วนหัวของไฟล์เช่นนี้ (เช่นเดียวกับในPEP 0263 )

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

หลังจากนั้นสตริงของฉันจะถูกเขียนตามปกติ:

a = "A normal string without declared Unicode"

แต่ทุกครั้งที่ฉันเห็นรหัสโครงการ python การเข้ารหัสจะไม่ถูกประกาศที่ส่วนหัว แต่จะมีการประกาศทุกสตริงดังนี้:

a = u"A string with declared Unicode"

อะไรคือความแตกต่าง? จุดประสงค์ของสิ่งนี้คืออะไร? ฉันรู้ว่า Python 2.6.x ตั้งค่าการเข้ารหัส ASCII ตามค่าเริ่มต้น แต่สามารถลบล้างได้ด้วยการประกาศส่วนหัวดังนั้นจุดของการประกาศต่อสตริงคืออะไร?

ภาคผนวก:ดูเหมือนว่าฉันผสมการเข้ารหัสไฟล์กับการเข้ารหัสสตริง ขอบคุณที่อธิบาย :)


6
# coding: utf8ดีพอไม่ต้อง-*-
แมงกะพรุน

1
@jellyfish # coding: utf-8ผมถือว่าคุณหมายถึงการพิมพ์
Samuel Harmer

#coding=utf-8ควรจะเป็น python.org/dev/peps/pep-0263
Guangtong Shen

คำตอบ:


167

สิ่งเหล่านี้เป็นสองสิ่งที่แตกต่างกันดังที่คนอื่นกล่าวถึง

เมื่อคุณระบุ# -*- coding: utf-8 -*-คุณกำลังบอก Python ว่าไฟล์ต้นฉบับที่คุณบันทึกไว้คือutf-8อะไร ค่าเริ่มต้นสำหรับ Python 2 คือ ASCII (สำหรับ Python 3 utf-8) สิ่งนี้มีผลต่อวิธีที่ล่ามอ่านอักขระในไฟล์

โดยทั่วไปอาจไม่ใช่ความคิดที่ดีที่สุดในการฝังอักขระ Unicode สูงลงในไฟล์ของคุณไม่ว่าการเข้ารหัสจะเป็นอย่างไร คุณสามารถใช้สตริง Unicode Escape ซึ่งทำงานในการเข้ารหัส


เมื่อคุณประกาศสตริงที่มีuในหน้าเหมือนu'This is a string'จะบอกคอมไพเลอร์หลามที่สตริงเป็น Unicode ไม่ไบต์ สิ่งนี้ได้รับการจัดการอย่างโปร่งใสโดยล่ามเป็นส่วนใหญ่ ความแตกต่างที่ชัดเจนที่สุดคือตอนนี้คุณสามารถฝังอักขระ Unicode ในสตริงได้แล้ว (นั่นu'\u2665'คือตอนนี้ถูกกฎหมาย) คุณสามารถใช้from __future__ import unicode_literalsเพื่อให้เริ่มต้น

สิ่งนี้ใช้ได้กับ Python 2 เท่านั้น ใน Python 3 ค่าเริ่มต้นคือ Unicode และคุณต้องระบุbข้างหน้า (เช่นb'These are bytes'เพื่อประกาศลำดับไบต์)


ขอบคุณสำหรับคำอธิบาย! ฉันจะตั้งค่านี้เป็นที่ยอมรับเนื่องจากเป็นเกมที่สมบูรณ์ที่สุด :)
Oscar Carballal

2
การเข้ารหัสแหล่งเริ่มต้นสำหรับงูหลาม 2 เป็นASCII
Mark Tolonen

27
เป็นความคิดที่ดีในการฝังอักขระ Unicode ลงในไฟล์ของคุณ ฉันสงสัยว่าผู้ที่ไม่ใช้ภาษาอังกฤษต้องการอ่าน Unicode Escape ในสตริงของพวกเขา
Mark Tolonen

@ มาร์ค: ขอบคุณสำหรับการแก้ไข ASCII; ฉันอ่าน PEP ( python.org/dev/peps/pep-0263 ) อย่างรวดเร็วและพูดถึงภาษาละติน -1 ในคำนำ ฉันไม่คิดว่าเป็นความคิดที่ดีที่จะฝังอักขระ Unicode สูงในไฟล์ของคุณโดยส่วนใหญ่ แน่นอนว่าหากคุณกำลังเข้ารหัสสตริงที่ไม่ใช่ภาษาอังกฤษจำนวนมากในไฟล์ต้นฉบับของคุณอาจทำให้ง่ายขึ้น แต่โดยทั่วไปคุณจะทำเช่นนั้นเพื่อแสดงต่อผู้ใช้และคุณควรกำหนดสตริงเหล่านั้นในที่แยกต่างหาก และโปรแกรมแก้ไขข้อความที่กำหนดค่าไม่ถูกต้องเพียงครั้งเดียวอาจทำให้อักขระเหล่านั้นเสียหายได้
Chris B.

4
ตกลงกันว่าคุณกำลังเขียนโปรแกรมแอพ i18nalized แต่ให้พิจารณาว่าคุณเป็นโปรแกรมเมอร์จีนหรือฝรั่งเศส ไม่ใช่แค่สตริง แต่แสดงความคิดเห็นด้วย เป็นเรื่องดีที่ Python มีความยืดหยุ่นในการเข้ารหัสแหล่งที่มา Python 3 สามารถมีอักขระที่ไม่ใช่ ASCII ในชื่อตัวแปรได้
Mark Tolonen

23

ตามที่คนอื่น ๆ ได้กล่าว# coding:ไว้ระบุการเข้ารหัสที่ไฟล์ต้นฉบับจะถูกบันทึกไว้นี่คือตัวอย่างบางส่วนเพื่อแสดงสิ่งนี้:

ไฟล์ที่บันทึกในดิสก์เป็น cp437 (การเข้ารหัสคอนโซลของฉัน) แต่ไม่มีการประกาศการเข้ารหัส

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

เอาท์พุท:

  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

ผลลัพธ์ของไฟล์ที่# coding: cp437เพิ่ม:

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

ในตอนแรก Python ไม่รู้จักการเข้ารหัสและบ่นเกี่ยวกับอักขระที่ไม่ใช่ ASCII เมื่อทราบการเข้ารหัสแล้วสตริงไบต์จะได้รับไบต์ที่อยู่บนดิสก์จริง สำหรับสตริง Unicode Python อ่าน \ x81 รู้ว่าใน cp437 นั่นคือüและถอดรหัสเป็น Unicode codepoint สำหรับüซึ่งเป็น U + 00FC เมื่อพิมพ์สตริงไบต์ Python จะส่งค่าฐานสิบหก81ไปยังคอนโซลโดยตรง เมื่อสายอักขระ Unicode พิมพ์, Python อย่างถูกต้องตรวจพบการเข้ารหัสคอนโซลของฉันเป็น cp437 และแปล Unicode üกับค่า cp437 สำหรับü

นี่คือสิ่งที่เกิดขึ้นกับไฟล์ที่ประกาศและบันทึกใน UTF-8:

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

ใน UTF-8 üถูกเข้ารหัสเป็นไบต์ฐานสิบหกC3 BCดังนั้นสตริงไบต์จึงมีไบต์เหล่านั้น แต่สตริง Unicode จะเหมือนกับตัวอย่างแรก Python อ่านสองไบต์และถอดรหัสอย่างถูกต้อง Python พิมพ์สตริงไบต์ไม่ถูกต้องเนื่องจากส่ง UTF-8 ไบต์สองไบต์ที่แสดงถึงüไปยังคอนโซล cp437 ของฉันโดยตรง

ที่นี่มีการประกาศไฟล์ cp437 แต่บันทึกใน UTF-8:

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

สตริงไบต์ยังคงได้รับไบต์บนดิสก์ (UTF-8 hex bytes C3 BC) แต่ตีความเป็นอักขระ cp437 สองตัวแทนที่จะเป็นอักขระเข้ารหัส UTF-8 ตัวเดียว อักขระสองตัวที่แปลเป็นรหัส Unicode ชี้และทุกอย่างพิมพ์ไม่ถูกต้อง


10

ที่ไม่ได้กำหนดรูปแบบของสตริง มันกำหนดรูปแบบของไฟล์ แม้จะมีส่วนหัวนั้น แต่"hello"ก็เป็นสตริงไบต์ไม่ใช่สตริง Unicode เพื่อให้เป็น Unicode คุณจะต้องใช้u"hello"ทุกที่ ส่วนหัวเป็นเพียงคำใบ้ว่าจะใช้รูปแบบใดเมื่ออ่าน.pyไฟล์


ฉันเข้าใจผิดแล้วฉันคิดว่าพวกเขาเหมือนกัน ดังนั้นการใช้สตริง Unicode คือ i18n?
Oscar Carballal

@Oscar: ใช่ส่วนใหญ่ หากคุณกำลังสร้างเว็บไซต์ด้วย Django หรือบางสิ่งบางอย่างและต้องจัดการกับคนที่มีอักขระที่ไม่ใช่ ASCII นั่นก็เป็นอีกวิธีหนึ่งที่เป็นไปได้
icktoofay

7

นิยามส่วนหัวคือการกำหนดการเข้ารหัสของโค้ดเองไม่ใช่สตริงผลลัพธ์ที่รันไทม์

การใส่อักขระที่ไม่ใช่ ascii เช่น ۲ ในสคริปต์ python โดยไม่มีการกำหนดส่วนหัว utf-8 จะส่งคำเตือน

ความผิดพลาด


-1

ฉันสร้างโมดูลต่อไปนี้ที่เรียกว่า unicoder เพื่อให้สามารถแปลงตัวแปรได้:

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

จากนั้นในโปรแกรมของคุณคุณสามารถทำสิ่งต่อไปนี้:

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

from unicoder import ustr

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

print type(txt) # <type 'unicode'>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.