2012-01-28 15 views
20

Czy istnieją jakieś standalansowe rozwiązania do normalizowania międzynarodowego tekstu Unicode na bezpieczne identyfikatory i nazwy plików w języku Python?Normalizowanie tekstu w Unicode do nazw plików itp. W języku Python

E.g. skręcić My International Text: åäö do my-international-text-aao

plone.i18n robi naprawdę dobrą robotę, ale niestety to zależy zope.security i zope.publisher i niektórych innych pakietów składających że jest nietrwały zależność.

Some operations that plone.i18n applies

+2

"Mój międzynarodowy tekst: åäö" to całkowicie poprawna nazwa pliku we wszystkich systemach, z których korzystam, więc możesz chcieć być nieco bardziej konkretny. Na przykład, na jakie dokładnie postacie chcesz (dis) pozwolić? –

+2

@LaurenceGonsalves Może to być całkowicie poprawne, ale to nie znaczy, że przeżyje konkretny serwer sieciowy/przeglądarka internetowa/system operacyjny OS podczas pobierania. Kiedy pojawia się ten raport o błędzie, zwykle szybciej jest po prostu rozebrać akcenty, niż spróbować ustalić, gdzie leży problem. – millimoose

+2

możliwy duplikat [Jaki jest najlepszy sposób na usunięcie akcentów w ciągu Pythona Unicode?] (Http://stackoverflow.com/questions/517923/what-is-the-best-way-to-remove-accents-in -a-python-unicode-string) – Johnsyweb

Odpowiedz

33

Co chcesz zrobić, jest również znany jako „slugify” ciąg. Oto możliwe rozwiązanie:

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

Zastosowanie:

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

Można również zmienić separatorem:

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

Źródło:Generating Slugs

dla Pythona 3:pastebin.com/ft7Yb3KS (dzięki @MrPoxipol).

+1

Czy ktoś może mi powiedzieć, dlaczego otrzymałem obniżkę? (człowieku, nienawidzę tych "anonimowych spadochroniarzy") – juliomalegria

+0

Wiele nazw dla tej samej procedury :) Dziękuję za bardzo dobrą odpowiedź. –

+2

Ja też nienawidzę głosowania bez komentarza, aby wyjaśnić, dlaczego przegłosowano. Proszę zawsze tłumaczyć swoje głosowanie. – aclark

2

Poniższa usunie akcenty z cokolwiek znaków Unicode mogą rozkładają się łączenie par, odrzucić wszelkie dziwne znaki nie mogą i nuke spacje:

# 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 

To nie próbuje pozbyć się znaków niedozwolonych na systemach plików, ale można ukraść DANGEROUS_CHARS_REGEX z pliku, który do niego linkował.

5

Sposobem na rozwiązanie tego problemu jest do podjęcia decyzji, w którym znaki są dozwolone (różne systemy mają różne zasady ważnych identyfikatorów.

Po zdecydować na jakie znaki są dozwolone, napisać dozwolony() orzecznik i podklasy dict do użytku z 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()) 

funkcja ta jest bardzo elastyczna to niech Ci łatwo określić zasady podejmowania decyzji, które są przechowywane i tekst, który tekst jest albo zastąpione lub usunięte..

Oto prosty przykład stosując regułę „pozwalają jedynie znaki, które są w Unicode kategorii 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)) 

Ten kod produkuje bezpieczne wyjście następująco:

the_ides_of_march 
theidesofmarch 
+0

Posiadanie zamiennika może być funkcją niedozwolonej postaci, co czyni go bardziej elastycznym. Weźmy na przykład doskonale ważne fińskie słowo hääyöaie, i jak będzie ono molestowane do czegoś takiego jak hyaie lub h - y-aie z twoim obecnym mechanizmem zastępowania. –

2

będę wrzuć tu także moje własne (częściowe) rozwiązanie:

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

To nie robi wszystkiego, co chcesz, ale daje kilka ni ce triki zawinięte w wygodną metodę: unicode.normalise('NFD', some_unicode_string) dokonuje dekompozycji znaków unicode, na przykład łamie "ä" na dwa unikodowe punkty kodowe U+03B3 i U+0308.

Druga metoda, unicodedata.category(char), zwraca kategorię znaków enicode dla tego konkretnego char. Kategoria Mn zawiera wszystkie kombinujące akcenty, dzięki czemu deaccent usuwa wszystkie akcenty ze słów.

Należy jednak pamiętać, że jest to tylko częściowe rozwiązanie, pozbawia się akcentów. Nadal potrzebujesz jakiejś białej listy znaków, które chcesz dopuścić po tym.

Powiązane problemy