Normalización de texto Unicode a nombres de archivo, etc. en Python

¿Existe alguna solución estándar para normalizar el texto internacional de Unicode a las ID y los nombres de archivo seguros en Python?

Por ejemplo, gire My International Text: åäö a my-international-text-aao

plone.i18n hace realmente un buen trabajo, pero desafortunadamente depende de zope.security y zope.publisher y algunos otros paquetes que lo hacen frágil dependencia.

Algunas operaciones que aplica plone.i18n

    Lo que quieres hacer también se conoce como “slugify” una cadena. Aquí hay una posible solución:

     import re from unicodedata import normalize _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.:]+') def slugify(text, delim=u'-'): """Generates an slightly worse ASCII-only slug.""" result = [] for word in _punct_re.split(text.lower()): word = normalize('NFKD', word).encode('ascii', 'ignore') if word: result.append(word) return unicode(delim.join(result)) 

    Uso:

     >>> slugify(u'My International Text: åäö') u'my-international-text-aao' 

    También puedes cambiar el delimitador:

     >>> slugify(u'My International Text: åäö', delim='_') u'my_international_text_aao' 

    Fuente: Generating Slugs

    Para Python 3: pastebin.com/ft7Yb3KS (gracias @MrPoxipol ).

    La forma de resolver este problema es tomar una decisión sobre qué caracteres se permiten (los diferentes sistemas tienen diferentes reglas para identificadores válidos).

    Una vez que decida qué caracteres están permitidos, escriba un predicado permitido () y una subclase dict para usar con str.translate :

     def makesafe(text, allowed, substitute=None): ''' Remove unallowed characters from text. If *substitute* is defined, then replace the character with the given substitute. ''' class D(dict): def __getitem__(self, key): return key if allowed(chr(key)) else substitute return text.translate(D()) 

    Esta función es muy flexible. Le permite especificar fácilmente las reglas para decidir qué texto se conserva y qué texto se reemplaza o elimina.

    Aquí hay un ejemplo simple usando la regla, “solo se permiten los caracteres que están en la categoría L de Unicode”:

     import unicodedata def allowed(character): return unicodedata.category(character).startswith('L') print(makesafe('the*ides&of*march', allowed, '_')) print(makesafe('the*ides&of*march', allowed)) 

    Ese código produce una salida segura de la siguiente manera:

     the_ides_of_march theidesofmarch 

    Lo siguiente eliminará los acentos de cualquier carácter que Unicode pueda descomponer en pares combinados, descartar cualquier carácter extraño que no pueda, y destruir espacios en blanco:

     # encoding: utf-8 from unicodedata import normalize import re original = u'ľ š č ť ž ý á í é' decomposed = normalize("NFKD", original) no_accent = ''.join(c for c in decomposed if ord(c)<0x7f) no_spaces = re.sub(r'\s', '_', no_accent) print no_spaces # output: l_s_c_t_z_y_a_i_e 

    No intenta deshacerse de los caracteres no permitidos en los sistemas de archivos, pero puedes robar DANGEROUS_CHARS_REGEX del archivo que vinculaste para eso.

    Voy a lanzar mi propia solución (parcial) aquí también:

     import unicodedata def deaccent(some_unicode_string): return u''.join(c for c in unicodedata.normalize('NFD', some_unicode_string) if unicodedata.category(c) != 'Mn') 

    Esto no hace todo lo que quiere, pero le brinda algunos trucos unicode.normalise('NFD', some_unicode_string) envueltos en un método conveniente: unicode.normalise('NFD', some_unicode_string) realiza una descomposición de caracteres Unicode, por ejemplo, rompe ‘ä’ en dos puntos de código Unicode. U+03B3 y U+0308 .

    El otro método, unicodedata.category(char) , devuelve la categoría de caracteres enicode para ese char particular. La categoría Mn contiene todos los acentos de combinación, por lo tanto, deaccent elimina todos los acentos de las palabras.

    Pero tenga en cuenta que esto es solo una solución parcial, se deshace de los acentos. Aún necesita algún tipo de lista blanca de caracteres que desee permitir después de esto.

    Yo iria con

    https://pypi.python.org/pypi?%3Aaction=search&term=slug

    Es difícil encontrar un escenario donde uno de estos no se ajuste a sus necesidades.