Po co deklarować unicode przez string w Pythonie?

122

Wciąż uczę się Pythona i mam wątpliwości:

W pythonie 2.6.x zwykle deklaruję kodowanie w nagłówku pliku w ten sposób (jak w PEP 0263 )

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

Następnie moje napisy są zapisywane jak zwykle:

a = "A normal string without declared Unicode"

Ale za każdym razem, gdy widzę kod projektu w Pythonie, kodowanie nie jest zadeklarowane w nagłówku. Zamiast tego jest deklarowany w każdym łańcuchu w następujący sposób:

a = u"A string with declared Unicode"

Co za różnica? Jaki jest tego cel? Wiem, że Python 2.6.x domyślnie ustawia kodowanie ASCII, ale można je zastąpić deklaracją nagłówka, więc jaki jest cel deklaracji ciągu?

Dodatek: Wygląda na to, że pomyliłem kodowanie plików z kodowaniem ciągów. Dzięki za wyjaśnienie :)

Oscar Carballal
źródło
6
# coding: utf8jest wystarczająco dobry, nie ma potrzeby-*-
meduzy
1
@jellyfish Zakładam, że chciałeś pisać # coding: utf-8.
Samuel Harmer
Powinien być #coding=utf-8. python.org/dev/peps/pep-0263
Guangtong Shen

Odpowiedzi:

167

To dwie różne rzeczy, o czym wspominali inni.

Kiedy określasz# -*- coding: utf-8 -*- , mówisz Pythonowi, że zapisany plik źródłowy to utf-8. Wartość domyślna dla Pythona 2 to ASCII (dla Pythona 3 jest to utf-8). Ma to tylko wpływ na to, jak interpreter odczytuje znaki w pliku.

Ogólnie rzecz biorąc, prawdopodobnie nie jest najlepszym pomysłem osadzanie w pliku dużych znaków Unicode, niezależnie od tego, jakie jest kodowanie; możesz użyć znaków ucieczki Unicode, które działają w obu kodowaniach.


Kiedy deklarujesz ciąg z uprzednią , na przykład u'This is a string', mówi kompilatorowi Pythona, że ​​ciąg jest Unicode, a nie bajty. Jest to obsługiwane głównie w sposób przejrzysty przez tłumacza; Najbardziej oczywistą różnicą jest to, że można teraz osadzać znaki Unicode w łańcuchu (to znaczy, że u'\u2665'jest teraz legalny). Możesz użyć, from __future__ import unicode_literalsaby ustawić go jako domyślny.

Dotyczy to tylko Pythona 2; w Pythonie 3 wartością domyślną jest Unicode i musisz podać bprzednią literę (np. b'These are bytes'w celu zadeklarowania sekwencji bajtów).

Chris B.
źródło
Dziękuję za wyjaśnienie!
Ustawię
2
Domyślne kodowanie źródłowe dla Pythona 2 to ascii .
Mark Tolonen,
27
To naprawdę świetny pomysł, aby osadzić w pliku znaki o dużej liczbie znaków Unicode. Wątpię, czy osoby nieanglojęzyczne chcą czytać znaki unikodowe w swoich strunach.
Mark Tolonen,
@Mark: Dzięki za korektę ASCII; Szybko przejrzałem PEP ( python.org/dev/peps/pep-0263 ) i mówi o Latin-1 w preambule. Nie sądzę, aby w większości przypadków osadzanie w pliku dużych znaków Unicode było dobrym pomysłem. Oczywiście, jeśli kodujesz wiele nieanglojęzycznych ciągów w swoim pliku źródłowym, może to ułatwić, ale generalnie robisz to w celu wyświetlenia użytkownikowi i prawdopodobnie powinieneś zdefiniować je w osobnym miejscu. A pojedynczy źle skonfigurowany edytor tekstu może uszkodzić wszystkie te znaki.
Chris B.,
4
zgodził się, jeśli programujesz aplikację i18nalized, ale zastanów się, czy jesteś programistą chińskim czy francuskim. Nie chodzi tylko o struny, ale także o komentarze. To wspaniale, że Python jest elastyczny dzięki kodowaniu źródeł. Python 3 może nawet zawierać znaki spoza ASCII w nazwach zmiennych.
Mark Tolonen,
23

