file UTF-8 HTML e CSS con BOM (e come rimuovere il BOM con Python)
-
20-09-2019 - |
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!
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()