Нормализация текста в юникоде в имена файлов и т. Д. В Python - PullRequest
20 голосов
/ 28 января 2012

Существуют ли какие-либо автономные решения для нормализации международного текста Юникода в безопасных идентификаторах и именах файлов в Python?

Например, поворот My International Text: åäö на my-international-text-aao

plone.i18n делает действительно хорошую работу, но, к сожалению, это зависит от zope.security и zope.publisher и некоторых других пакетов, делающих его хрупкой зависимостью.

Некоторые операции, к которым применяется plone.i18n

Ответы [ 5 ]

33 голосов
/ 28 января 2012

То, что вы хотите сделать, также известно как «slugify» строка.Вот возможное решение:

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

Использование:

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

Вы также можете изменить разделитель:

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

Источник: Генерация слагов

Для Python 3: pastebin.com / ft7Yb3KS (спасибо @ MrPoxipol ).

4 голосов
/ 28 января 2012

Способ решения этой проблемы заключается в принятии решения о том, какие символы разрешены (разные системы имеют разные правила для допустимых идентификаторов.

После того, как вы решите, какие символы разрешены, напишите разрешено() предикат и подкласс dict для использования с 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())

Эта функция очень гибкая. Она позволяет легко задавать правиладля принятия решения, какой текст сохранить, а какой текст заменить или удалить.

Вот простой пример, использующий правило «разрешать только символы из категории 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))

Этот код производит безопасный вывод следующим образом:

the_ides_of_march
theidesofmarch
2 голосов
/ 28 января 2012

Я добавлю здесь и свое (частичное) решение:

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

Это не делает все, что вы хотите, но дает несколько приятных трюков, заключенных в удобный метод: unicode.normalise('NFD', some_unicode_string) выполняет декомпозицию символов Unicode, например, разбивает 'ä' на две кодовые точки Unicode U+03B3 U+0308.

Другой метод, unicodedata.category(char), возвращает категорию символов enicode для этого конкретного char. Категория Mn содержит все сочетания акцентов, поэтому deaccent удаляет все акценты в словах.

Но учтите, что это только частичное решение, оно избавляет от акцентов. Вам все еще нужен какой-то белый список символов, который вы хотите разрешить после этого.

2 голосов
/ 28 января 2012

Следующее удалит акценты из любых символов, которые Unicode может разложить на объединяющие пары, отбросит любые странные символы, которые он не может, и уничтожит пробелы:

# 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

Он не пытается избавиться от символов, запрещенных в файловых системах, но вы можете украсть DANGEROUS_CHARS_REGEX из файла, с которым вы связались.

0 голосов
/ 21 марта 2015

Я бы пошел с

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

Сложно придумать сценарий, когда один из них не соответствует вашим потребностям.

...