Jak powiedzieli inni, # coding:określa kodowanie, w jakim zapisywany jest plik źródłowy. Oto kilka przykładów ilustrujących to:

Plik zapisany na dysku jako cp437 (moje kodowanie konsoli), ale nie zadeklarowano kodowania

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

Wynik:

  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

Wyjście pliku z # coding: cp437dodanymi:

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

Początkowo Python nie znał kodowania i narzekał na znak spoza ASCII. Gdy już poznał kodowanie, ciąg bajtów pobierał bajty, które faktycznie znajdowały się na dysku. W przypadku ciągu znaków Unicode, Python odczytał \ x81, wiedział, że w cp437 był to ü i zdekodował go do punktu kodowego Unicode dla ü, czyli U + 00FC. Gdy łańcuch bajtów został wydrukowany, Python wysłał wartość szesnastkową 81bezpośrednio do konsoli. Po wydrukowaniu łańcucha Unicode Python poprawnie wykrył kodowanie mojej konsoli jako cp437 i przetłumaczył Unicode ü na wartość cp437 dla ü .

Oto, co dzieje się z plikiem zadeklarowanym i zapisanym w UTF-8:

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

W UTF-8 ü jest kodowane jako bajty szesnastkowe C3 BC, więc ciąg bajtów zawiera te bajty, ale łańcuch Unicode jest identyczny z pierwszym przykładem. Python odczytał dwa bajty i poprawnie je zdekodował. Python niepoprawnie wydrukował łańcuch bajtów, ponieważ wysłał dwa bajty UTF-8 reprezentujące ü bezpośrednio do mojej konsoli cp437.

Tutaj plik jest zadeklarowany jako cp437, ale zapisany w UTF-8:

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

Ciąg bajtów nadal zawiera bajty na dysku (bajty szesnastkowe UTF-8 C3 BC), ale zinterpretował je jako dwa znaki cp437 zamiast jednego znaku zakodowanego w UTF-8. Te dwa znaki zostały przetłumaczone na punkty kodowe Unicode i wszystko jest drukowane nieprawidłowo.

Mark Tolonen
źródło
10

To nie ustawia formatu ciągu; ustawia format pliku. Nawet z tym nagłówkiem "hello"jest ciągiem bajtów, a nie ciągiem Unicode. Aby uzyskać Unicode, będziesz musiał używać go u"hello"wszędzie. Nagłówek to tylko wskazówka, jakiego formatu użyć podczas czytania .pypliku.

icktoofay
źródło
Myliłem się wtedy, myślałem, że są takie same. Więc użycie dla ciągów Unicode to i18n?
Oscar Carballal
@Oscar: Tak, w większości. Jeśli tworzyłeś stronę internetową za pomocą Django lub czegoś podobnego i musiała ona obsługiwać ludzi ze znakami spoza ASCII, to jest to kolejne możliwe zastosowanie.
icktoofay,
7

Definicja nagłówka służy do definiowania kodowania samego kodu, a nie wynikowych ciągów w czasie wykonywania.

umieszczenie znaku innego niż ASCII, takiego jak ۲ w skrypcie Pythona bez definicji nagłówka utf-8 spowoduje wyświetlenie ostrzeżenia

błąd

odpływ
źródło
-1

Zrobiłem następujący moduł o nazwie unicoder, aby móc wykonać transformację na zmiennych:

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

Następnie w swoim programie możesz wykonać następujące czynności:

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

from unicoder import ustr

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

print type(txt) # <type 'unicode'>
tecnobillo
źródło