Python - mixed charset decoder hack

Idonkent meg kell oldani vegyes kodolasu (tipikusan reszben utf8 reszben latin1/latin2) szovegfileok ertelmes megjeleniteset.

A python decode() elegge erzekeny, minden hibara exceptiont dob, bar ra lehet errors="ignore" es tarsaival venni hogy ne dobjon, de a kimenet attol meg ritkan lesz hasznalhato. Anno valahol talaltam (talan stackoverflow-on?) egy hacket hogy lehet irni sajat hibakezelest a decode()-hoz es ott ki lehet talalni, hogy az utf8-kent nem ertelmezheto byteokkal mihez kezdjunk. Ebbol kiindulva sok kiserletezessel az alabbit hoztam ossze evek alatt, ez mar ugy az esetek nagyon nagy tobbsegeben jol mukodik:

# based on:  /usr/lib/python3.10/html/__init__.py
invalid_charrefs = {
    0x00: ' ',        # REPLACEMENT CHARACTER
    0x80: '\u20ac',  # EURO SIGN
    0x81: '',        # <control>
    0x82: '\u201a',  # SINGLE LOW-9 QUOTATION MARK
    0x83: '\u0192',  # LATIN SMALL LETTER F WITH HOOK
    0x84: '\u201e',  # DOUBLE LOW-9 QUOTATION MARK
    0x85: '\u2026',  # HORIZONTAL ELLIPSIS
    0x86: '\u2020',  # DAGGER
    0x87: '\u2021',  # DOUBLE DAGGER
    0x88: '\u02c6',  # MODIFIER LETTER CIRCUMFLEX ACCENT
    0x89: '\u2030',  # PER MILLE SIGN
    0x8a: '\u0160',  # LATIN CAPITAL LETTER S WITH CARON
    0x8b: '\u2039',  # SINGLE LEFT-POINTING ANGLE QUOTATION MARK
    0x8c: '\u0152',  # LATIN CAPITAL LIGATURE OE
    0x8d: '',        # <control>
    0x8e: '\u017d',  # LATIN CAPITAL LETTER Z WITH CARON
    0x8f: '',        # <control>
    0x90: '',        # <control>
    0x91: '\u2018',  # LEFT SINGLE QUOTATION MARK
    0x92: '\u2019',  # RIGHT SINGLE QUOTATION MARK
    0x93: '\u201c',  # LEFT DOUBLE QUOTATION MARK
    0x94: '\u201d',  # RIGHT DOUBLE QUOTATION MARK
    0x95: '\u2022',  # BULLET
    0x96: '\u2013',  # EN DASH
    0x97: '\u2014',  # EM DASH
    0x98: '\u02dc',  # SMALL TILDE
    0x99: '\u2122',  # TRADE MARK SIGN
    0x9a: '\u0161',  # LATIN SMALL LETTER S WITH CARON
    0x9b: '\u203a',  # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
    0x9c: '\u0153',  # LATIN SMALL LIGATURE OE
    0x9d: '',        # <control>
    0x9e: '\u017e',  # LATIN SMALL LETTER Z WITH CARON
    0x9f: '\u0178',  # LATIN CAPITAL LETTER Y WITH DIAERESIS
    0xA0: ' ',       # &nbsp Unicode Character 'NO-BREAK SPACE' (U+00A0)
    0xAD: '',        # &shy SOFT HYPHEN  https://stackoverflow.com/questions/34835786/what-is-shy-and-how-do-i-get-rid-of-it
    # csak ezek ternek el a magyar abc-ben a latin1 es latin2 kozott, inkabb a latin2-eset hasznaljuk ezekbol:
    0xD5: '\u0150',  # O~ => O"
    0xDB: '\u0170',  # U^ => U"
    0xF5: '\u0151',  # o~ => o"
    0xFB: '\u0171',  # u^ => u"
}

def mixed_decoder(unicode_error):
    position = unicode_error.start
    new_char = unicode_error.object[position] # csak 1 byte kell!
    new_char = invalid_charrefs.get(new_char, chr(new_char))
    return new_char, position + 1

codecs.register_error("mixed", mixed_decoder)

Ezutan igy lehet hasznalni:  text=bindata.decode("utf-8",errors="mixed")

Ahogy latszik a fenti mapping tabla a kulcs, ebben sokat modositgattam az eredetihez kepest, pl. az nbsp csereje sima space-re.

De ami erdekes, hogy a unicode 128..255 karakterkodok megegyeznek a latin-1 kodokkal, ehhez kepest a fobb elteresek, hogy a 128..160 kozotti kontrol karakterek helyett a webes vilagban hasznalatos kodokra (xml charref) cserelem, pl. 0x80 = EURO jel stb. Valamint a magyar ABC-ben osszesen 4 betu ter el a latin1 es latin2 kodolasban (őűŐŰ) ezekbol a latin2-est hasznalom, mert tobbsegeben magyar szovegekre kellett, de ahol nincs megadva (vagy hibas) a kodlap, azok a szovegek viszont tobbsegeben latin1 kodolasuak voltak. Szoval ez igy kicsit "gany", mert kavar az utf8, latin1 es latin2, valamint az xmlcharref kozott, de sok teszteles es finomhangolas eredmenye, a gyakorlatban pont igy mukodik jol :)

Hozzászólások

Első blikkre Inkább windows-1250(2)-nek tűnik, de a magyar betűk szempontjából az is jó.