Ha egy karakterláncot bájtsorozattá kívánunk alakítani, akkor a karakterláncra meg kell hívni az encode() metódust, amelynek argumentuma egy, a kódolásra utaló név, ami az adott kódolást végző kodeket azonosítja.
Az egyes kodekek mint modulfájlok a Python telepítésekor az encodings mappa alá kerülnek. A kodekfájl neve a kódolás szokásos, illetve standard nevével egyezik meg. Még pontosabban, a fájlnév a kódolás nevének úgynevezett normalizált formája, aminek lefőbb jellemzője, hogy a kódolás elnevezésében előforduló nem alfanumerikus karakter (pl. kötőjel vagy mínusz jel) aláhúzásjelként jelenik meg a fájlnévben. Például az ‘iso8859-2’ kódolás kodekfájljának neve iso8859_2.py.
Az egyes kódolásoknak lehetnek alternatív nevei (alias) is, amit ugyanúgy megadhatunk az encode() metódusnak. Ezeket az encodings mappában található aliases.py fájlban az aliases nevű szótár tartalmazza, ahol a szótár kulcsai az alternatív nevek, a kulcsokhoz tartozó értékek pedig a kódolás standard vagy szokásos neve. Például a ‘cp1250’ kódolás alternatív nevei ‘1250’ vagy ‘windows-1250’. Ezért az aliases szótárban az ‘1250’ és ‘windows-1250’ kulcsként szerepel, és mindkettőhöz a ‘cp1250’ érték tartozik.
Most azt a feladatot tűzzük célul ki, hogy gyűjtsük össze a karakterkódolásra alkalmas kodekek standard neveit és a hozzájuk tartozó alternatív neveket. Ez a gyakorláson túl jól jöhet akkor, ha az egyes kódolásokat, azok hatását akarjuk vizsgálni, és nem literál formában akarjuk az encode() metódusnak megadni a kódolások nevét.
A feladat megoldását nehezíti, hogy az encodings mappa alatti kodekek nem mindegyike szolgál karakterkódolásra. Tehát ezeket ki kell szűrni. A másik megoldandó részfeladat az, hogy a karakterkódolásra alkalmas kodeknevek leválogatása után mindegyikhez hozzá kell rendelni az összes alternatív nevet.
A kodeknevek szétválogatását egy erre a célra alkalmas TextEncodingsSelector nevű osztállyal fogjuk végezni, amelynek három különböző példányattribútuma (egy lista, egy halmaz és egy szótár) tartalmazza majd a karakterkódolásra alkalmas kodekneveket, a nem karakterkódolásra szolgáló kodekek neveit, valamint a karakterkódolásra alkalmas kodekneveket a hozzájuk tartozó alternatív nevekkel. Az osztály definíciója a következő. A részletes kommentek segítenek a működési logika megértésében.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
from itertools import groupby from pathlib import Path from importlib import import_module import encodings # A kodekek alternatív neveit (alias) mint kulcsokat és a szokásos vagy standard kodekneveket mint értékeket tartalmazó szótár # kikérése az encodings modul aliases almoduljából. from encodings.aliases import aliases class TextEncodingsSelector: """A Python karakterkódolásra alkalmas kodekeinek neveit gyűjti össze az alternatív neveikkel együtt.""" def __init__(self): self.text_encodings = [] # Karakterkódolásra alkalmas kodekek nevei. self.non_text_encodings = set() # Nem karakterkódolásra való kodekek nevei. self.text_encodings_and_aliases: dict[str, list[str]] = {} # Karakterkódolásra alkalmas kodekek nevei az alternatív nevekkel. # Az encodings modulban levő összes kodek nevének előállítása a kodekfájlok nevei alapján. # Az encodings modulobjektum __file__ attribútuma megadja, hogy aktuálisan milyen útvonalon érhető el az # encodings __init__.py fájlja. Az encodings mappa az ezen útvonal Path példányának szülője lesz. # Az encodings mappa tartalmán végighaladva, kiszűrjük a kodekfájlokat és azok neveit egy halmazban összegyűjtjük. potential_codecs: set = {fn.stem for fn in Path(encodings.__file__).parent.iterdir() if not (fn.stem.startswith('_') or fn.stem == 'aliases')} # Az összegyűjtött kodekneveket abc sorrendbe rendezzük, majd sorban egymás után megvizsgáljuk, hogy az aktuális kodek # karakterkódolásra való vagy sem. Ennek alapján a kodekneveket szétválogatjuk egy-egy konténerbe. for codec_name in sorted(potential_codecs): try: ''.encode(codec_name) self.text_encodings.append(codec_name) except (LookupError, UnicodeError): self.non_text_encodings.add(codec_name) # Az aliases szótárból egy olyan szótárat állítunk elő, amelyben a kulcsok a standard kodeknevek és a kulcsokhoz tartozó # értékek az alternatív nevek egy listában felsorolva. codecs_and_aliases: dict = {codec_name: [alias for alias, _ in alias_iterator] for codec_name, alias_iterator in groupby(sorted(aliases.items(), key=lambda e: e[1]), key=lambda e: e[1])} # Mivel nem minden standard kodeknévhez tartozik alias, ezért előállítunk egy olyan szótárt, amely kulcsként az összes, # karakterkódolásra alkalmas kodeknevet tartalmazza, és értékként egy listát, amelynek elemei az alternatív nevek, vagy # üres ha nincs alias. self.text_encodings_and_aliases.update({codec_name: codecs_and_aliases.get(codec_name) if codec_name in codecs_and_aliases else [] for codec_name in self.text_encodings}) |
A teszteredmények alább láthatók. Itt először a karakterkódolásra alkalmas és nem alkalmas kodekek száma látható. Ezt követően kiírtuk a konkrét kódolások nevei közül néhányat (az összes negyedét) az alternatív nevekkel együtt. Azért nem az összeset, mert amint látható, a karakterkódolásra alkalmas kodekek száma meglehetősen nagy.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# TESZT selector = TextEncodingsSelector() print('Karakterkódolásra alkalmas kodekek száma: {}'.format(len(selector.text_encodings))) print('Nem karakterkódolásra való kodekek száma: {}'.format(len(selector.non_text_encodings))) print('\nKarakterkódolásra alkalmas néhány kodek és alternatív neveik zárójelben felsorolva:') for codec_name, alias_list in tuple(selector.text_encodings_and_aliases.items())[1::4]: print('{:18} {}'.format(codec_name, alias_list)) # Eredmények: # Karakterkódolásra alkalmas kodekek száma: 112 # Nem karakterkódolásra való kodekek száma: 8 # # Karakterkódolásra alkalmas néhány kodek és alternatív neveik zárójelben felsorolva: # big5 ['big5_tw', 'csbig5', 'x_mac_trad_chinese'] # cp1006 [] # cp1250 ['1250', 'windows_1250'] # cp1254 ['1254', 'windows_1254'] # cp1258 ['1258', 'windows_1258'] # cp500 ['500', 'csibm500', 'ebcdic_cp_be', 'ebcdic_cp_ch', 'ibm500'] # cp850 ['850', 'cspc850multilingual', 'ibm850'] # cp857 ['857', 'csibm857', 'ibm857'] # cp862 ['862', 'cspc862latinhebrew', 'ibm862'] # cp866 ['866', 'csibm866', 'ibm866'] # cp932 ['932', 'ms932', 'mskanji', 'ms_kanji'] # euc_jisx0213 ['eucjisx0213'] # gb2312 ['chinese', 'csiso58gb231280', 'euc_cn', 'euccn', 'eucgb2312_cn', 'gb2312_1980', 'gb2312_80', 'iso_ir_58', 'x_mac_simp_chinese'] # idna [] # iso2022_jp_2004 ['iso_2022_jp_2004', 'iso2022jp_2004'] # iso8859_1 [] # iso8859_14 ['iso_8859_14', 'iso_8859_14_1998', 'iso_celtic', 'iso_ir_199', 'l8', 'latin8'] # iso8859_3 ['csisolatin3', 'iso_8859_3', 'iso_8859_3_1988', 'iso_ir_109', 'l3', 'latin3'] # iso8859_7 ['csisolatingreek', 'ecma_118', 'elot_928', 'greek', 'greek8', 'iso_8859_7', 'iso_8859_7_1987', 'iso_ir_126'] # koi8_r ['cskoi8r'] # latin_1 ['8859', 'cp819', 'csisolatin1', 'ibm819', 'iso8859', 'iso8859_1', 'iso_8859_1', 'iso_8859_1_1987', 'iso_ir_100', 'l1', 'latin', 'latin1'] # mac_farsi [] # mac_roman ['macintosh', 'macroman'] # oem [] # raw_unicode_escape [] # tis_620 ['tis620', 'tis_620_0', 'tis_620_2529_0', 'tis_620_2529_1', 'iso_ir_166'] # utf_16_le ['unicodelittleunmarked', 'utf_16le'] # utf_7 ['u7', 'utf7', 'unicode_1_1_utf_7'] |
A Python hivatalos dokumentációban a codecs modul leírásánál az „Encodings and Unicode” alcím alatt találunk felsorolást a kódolások nevére alternatív nevekkel együtt. Ha a programunk eredményét tüzetesebben összehasonlítjuk a dokumentációban találhatókkal, akkor felfedezhetjük, hogy ott néhány nem szerepel. Pl. nem találjuk a dokumentációban a ‘mac_arabic’, ‘hp_roman8’, ‘mac_croatian’, ‘mac_farsi’, ‘mac_romanian’, és ’tis_620′ nevű karakterkódolásokat. Pedig, ha kipróbáljuk, akkor ezek is érvényesek és működnek. A nevük alapján pedig a neten utánanézhetünk, hogy mik ezek és mire használhatók.
Bár a létrehozott osztály definíciója nem tartalmaz sok kódsort, de a felhasznált nyelvi eszköztár meglehetősen széles. Mindezekről a Python tudásépítés lépésről lépésre című e-könyvben elsősorban a „Beépített konténerobjektumok”, „Műveletek”, „Panelprogram – modulok”, „ Készétel fogyasztás – a szabványos könyvtár moduljainak használata”, valamint a „Mentsük, ami menthető! – fájlok és mappák” fejezetekben lehet részleteiben olvasni.