fichiers UTF-8 HTML et CSS avec la nomenclature (et comment supprimer la nomenclature avec Python)

StackOverflow https://stackoverflow.com/questions/2456380

Question

D'abord un bref rappel historique: Je développe une application web en utilisant Python. Tous mes fichiers (texte) sont actuellement stockés en UTF-8 avec la nomenclature. Cela inclut tous mes modèles HTML et les fichiers CSS. Ces ressources sont stockées sous forme de données binaires (BOM et tous) dans mon DB.

Quand je récupère les modèles de la DB, je les décode en utilisant template.decode('utf-8'). Lorsque le HTML arrive dans le navigateur, la nomenclature est présente au début du corps de réponse HTTP. Cela génère une erreur très intéressant dans Chrome:

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

Chrome semble générer une balise <html> automatiquement quand il voit la nomenclature et prend pour le contenu, ce qui rend la vraie balise <html> une erreur.

Ainsi, en utilisant Python, quelle est la meilleure façon d'enlever la nomenclature de mes UTF-8 modèles codés? (Si elle existe - je ne peux pas garantir à l'avenir)

Pour d'autres fichiers texte comme CSS, seront les principaux navigateurs interpréter correctement (ou ignorer) la nomenclature? Ils sont envoyés sous forme de données binaires simples sans .decode('utf-8').

Note:. J'utilise Python 2.5

Merci!

Était-ce utile?

La solution

Puisque vous État:

  

Tous mes fichiers (texte) sont actuellement   stocké dans UTF-8 avec la nomenclature

puis utilisez le codec 'utf-8-sig' pour les décoder:

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

Il supprime automatiquement la nomenclature prévue et fonctionne correctement si la nomenclature est présente pas aussi bien.

Autres conseils

Vérifier le premier caractère après décodage pour voir si elle est la nomenclature:

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

La réponse précédemment acceptée est MAUVAIS.

u'\ufffe' est pas un caractère. Si vous l'obtenez dans une chaîne de quelqu'un unicode a bouché avec force.

La nomenclature (aka ZERO WIDTH ESPACE SANS COUPURE) est u'\ufeff'

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

cette (recherche Ctrl-F pour BOM) et cette et cette (recherche Ctrl-F pour BOM).

Voici une réponse correcte et faute de frappe / résistant à braino:

Décoder votre entrée en unicode_str. Ensuite, faites ceci:

# 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: en utilisant une constante nommée donne à vos lecteurs un peu plus d'un indice de ce qui se passe que fait une collection de hexoglyphics apparemment arbitraires

.

Mise à jour Malheureusement, il ne semble pas approprié constante nommée dans la bibliothèque standard de Python.

Hélas, le module codecs ne fournit que "un piège et une illusion":

>>> 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')]
>>>

Mise à jour 2 Si vous n'êtes pas encore décodé votre entrée, et que vous souhaitez vérifier pour une nomenclature, vous devez vérifier DEUX différents BOM UTF-16 et au moins DEUX différents pour BOM UTF-32. S'il n'y avait qu'une seule façon chacun, alors vous pas besoin d'une nomenclature, vous?

Ici unprettified mot pour mot de mon code est ma solution à ceci:

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

Le s d'entrée doit être au moins les 4 premiers octets de votre entrée. Il retourne le codage qui peut être utilisé pour décoder la partie post-nomenclature de votre entrée, plus la longueur de la nomenclature (le cas échéant).

Si vous êtes paranoïaque, vous pouvez permettre une autre 2 (non standard) UTF-32 ordonnancements, mais Python ne fournit pas un codage pour eux et je n'ai jamais entendu parler d'un événement réel, donc je ne déranger.

Vous pouvez utiliser quelque chose de similaire pour supprimer la nomenclature:

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()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top