Domanda

In primo luogo, alcuni retroscena: Sto sviluppando un'applicazione web utilizzando Python. Tutti i file di miei (testo) sono attualmente conservati in UTF-8 con la distinta. Questo include tutti i miei modelli HTML e file CSS. Queste risorse sono memorizzate come dati binari (BOM e tutti) nel mio DB.

Quando ho recuperare i modelli dalla DB, decodifico utilizzando template.decode('utf-8'). Quando l'HTML arriva nel browser, la distinta base è presente all'inizio del corpo della risposta HTTP. Questo genera un errore molto interessante in Chrome:

Extra <html> encountered. Migrating attributes back to the original <html> element and ignoring the tag.

Chrome sembra generare automaticamente un tag <html> quando vede la distinta ed eventuali errori per i contenuti, rendendo il vero tag <html> un errore.

Quindi, utilizzando Python, qual è il modo migliore per rimuovere la distinta dai miei UTF-8 modelli codificati? (Se esiste - non posso garantire in futuro)

Per gli altri file di testo basati su come CSS, saranno i principali browser interpretare correttamente (o ignorare) la distinta base? Essi vengono inviati come dati binari semplici, senza .decode('utf-8').

Nota:. Sto usando Python 2.5

Grazie!

È stato utile?

Soluzione

Dal momento che lo stato:

  

Tutti i file miei (testo) sono attualmente   memorizzato in UTF-8 con il BOM

quindi utilizzare il codec 'utf-8-SIG' per decodificare loro:

>>> s = u'Hello, world!'.encode('utf-8-sig')
>>> s
'\xef\xbb\xbfHello, world!'
>>> s.decode('utf-8-sig')
u'Hello, world!'

Si rimuove automaticamente l'atteso distinta base, e funziona correttamente se la distinta non è presente pure.

Altri suggerimenti

Controlla il primo carattere dopo la decodifica per vedere se è il BOM:

if u.startswith(u'\ufeff'):
  u = u[1:]

La risposta precedentemente accettata è sbagliato.

u'\ufffe' non è un personaggio. Se si ottiene in una stringa unicode qualcuno ha farcito con potenza.

La distinta base (aka ZERO WIDTH no-break SPACE) è u'\ufeff'

>>> UNICODE_BOM = u'\N{ZERO WIDTH NO-BREAK SPACE}'
>>> UNICODE_BOM
u'\ufeff'
>>>

questo (Ctrl-F ricerca di BOM) e questo e questo (Ctrl-F ricerca di BOM).

Ecco un errore di battitura / risposta corretta e Braino-resistente:

decifrare il vostro input in unicode_str. Poi fare questo:

# If I mistype the following, it's very likely to cause a SyntaxError.
UNICODE_BOM = u'\N{ZERO WIDTH NO-BREAK SPACE}'
if unicode_str and unicode_str[0] == UNICODE_BOM:
    unicode_str = unicode_str[1:]

Bonus: utilizzando un nome costante dà ai vostri lettori un po 'più di un indizio per quello che sta succedendo di quanto non faccia una raccolta di hexoglyphics apparentemente arbitrari

.

Aggiorna Purtroppo non sembra essere non adatti chiamato costante nella libreria standard di Python.

Purtroppo, il modulo codecs prevede solo "una trappola e una delusione":

>>> import pprint, codecs
>>> pprint.pprint([(k, getattr(codecs, k)) for k in dir(codecs) if k.startswith('BOM')])
[('BOM', '\xff\xfe'),   #### aarrgghh!! ####
 ('BOM32_BE', '\xfe\xff'),
 ('BOM32_LE', '\xff\xfe'),
 ('BOM64_BE', '\x00\x00\xfe\xff'),
 ('BOM64_LE', '\xff\xfe\x00\x00'),
 ('BOM_BE', '\xfe\xff'),
 ('BOM_LE', '\xff\xfe'),
 ('BOM_UTF16', '\xff\xfe'),
 ('BOM_UTF16_BE', '\xfe\xff'),
 ('BOM_UTF16_LE', '\xff\xfe'),
 ('BOM_UTF32', '\xff\xfe\x00\x00'),
 ('BOM_UTF32_BE', '\x00\x00\xfe\xff'),
 ('BOM_UTF32_LE', '\xff\xfe\x00\x00'),
 ('BOM_UTF8', '\xef\xbb\xbf')]
>>>

Aggiorna 2 Se non avete ancora decodificato l'input, e il desiderio di controllare la presenza di una distinta base, è necessario verificare la presenza di DUE diverse distinte base per UTF-16 e almeno due diverse distinte materiali per UTF-32. Se ci fosse un solo modo ciascuno, allora non avrebbe bisogno di una distinta base, vero?

Ecco testualmente unprettified dal mio codice è la mia soluzione a questo:

def check_for_bom(s):
    bom_info = (
        ('\xFF\xFE\x00\x00', 4, 'UTF-32LE'),
        ('\x00\x00\xFE\xFF', 4, 'UTF-32BE'),
        ('\xEF\xBB\xBF',     3, 'UTF-8'),
        ('\xFF\xFE',         2, 'UTF-16LE'),
        ('\xFE\xFF',         2, 'UTF-16BE'),
        )
    for sig, siglen, enc in bom_info:
        if s.startswith(sig):
            return enc, siglen
    return None, 0

Il s ingresso dovrebbe essere almeno i primi 4 byte del vostro ingresso. Esso restituisce la codifica che può essere utilizzato per decodificare la parte post-BOM del vostro input, più la lunghezza della distinta base (se presente).

Se siete paranoici, si potrebbe consentire per altri 2 (non standard) UTF-32 ordinamenti, ma Python non fornisce una codifica per loro e non ho mai sentito parlare di un effettivo verificarsi, quindi non fastidio.

Si può usare qualcosa di simile per rimuovere BOM:

import os, codecs
def remove_bom_from_file(filename, newfilename):
    if os.path.isfile(filename):
        # open file
        f = open(filename,'rb')

        # read first 4 bytes
        header = f.read(4)

        # check if we have BOM...
        bom_len = 0
        encodings = [ ( codecs.BOM_UTF32, 4 ),
            ( codecs.BOM_UTF16, 2 ),
            ( codecs.BOM_UTF8, 3 ) ]

        # ... and remove appropriate number of bytes    
        for h, l in encodings:
            if header.startswith(h):
                bom_len = l
                break
        f.seek(0)
        f.read(bom_len)

        # copy the rest of file
        contents = f.read() 
        nf = open(newfilename)
        nf.write(contents)
        nf.close()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top