在 Python 中将 unicode 文本规范化为文件名等

Posted

技术标签:

【中文标题】在 Python 中将 unicode 文本规范化为文件名等【英文标题】:Normalizing unicode text to filenames, etc. in Python 【发布时间】:2012-02-20 23:40:34 【问题描述】:

是否有任何独立的解决方案可以将国际 unicode 文本标准化为 Python 中的安全 id 和文件名?

例如把My International Text: åäö转成my-international-text-aao

plone.i18n 确实做得很好,但不幸的是它依赖于zope.securityzope.publisher 以及其他一些使其脆弱的依赖包。

Some operations that plone.i18n applies

【问题讨论】:

“我的国际文本:åäö”在我使用的所有系统上都是一个完全有效的文件名,因此您可能想要更具体一点。例如,您究竟想要(禁止)哪些字符? @LaurenceGonsalves 这可能是完全有效的,但这并不意味着它在下载时一定会在特定的网络服务器/网络浏览器/网络操作系统组合中存活下来。当该错误报告到达时,去除重音通常比试图找出问题所在更快。 What is the best way to remove accents in a python unicode string?的可能重复 看看unidecode(slugify(u'My International Text: åäö'))是如何实现的[忽略django依赖它对于Unicode输入没有必要]。 【参考方案1】:

你想要做的也被称为“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'

来源: Generating Slugs

对于 Python 3: pastebin.com/ft7Yb3KS(感谢 @MrPoxipol)。

【讨论】:

有人能告诉我为什么我被否决了吗? (伙计,我讨厌那些“匿名投票者”) 同一个过程有很多名字 :) 谢谢你的回答。 我也讨厌在不评论的情况下拒绝投票以解释您拒绝投票的原因。请始终解释您的反对意见。 此解决方案(与其他解决方案一样)不处理无法通过 Unicode 规范化分解的字符,例如 ß 或 –。【参考方案2】:

以下内容将从 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

【讨论】:

【参考方案3】:

解决这个问题的方法是决定允许哪些字符(不同的系统对有效标识符有不同的规则。

一旦您决定了允许使用哪些字符,就编写一个 allowed() 谓词和一个 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

【讨论】:

让替代品成为不允许的字符的功能将使这更加灵活。例如,考虑一个完全有效的芬兰语单词 hääyöaie,以及如何使用您当前的替换机制将其转换为 hyaie 或 h-y-aie。【参考方案4】:

我也会在这里提出我自己的(部分)解决方案:

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+03B3U+0308.

另一种方法unicodedata.category(char) 返回该特定char 的编码字符类别。类别Mn 包含所有重音组合,因此deaccent 会删除单词中的所有重音。

但请注意,这只是部分解决方案,它消除了重音。在此之后,您仍然需要某种要允许的字符白名单。

【讨论】:

【参考方案5】:

我会去的

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

很难想出其中一个不符合您的需求的场景。

【讨论】:

以上是关于在 Python 中将 unicode 文本规范化为文件名等的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 2012 中将两个 nvarchar 列与 Unicode 文本进行比较

如何在 R Windows 中将 Unicode 字符串写入文本文件?

在 C# 中将 HTML 实体转换为 Unicode 字符

在python 3中将表情符号转换为Unicode,反之亦然

在python中将Unicode数据转换为int

在 Python 中将 XML/HTML 实体转换为 Unicode 字符串 [重复]