Unicode HOWTO¶
- Release:
1.12
Denna HOWTO diskuterar Pythons stöd för Unicode-specifikationen för att representera textdata, och förklarar olika problem som människor ofta stöter på när de försöker arbeta med Unicode.
Introduktion till Unicode¶
Definitioner¶
Dagens program måste kunna hantera en stor mängd olika tecken. Applikationer är ofta internationaliserade för att visa meddelanden och utdata på en mängd olika språk som kan väljas av användaren; samma program kan behöva skriva ut ett felmeddelande på engelska, franska, japanska, hebreiska eller ryska. Webbinnehåll kan skrivas på vilket som helst av dessa språk och kan även innehålla en mängd olika emoji-symboler. Pythons strängtyp använder Unicode-standarden för att representera tecken, vilket gör att Python-program kan arbeta med alla dessa olika möjliga tecken.
Unicode (https://www.unicode.org/) är en specifikation som syftar till att lista alla tecken som används av mänskliga språk och ge varje tecken sin egen unika kod. Unicode-specifikationerna revideras och uppdateras kontinuerligt för att lägga till nya språk och symboler.
Ett tecken är den minsta möjliga beståndsdelen i en text. ”A”, ”B”, ”C” osv. är alla olika tecken. Det är även ”È” och ”Í”. Tecknen varierar beroende på vilket språk eller sammanhang man talar om. Det finns till exempel ett tecken för ”romersk siffra ett”, ”Ⅰ”, som är skilt från versalbokstaven ”I”. De ser vanligtvis likadana ut, men det är två olika tecken som har olika betydelser.
Unicode-standarden beskriver hur tecken representeras av kodpunkter. Ett kodpunktsvärde är ett heltal i intervallet 0 till 0x10FFFF (ca 1,1 miljoner värden, det verkliga antalet tilldelade är mindre än så). I standarden och i detta dokument skrivs en kodpunkt med notationen U+265E
för att beteckna tecknet med värdet 0x265e
(9.822 i decimal).
Unicode-standarden innehåller en mängd tabeller med tecken och deras motsvarande kodpunkter:
0061 "a"; LATINSK SMÅ BOKSTAV A
0062 "b"; LATINSKA SMÅ BOKSTAVAR B
0063 "c"; LATINSKA SMÅ BOKSTAVAR C
...
007B '{'; VÄNSTER SNIRKLIG HAKPARENTES
...
2167 'Ⅷ'; ROMERSK SIFFRA ÅTTA
2168 'Ⅸ'; ROMERSK SIFFRA NIO
...
265E '♞'; SVART SCHACKSPRINGAR
265F '♟'; SVART SCHACKBONDE
...
1F600 '😀'; FLINANDE ANSIKTE
1F609 '😉'; BLINKANDE ANSIKTE
...
Strikt taget innebär dessa definitioner att det är meningslöst att säga ”detta är tecknet U+265E
”. U+265E
är en kodpunkt som representerar ett visst tecken; i detta fall representerar den tecknet ’BLACK CHESS KNIGHT’, ’♞’. I informella sammanhang glöms ibland denna distinktion mellan kodpunkter och tecken bort.
Ett tecken representeras på en skärm eller på papper av en uppsättning grafiska element som kallas för en glyf. Glyfen för ett stort A, till exempel, är två diagonala streck och ett horisontellt streck, men de exakta detaljerna beror på vilket typsnitt som används. Den mesta Python-koden behöver inte oroa sig för glyfer; att räkna ut vilken glyf som ska visas är i allmänhet ett jobb för en GUI-verktygslåda eller en terminals typsnittsrendering.
Kodningar¶
För att sammanfatta föregående avsnitt: en Unicode-sträng är en sekvens av kodpunkter, som är siffror från 0 till 0x10FFFF
(1.114.111 decimalt). Denna sekvens av kodpunkter måste representeras i minnet som en uppsättning kodenheter, och kodenheter mappas sedan till 8-bitars bytes. Reglerna för att översätta en Unicode-sträng till en sekvens av bytes kallas en teckenkodning, eller bara en kodning.
Den första kodningen du kan tänka dig är att använda 32-bitars heltal som kodningsenhet och sedan använda CPU:ns representation av 32-bitars heltal. I den här representationen kan strängen ”Python” se ut så här:
P y t t h o n
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00 00
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Denna representation är okomplicerad, men det finns ett antal problem med att använda den.
Den är inte portabel; olika processorer ordnar bytena på olika sätt.
Det är mycket slösaktigt med utrymme. I de flesta texter är majoriteten av kodpunkterna mindre än 127 eller mindre än 255, så en hel del utrymme upptas av
0x00
bytes. Ovanstående sträng tar 24 byte jämfört med de 6 byte som behövs för en ASCII-representation. Ökad RAM-användning spelar inte så stor roll (stationära datorer har gigabyte RAM och strängar är vanligtvis inte så stora), men att utöka vår användning av disk- och nätverksbandbredd med en faktor 4 är oacceptabelt.Det är inte kompatibelt med befintliga C-funktioner som
strlen()
, så en ny familj av breda strängfunktioner skulle behöva användas.
Därför används inte den här kodningen särskilt mycket, och folk väljer istället andra kodningar som är mer effektiva och praktiska, till exempel UTF-8.
UTF-8 är en av de vanligaste kodningarna och Python använder den ofta som standard. UTF står för ”Unicode Transformation Format”, och ”8” betyder att 8-bitarsvärden används i kodningen. (Det finns även UTF-16- och UTF-32-kodningar, men de används mer sällan än UTF-8.) UTF-8 använder följande regler:
Om kodpunkten är < 128 representeras den av motsvarande bytevärde.
Om kodpunkten är >= 128 omvandlas den till en sekvens av två, tre eller fyra byte, där varje byte i sekvensen är mellan 128 och 255.
UTF-8 har flera praktiska egenskaper:
Den kan hantera alla Unicode-kodpunkter.
En Unicode-sträng omvandlas till en sekvens av bytes som innehåller inbäddade nollbytes endast där de representerar nolltecknet (U+0000). Detta innebär att UTF-8-strängar kan bearbetas av C-funktioner som
strcpy()
och skickas via protokoll som inte kan hantera nollbytes för något annat än markörer för strängens slut.En sträng med ASCII-text är också giltig UTF-8-text.
UTF-8 är ganska kompakt; de flesta vanliga tecken kan representeras med en eller två byte.
Om byte skadas eller förloras är det möjligt att bestämma början på nästa UTF-8-kodade kodpunkt och synkronisera på nytt. Det är också osannolikt att slumpmässiga 8-bitarsdata ser ut som giltig UTF-8.
UTF-8 är en byteorienterad kodning. Kodningen anger att varje tecken representeras av en specifik sekvens av en eller flera byte. På så sätt undviks de problem med byte-ordning som kan uppstå med heltals- och ordorienterade kodningar, som UTF-16 och UTF-32, där byte-sekvensen varierar beroende på den maskinvara som strängen kodades med.
Referenser¶
På Unicode Consortiums webbplats finns teckenkartor, en ordlista och PDF-versioner av Unicode-specifikationen. Var beredd på en del svår läsning. en kronologi över Unicodes ursprung och utveckling finns också på webbplatsen.
På Computerphiles Youtube-kanal diskuterar Tom Scott kortfattat historien om Unicode och UTF-8 (9 minuter och 36 sekunder).
För att hjälpa till att förstå standarden har Jukka Korpela skrivit en introduktionsguide till läsning av Unicodes teckentabeller”.
En annan bra introduktionsartikel skrevs av Joel Spolsky. Om den här introduktionen inte klargjorde saker för dig, bör du försöka läsa den här alternativa artikeln innan du fortsätter.
Wikipedia-poster är ofta till stor hjälp, se till exempel posterna för ”teckenkodning” och UTF-8.
Pythons stöd för Unicode¶
Nu när du har lärt dig grunderna i Unicode kan vi titta på Pythons Unicode-funktioner.
Typ av sträng¶
Sedan Python 3.0 innehåller språkets typ str
Unicode-tecken, vilket innebär att alla strängar som skapas med hjälp av "unicode rocks!"
, 'unicode rocks!"
eller strängsyntaxen med tre citattecken lagras som Unicode.
Standardkodningen för Python-källkod är UTF-8, så du kan helt enkelt inkludera ett Unicode-tecken i en stränglitteral:
försök:
med open('/tmp/input.txt', 'r') som f:
...
except OSError:
# Felmeddelandet "Filen hittades inte".
print("Fichier non trouvé")
Python 3 stöder också användning av Unicode-tecken i identifierare:
répertoire = "/tmp/records.log"
med open(répertoire, "w") som f:
f.write("test\n")
Om du inte kan ange ett visst tecken i din editor eller om du av någon anledning vill behålla källkoden som ASCII-only, kan du också använda escape-sekvenser i stränglitteraler. (Beroende på ditt system kan det hända att du ser den faktiska glyfen capital-delta i stället för en u-escape)
>>> "\N{GREEK CAPITAL LETTER DELTA}" # Using the character name
'\u0394'
>>> "\u0394" # Using a 16-bit hex value
'\u0394'
>>> "\U00000394" # Using a 32-bit hex value
'\u0394'
Dessutom kan man skapa en sträng med hjälp av decode()
-metoden i bytes
. Denna metod tar ett kodnings-argument, till exempel UTF-8
, och eventuellt ett fel-argument.
Argumentet errors anger svaret när indatasträngen inte kan konverteras enligt kodningens regler. Legala värden för detta argument är 'strict'
(ger ett UnicodeDecodeError
undantag), 'replace'
(använder U+FFFD
, REPLACEMENT CHARACTER
), 'ignore'
(lämnar bara tecknet utanför Unicode-resultatet), eller 'backslashreplace'
(infogar en xNN
escape-sekvens). Följande exempel visar skillnaderna:
>>> b'\x80abc'.decode("utf-8", "strict")
Traceback (most recent call last):
...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:
invalid start byte
>>> b'\x80abc'.decode("utf-8", "replace")
'\ufffdabc'
>>> b'\x80abc'.decode("utf-8", "backslashreplace")
'\\x80abc'
>>> b'\x80abc'.decode("utf-8", "ignore")
'abc'
Kodningar anges som strängar som innehåller kodningens namn. Python kommer med ungefär 100 olika kodningar; se Python Library Reference på Standardkodningar för en lista. Vissa kodningar har flera namn; till exempel är 'latin-1'
, 'iso_8859_1'
och '8859
alla synonymer för samma kodning.
Unicode-strängar med ett tecken kan också skapas med den inbyggda funktionen chr()
, som tar heltal och returnerar en Unicode-sträng med längden 1 som innehåller motsvarande kodpunkt. Den omvända operationen är den inbyggda funktionen ord()
som tar en Unicode-sträng med ett tecken och returnerar kodpunktsvärdet:
>>> chr(57344)
'\ue000'
>>> ord('\ue000')
57344
Konvertering till byte¶
Den motsatta metoden till bytes.decode()
är str.encode()
, som returnerar en bytes
-representation av Unicode-strängen, kodad med den begärda kodningen.
Parametern errors är densamma som parametern i metoden decode()
men stöder några fler möjliga hanteringar. Förutom 'strict'
, 'ignore'
och 'replace'
(som i det här fallet infogar ett frågetecken i stället för det okodbara tecknet), finns det också 'xmlcharrefreplace'
(infogar en XML-teckenreferens), backslashreplace
(infogar en uNNNN
escapesequence) och namereplace
(infogar en N{...}
escapesequence).
Följande exempel visar de olika resultaten:
>>> u = chr(40960) + 'abcd' + chr(1972)
>>> u.encode('utf-8')
b'\xea\x80\x80abcd\xde\xb4'
>>> u.encode('ascii')
Traceback (most recent call last):
...
UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in
position 0: ordinal not in range(128)
>>> u.encode('ascii', 'ignore')
b'abcd'
>>> u.encode('ascii', 'replace')
b'?abcd?'
>>> u.encode('ascii', 'xmlcharrefreplace')
b'ꀀabcd޴'
>>> u.encode('ascii', 'backslashreplace')
b'\\ua000abcd\\u07b4'
>>> u.encode('ascii', 'namereplace')
b'\\N{YI SYLLABLE IT}abcd\\u07b4'
Lågnivårutinerna för registrering och åtkomst av tillgängliga kodningar finns i modulen codecs
. För att implementera nya kodningar krävs också att man förstår modulen codecs
. De kodnings- och avkodningsfunktioner som returneras av denna modul är dock vanligtvis mer lågnivå än vad som är bekvämt, och att skriva nya kodningar är en specialiserad uppgift, så modulen kommer inte att behandlas i denna HOWTO.
Unicode-bokstäver i Python-källkod¶
I Python-källkod kan specifika Unicode-kodpunkter skrivas med hjälp av escape-sekvensen u
, som följs av fyra hexadecimaler som anger kodpunkten. Escapesequencen U
är liknande, men förväntar sig åtta hexadecimaler, inte fyra:
>>> s = "a\xac\u1234\u20ac\U00008000"
... # ^^^^ two-digit hex escape
... # ^^^^^^ four-digit Unicode escape
... # ^^^^^^^^^^ eight-digit Unicode escape
>>> [ord(c) for c in s]
[97, 172, 4660, 8364, 32768]
Att använda escape-sekvenser för kodpunkter större än 127 är bra i små doser, men blir ett irritationsmoment om du använder många tecken med accent, som i ett program med meddelanden på franska eller något annat språk med accent. Du kan också sätta ihop strängar med hjälp av den inbyggda funktionen chr()
, men det är ännu mer omständligt.
Helst skulle du vilja kunna skriva litteraler i ditt språks naturliga kodning. Du kan sedan redigera Python-källkod med din favoritredigerare som visar de accentuerade tecknen naturligt och har rätt tecken som används vid körning.
Python stöder som standard skrivning av källkod i UTF-8, men du kan använda nästan vilken kodning som helst om du deklarerar den kodning som används. Detta görs genom att inkludera en speciell kommentar som antingen den första eller andra raden i källfilen:
#!/usr/bin/env python
# -*- kodning: latin-1 -*-
u = 'abcdé'
print(ord(u[-1]))
Syntaxen är inspirerad av Emacs notation för att ange variabler som är lokala i en fil. Emacs stöder många olika variabler, men Python stöder bara ’coding’. Symbolerna -*-
anger för Emacs att kommentaren är speciell; de har ingen betydelse för Python utan är en konvention. Python letar efter coding: name
eller coding=name
i kommentaren.
Om du inte inkluderar en sådan kommentar kommer standardkodningen som används att vara UTF-8 som redan nämnts. Se även PEP 263 för mer information.
Unicode-egenskaper¶
Unicode-specifikationen innehåller en databas med information om kodpunkter. För varje definierad kodpunkt innehåller informationen tecknets namn, dess kategori, det numeriska värdet om tillämpligt (för tecken som representerar numeriska begrepp som romerska siffror, bråk som en tredjedel och fyra femtedelar etc.) Det finns också visningsrelaterade egenskaper, t.ex. hur kodpunkten ska användas i dubbelriktad text.
Följande program visar lite information om flera tecken och skriver ut det numeriska värdet för ett visst tecken:
import unicodedata
u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)
for i, c in enumerate(u):
print(i, '%04x' % ord(c), unicodedata.category(c), end=" ")
print(unicodedata.name(c))
# Get numeric value of second character
print(unicodedata.numeric(u[1]))
När det körs skrivs det ut:
0 00e9 Ll LATINSKA SMÅ BOKSTÄVER E MED ACUTE
1 0bf2 No TAMIL NUMBER ETT TUSEN
2 0f84 Mn TIBETAN MARK HALANTA
3 1770 Lo TAGBANWA LETTER SA
4 33af So SQUARE RAD OVER S SQUARED
1000.0
Kategorikoderna är förkortningar som beskriver karaktären på tecknet. De grupperas i kategorier som ”Letter”, ”Number”, ”Punctuation” eller ”Symbol”, som i sin tur delas upp i underkategorier. För att ta koderna från ovanstående utdata betyder 'Ll'
”Letter, lowercase”, 'No'
betyder ”Number, other”, 'Mn'
är ”Mark, nonspacing” och 'So'
är ”Symbol, other”. Se avsnittet General Category Values i dokumentationen för Unicode Character Database för en lista över kategorikoder.
Jämförelse av strängar¶
Unicode gör det lite svårare att jämföra strängar eftersom samma uppsättning tecken kan representeras av olika sekvenser av kodpunkter. Till exempel kan en bokstav som ”ê” representeras som en enda kodpunkt U+00EA, eller som U+0065 U+0302, vilket är kodpunkten för ”e” följt av en kodpunkt för ”COMBINING CIRCUMFLEX ACCENT”. Dessa ger samma resultat när de skrivs ut, men den ena är en sträng med längd 1 och den andra har längd 2.
Ett verktyg för en jämförelse som inte tar hänsyn till versaler är strängmetoden casefold()
som omvandlar en sträng till en form som inte tar hänsyn till versaler enligt en algoritm som beskrivs i Unicode-standarden. Denna algoritm har särskild hantering för tecken som den tyska bokstaven ”ß” (kodpunkt U+00DF), som blir ett par gemena bokstäver ”ss”.
>>> street = 'Gürzenichstraße'
>>> street.casefold()
'gürzenichstrasse'
Ett annat verktyg är unicodedata
-modulens funktion normalize()
som omvandlar strängar till en av flera normalformer, där bokstäver som följs av ett kombinationstecken ersätts med enstaka tecken. normalize()
kan användas för att utföra strängjämförelser som inte felaktigt rapporterar ojämlikhet om två strängar använder kombinationstecken på olika sätt:
import unicodedata
def compare_strs(s1, s2):
def NFD(s):
return unicodedata.normalize('NFD', s)
return NFD(s1) == NFD(s2)
single_char = 'ê'
multiple_chars = '\N{LATIN SMALL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'
print('length of first string=', len(single_char))
print('length of second string=', len(multiple_chars))
print(compare_strs(single_char, multiple_chars))
När den körs matas den ut:
$ python jämför-strängar.py
längd på första strängen= 1
längd på andra strängen= 2
Sant
Det första argumentet till funktionen normalize()
är en sträng som anger den önskade normaliseringsformen, som kan vara en av ’NFC’, ’NFKC’, ’NFD’ och ’NFKD’.
Unicode-standarden anger också hur man gör jämförelser utan versaler:
import unicodedata
def compare_caseless(s1, s2):
def NFD(s):
return unicodedata.normalize('NFD', s)
return NFD(NFD(s1).casefold()) == NFD(NFD(s2).casefold())
# Example usage
single_char = 'ê'
multiple_chars = '\N{LATIN CAPITAL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'
print(compare_caseless(single_char, multiple_chars))
Detta kommer att skriva ut True
. (Varför anropas NFD()
två gånger? För att det finns några tecken som gör att casefold()
returnerar en icke-normaliserad sträng, så resultatet måste normaliseras igen. Se avsnitt 3.13 i Unicode-standarden för en diskussion och ett exempel)
Reguljära uttryck i Unicode¶
De reguljära uttryck som stöds av modulen re
kan anges antingen som bytes eller strängar. Vissa av specialteckensekvenserna, t.ex. d
och w
, har olika betydelse beroende på om mönstret anges som bytes eller strängar. Till exempel kommer d
att matcha tecknen [0-9]
i byte men i strängar kommer det att matcha alla tecken som är i kategorin 'Nd'
.
Strängen i det här exemplet har numret 57 skrivet med både thailändska och arabiska siffror:
import re
p = re.compile(r'\d+')
s = "Over \u0e55\u0e57 57 flavours"
m = p.search(s)
print(repr(m.group()))
När \d+
exekveras kommer den att matcha de thailändska siffrorna och skriva ut dem. Om du anger flaggan re.ASCII
till compile()
, kommer d+
att matcha substrängen ”57” istället.
På samma sätt matchar \w
ett stort antal Unicode-tecken men bara [a-zA-Z0-9_]
i byte eller om re.ASCII
anges, och s
matchar antingen Unicode-tecken för blanksteg eller [ \t\n\r\f\v]
.
Referenser¶
Några bra alternativa diskussioner om Pythons Unicode-stöd är:
Processing Text Files in Python 3, av Nick Coghlan.
Pragmatic Unicode, en PyCon 2012-presentation av Ned Batchelder.
Typen str
beskrivs i Python-bibliotekets referens på Textsekvenstyp — str.
Dokumentationen för modulen unicodedata
.
Dokumentationen för modulen codecs
.
Marc-André Lemburg gav en presentation med titeln ”Python and Unicode” (PDF slides) på EuroPython 2002. Bilderna är en utmärkt översikt över utformningen av Python 2:s Unicode-funktioner (där Unicode-strängtypen kallas unicode
och literaler börjar med u
).
Läsa och skriva Unicode-data¶
När du har skrivit kod som fungerar med Unicode-data är nästa problem input/output. Hur får du in Unicode-strängar i ditt program och hur konverterar du Unicode till en form som är lämplig för lagring eller överföring?
Det är möjligt att du inte behöver göra någonting beroende på dina inmatningskällor och utmatningsdestinationer; du bör kontrollera om de bibliotek som används i din applikation stöder Unicode nativt. XML-parsers returnerar ofta Unicode-data, till exempel. Många relationsdatabaser stöder också Unicode-värderade kolumner och kan returnera Unicode-värden från en SQL-fråga.
Unicode-data konverteras vanligtvis till en viss kodning innan de skrivs till disk eller skickas över ett uttag. Det är möjligt att göra allt arbete själv: öppna en fil, läs ett 8-bitars bytesobjekt från den och konvertera bytena med bytes.decode(encoding)
. Det manuella tillvägagångssättet rekommenderas dock inte.
Ett problem är att kodningarna består av flera byte; ett Unicode-tecken kan representeras av flera byte. Om du vill läsa filen i godtyckligt stora bitar (t.ex. 1024 eller 4096 byte) måste du skriva felhanteringskod för att fånga upp det fall där endast en del av de byte som kodar ett enda Unicode-tecken läses i slutet av en bit. En lösning skulle vara att läsa in hela filen i minnet och sedan utföra avkodningen, men då kan man inte arbeta med filer som är extremt stora; om man behöver läsa en fil på 2 GiB behöver man 2 GiB RAM. (Egentligen mer, eftersom du åtminstone för ett ögonblick måste ha både den kodade strängen och dess Unicode-version i minnet)
Lösningen skulle vara att använda avkodningsgränssnittet på låg nivå för att fånga upp fallet med partiella kodningssekvenser. Arbetet med att implementera detta har redan gjorts åt dig: den inbyggda funktionen open()
kan returnera ett filliknande objekt som förutsätter att filens innehåll är i en angiven kodning och accepterar Unicode-parametrar för metoder som read()
och write()
. Detta fungerar genom open()
parametrar encoding och errors som tolkas precis som de i str.encode()
och bytes.decode()
.
Att läsa Unicode från en fil är därför enkelt:
med open('unicode.txt', encoding='utf-8') som f:
för rad i f:
print(repr(rad))
Det är också möjligt att öppna filer i uppdateringsläge, vilket tillåter både läsning och skrivning:
med open('test', encoding='utf-8', mode='w+') som f:
f.write('\u4500 blah blah blah blah\n')
f.seek(0)
print(repr(f.readline()[:1])))
Unicode-tecknet U+FEFF
används som en byte-order mark (BOM) och skrivs ofta som det första tecknet i en fil för att hjälpa till med autodetektering av filens byte-ordning. Vissa kodningar, t.ex. UTF-16, förväntar sig att en BOM ska finnas i början av en fil; när en sådan kodning används skrivs BOM automatiskt som första tecken och tappas tyst när filen läses. Det finns varianter av dessa kodningar, t.ex. ”utf-16-le” och ”utf-16-be” för little-endian- och big-endian-kodningar, som anger en viss byteordning och inte hoppar över BOM.
Inom vissa områden är det också vanligt att använda en ”BOM” i början av UTF-8-kodade filer; namnet är missvisande eftersom UTF-8 inte är byteorderberoende. Märket meddelar helt enkelt att filen är kodad i UTF-8. För att läsa sådana filer, använd codec ’utf-8-sig’ för att automatiskt hoppa över markeringen om den finns.
Unicode-filnamn¶
De flesta operativsystem som används idag har stöd för filnamn som innehåller godtyckliga Unicode-tecken. Vanligtvis implementeras detta genom att konvertera Unicode-strängen till någon kodning som varierar beroende på systemet. Idag konvergerar Python mot att använda UTF-8: Python på MacOS har använt UTF-8 i flera versioner, och Python 3.6 bytte till att använda UTF-8 även på Windows. På Unix-system kommer det bara att finnas en filsystemkodning. om du har ställt in miljövariablerna LANG
eller LC_CTYPE
; om du inte har gjort det är standardkodningen återigen UTF-8.
Funktionen sys.getfilesystemencoding()
returnerar den kodning som ska användas på ditt nuvarande system, om du vill göra kodningen manuellt, men det finns inte mycket anledning att bry sig om det. När du öppnar en fil för läsning eller skrivning kan du vanligtvis bara ange Unicode-strängen som filnamn, så konverteras den automatiskt till rätt kodning för dig:
filnamn = 'filnamn\u4500abc'
med open(filnamn, 'w') som f:
f.write('blah\n')
Funktioner i modulen os
, t.ex. os.stat()
, accepterar också Unicode-filnamn.
Funktionen os.listdir()
returnerar filnamn, vilket väcker en fråga: ska den returnera Unicode-versionen av filnamnen, eller ska den returnera byte som innehåller de kodade versionerna? os.listdir()
kan göra båda, beroende på om du angav katalogsökvägen som byte eller en Unicode-sträng. Om du anger en Unicode-sträng som sökväg kommer filnamnen att avkodas med filsystemets kodning och en lista med Unicode-strängar kommer att returneras, medan en byte-sökväg returnerar filnamnen som bytes. Om man t.ex. antar att standard filsystemkodning är UTF-8, körs följande program:
fn = 'filename\u4500abc'
f = open(fn, 'w')
f.close()
import os
print(os.listdir(b'.'))
print(os.listdir('.'))
kommer att ge följande resultat:
$ python listdir-test.py
[b'filename\xe4\x94\x80abc', ...]
['filnamn\u4500abc', ...]
Den första listan innehåller UTF-8-kodade filnamn och den andra listan innehåller Unicode-versionerna.
Observera att vid de flesta tillfällen bör du bara hålla dig till att använda Unicode med dessa API:er. Bytes-API:erna bör endast användas på system där oavkodbara filnamn kan förekomma; det är i stort sett bara Unix-system nu.
Tips för att skriva Unicode-anpassade program¶
I detta avsnitt ges några förslag på hur man skriver programvara som hanterar Unicode.
Det viktigaste tipset är:
Programvaran ska bara arbeta med Unicode-strängar internt, avkoda indata så snart som möjligt och koda utdata först i slutet.
Om du försöker skriva bearbetningsfunktioner som accepterar både Unicode och byte-strängar, kommer du att upptäcka att ditt program är sårbart för buggar varhelst du kombinerar de två olika typerna av strängar. Det finns ingen automatisk kodning eller avkodning: om du gör t.ex. str + bytes
, kommer ett TypeError
att uppstå.
När du använder data som kommer från en webbläsare eller någon annan opålitlig källa är det vanligt att du kontrollerar om det finns olagliga tecken i en sträng innan du använder strängen i en genererad kommandorad eller lagrar den i en databas. Om du gör detta bör du vara noga med att kontrollera den avkodade strängen, inte de kodade bytesdata; vissa kodningar kan ha intressanta egenskaper, t.ex. att de inte är bijektiva eller inte är helt ASCII-kompatibla. Detta gäller särskilt om indata också anger kodningen, eftersom angriparen då kan välja ett smart sätt att dölja skadlig text i den kodade byteströmmen.
Konvertering mellan filkodningar¶
Klassen StreamRecoder
kan på ett transparent sätt konvertera mellan kodningar genom att ta en ström som returnerar data i kodning #1 och bete sig som en ström som returnerar data i kodning #2.
Om du till exempel har en inmatningsfil f som är i Latin-1 kan du linda in den med en StreamRecoder
för att returnera byte som är kodade i UTF-8:
new_f = codecs.StreamRecoder(f,
# en/decoder: används av read() för att koda dess resultat och
# av write() för att avkoda dess indata.
codecs.getencoder('utf-8'), codecs.getdecoder('utf-8'),
# läsare/skrivare: används för att läsa och skriva till strömmen.
codecs.getreader('latin-1'), codecs.getwriter('latin-1') )
Filer med okänd kodning¶
Vad kan du göra om du behöver göra en ändring i en fil, men inte känner till filens kodning? Om du vet att kodningen är ASCII-kompatibel och bara vill undersöka eller ändra ASCII-delarna, kan du öppna filen med felhanteraren surrogateescape
:
med open(fname, 'r', encoding="ascii", errors="surrogateescape") som f:
data = f.read()
# gör ändringar i strängen 'data'
med open(fnamn + '.new', 'w',
encoding="ascii", errors="surrogateescape") som f:
f.write(data)
Felhanteraren surrogateescape
avkodar alla icke-ASCII-bytes som kodpunkter i ett speciellt intervall som sträcker sig från U+DC80 till U+DCFF. Dessa kodpunkter förvandlas sedan tillbaka till samma byte när felhanteraren urrogateescape
används för att koda data och skriva ut den igen.
Referenser¶
Ett avsnitt i Mastering Python 3 Input/Output, ett föredrag av David Beazley på PyCon 2010, handlar om textbehandling och binär datahantering.
PDF-slides för Marc-André Lemburgs presentation ”Writing Unicode-aware Applications in Python” diskuterar frågor om teckenkodning samt hur man internationaliserar och lokaliserar en applikation. Dessa bilder täcker endast Python 2.x.
The Guts of Unicode in Python är ett föredrag på PyCon 2013 av Benjamin Peterson som diskuterar den interna Unicode-representationen i Python 3.3.
Tack till¶
Det första utkastet till detta dokument skrevs av Andrew Kuchling. Det har sedan reviderats ytterligare av Alexander Belopolsky, Georg Brandl, Andrew Kuchling och Ezio Melotti.
Tack till följande personer som har noterat felaktigheter eller lämnat förslag på denna artikel: Éric Araujo, Nicholas Bastin, Nick Coghlan, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André Lemburg, Martin von Löwis, Terry J. Reedy, Serhiy Storchaka, Eryk Sun, Chad Whitacre, Graham Wideman.