2012-01-28 12 views
20

¿Existe alguna solución estándar para normalizar el texto unicode internacional a identificadores y nombres de archivo seguros en Python?Normalización de texto unicode a nombres de archivo, etc. en Python

E.g. My International Text: åäö vuelta a my-international-text-aao

plone.i18n hace muy buen trabajo, pero por desgracia, depende de zope.security y zope.publisher y algunos otros paquetes de lo que es la dependencia frágil.

Some operations that plone.i18n applies

+2

"My International Text: åäö" es un nombre de archivo perfectamente válido en todos los sistemas que uso, por lo que es posible que desee ser un poco más específico. Por ejemplo, ¿qué personajes quieres (des) permitir? –

+2

@LaurenceGonsalves Podría ser perfectamente válido, pero eso no significa que sobrevivirá necesariamente a un servidor web/navegador web/combo web particular cuando se descarga. Cuando llega ese informe de errores, generalmente es más rápido simplemente despojar los acentos que tratar de descubrir dónde radica el problema. – millimoose

+2

posible duplicado de [¿Cuál es la mejor manera de eliminar acentos en una cadena unicode de python?] (Http://stackoverflow.com/questions/517923/what-is-the-best-way-to-remove-accents-in -a-python-unicode-string) – Johnsyweb

Respuesta

33

Lo que se quiere también se conoce como hacer "slugify" una cadena. He aquí una posible solución:

import re 
from unicodedata import normalize 

_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>[email protected]\[\\\]^_`{|},.:]+') 

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 puede 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).

+1

¿alguien puede decirme por qué me dieron downvoted? (Hombre, odio a los "downvoters anónimos") – juliomalegria

+0

Muchos nombres para el mismo procedimiento :) Gracias por una muy buena respuesta. –

+2

Yo también odio votar negativamente sin comentar para explicar por qué votaste negativamente. Por favor siempre explique sus votos abajo. – aclark

2

El siguiente eliminará los acentos de cualquier caracteres Unicode puede descomponerse en la combinación de pares, descartar los caracteres extraños que no pueden, y un espacio en blanco nuclear:

# 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 trata de deshacerse de caracteres no permitidos en los sistemas de archivos, pero puede robar DANGEROUS_CHARS_REGEX del archivo que ha vinculado para eso.

5

La manera de resolver este problema es hacer una decisión en la que se permiten caracteres (diferentes sistemas tienen diferentes reglas para los identificadores válidos.

Una vez que decida sobre la que se permiten caracteres, escribir un permitido() predicado y una subclase dict para su uso 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 Nos dejó usted especificar fácilmente reglas para decidir qué texto se mantiene y qué texto es o bien reemplazado o retirado..

Aquí está un ejemplo sencillo usando la regla "sólo permiten caracteres que se encuentran en la categoría Unicode L":

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 la salida segura de la siguiente manera:

the_ides_of_march 
theidesofmarch 
+0

Tener un sustituto sea una función del carácter no permitido lo haría más flexible. Considere, por ejemplo, una palabra finlandesa perfectamente válida hääyöaie, y cómo sería abusada a algo como hyaie o h-y-aie con su mecanismo de sustitución actual. –

2

voy arrojar 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 quieras, pero da unos pocos ce tricks envueltos en un método de conveniencia: unicode.normalise('NFD', some_unicode_string) hace una descomposición de caracteres Unicode, por ejemplo, rompe 'ä' 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 particular char. La categoría Mn contiene todos los acentos combinados, por lo que deaccent elimina todos los acentos de las palabras.

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

Cuestiones relacionadas