Python中的字符串分段


92

我正在寻找“塞子”字符串的最佳方法,“塞子” 是什么,而我目前的解决方案基于此配方

我将其更改为:

s = 'String to slugify'

slug = unicodedata.normalize('NFKD', s)
slug = slug.encode('ascii', 'ignore').lower()
slug = re.sub(r'[^a-z0-9]+', '-', slug).strip('-')
slug = re.sub(r'[-]+', '-', slug)

有人看到此代码有任何问题吗?它工作正常,但也许我缺少某些东西,或者您知道更好的方法?


您正在大量使用unicode吗?如果是这样的话,如果将unicode()包裹起来,最后一个re.sub可能会更好,这就是django所做的。另外,可以将[^ a-z0-9] +缩短为使用\ w。参见django.template.defaultfilters,它与您的相近,但更加精致。
Mike Ramirez

URL中允许使用Unicode字符吗?另外,我将\ w更改为a-z0-9,因为\ w包含_字符和大写字母。字母被预先设置为小写,因此将没有大写字母匹配。
Zygimantas

'_'是有效的(但是您确实选择了,但您确实要问),unicode表示为百分比编码字符。
麦克拉米雷斯

谢谢迈克。好吧,我问了一个错误的问题。如果我们已经替换了除“ az”,“ 0-9”和“-”以外的所有字符,是否有任何理由将其编码回unicode字符串?
Zygimantas

对于django,我认为将所有字符串作为unicode对象对他们来说很重要,以实现兼容性。如果需要,这是您的选择。
迈克·拉米雷斯

Answers:


144

有一个名为的python程序包python-slugify,它可以很好地实现Slugifying:

pip install python-slugify

像这样工作:

from slugify import slugify

txt = "This is a test ---"
r = slugify(txt)
self.assertEquals(r, "this-is-a-test")

txt = "This -- is a ## test ---"
r = slugify(txt)
self.assertEquals(r, "this-is-a-test")

txt = 'C\'est déjà l\'été.'
r = slugify(txt)
self.assertEquals(r, "cest-deja-lete")

txt = 'Nín hǎo. Wǒ shì zhōng guó rén'
r = slugify(txt)
self.assertEquals(r, "nin-hao-wo-shi-zhong-guo-ren")

txt = 'Компьютер'
r = slugify(txt)
self.assertEquals(r, "kompiuter")

txt = 'jaja---lol-méméméoo--a'
r = slugify(txt)
self.assertEquals(r, "jaja-lol-mememeoo-a")

查看更多示例

该软件包比您发布的内容多一点(看看源代码,它只是一个文件)。该项目仍处于活动状态(在我最初回答的前两天进行了更新,超过了七年时间(最新检查时间为2020-06-30),但仍在更新中)。

注意:周围还有第二个包装,名为slugify。如果两者都使用,则可能会出现问题,因为它们具有相同的导入名称。刚命名的那个slugify并没有做我快速检查过的所有事情:"Ich heiße"变成了"ich-heie"(应该是"ich-heisse"),因此在使用pip或时一定要选择正确的一个easy_install


6
python-slugify已获得麻省理工学院(MIT)许可,但使用的Unidecode是GPL许可的产品,因此可能不适用于某些项目。
Rotareti

@Rotareti您能为我解释为什么它不能适合所有项目吗?我们不能使用MIT或GPL许可下的任何东西并将它们包含在商业软件中吗?我认为唯一的限制是除了我们开发的代码外,还必须将许可证。我错了吗?
加塞姆·托菲吉

1
简而言之:@GhassemTofighi:您可以在商业软件中使用它,但是如果使用它,则还必须开源代码。无论如何,这不是法律建议。
Rotareti

@GhassemTofighi可以看看关于该主题的softwareengineering.stackexchange.com/q/47032/71504
克拉滕科

1
@Rotareti python-slugify现在默认使用艺术许可证,text-unidecode而不是GPL许可证Unidecode,从而解决了您的许可证问题。github.com/un33k/python-slugify/commit/…–
Emilien

31

安装unidecode表单 从此处获得unicode支持

点安装unidecode

# -*- coding: utf-8 -*-
import re
import unidecode

def slugify(text):
    text = unidecode.unidecode(text).lower()
    return re.sub(r'[\W_]+', '-', text)

text = u"My custom хелло ворлд"
print slugify(text)

>>> my-custom-khello-vorld


1
嗨,这有点奇怪,但是它给了我类似“ my-custom-ndud-d-d3-4-d2d3-4nd-d-”的
回复

1
不发送unicode字符串时发生的@derevo。替换slugify("My custom хелло ворлд")slugify(u"My custom хелло ворлд"),它应该可以工作。
kratenko

9
我建议不要使用像这样的变量名str。这将隐藏内置str类型。
crodjer 2014年

2
unidecode是GPL,可能不适合某些人。
豪尔赫·雷涛2015年

重新整理或重新整理呢?
Ryan Chou



6

问题出在ascii归一化行:

slug = unicodedata.normalize('NFKD', s)

这被称为Unicode规范化,它不会将很多字符分解为ASCII。例如,它将从以下字符串中去除非ASCII字符:

Mørdag -> mrdag
Æther -> ther

更好的方法是使用unidecode模块,该模块尝试将字符串音译为ascii。因此,如果将以上行替换为:

import unidecode
slug = unidecode.unidecode(s)

对于上述字符串以及许多希腊语和俄语字符,您会获得更好的结果:

Mørdag -> mordag
Æther -> aether

5
def slugify(value):
    """
    Converts to lowercase, removes non-word characters (alphanumerics and
    underscores) and converts spaces to hyphens. Also strips leading and
    trailing whitespace.
    """
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
    value = re.sub('[^\w\s-]', '', value).strip().lower()
    return mark_safe(re.sub('[-\s]+', '-', value))
slugify = allow_lazy(slugify, six.text_type)

这是django.utils.text中存在的Slugify函数。这足以满足您的要求。



2

GitHub上的几个选项:

  1. https://github.com/dimka665/awesome-slugify
  2. https://github.com/un33k/python-slugify
  3. https://github.com/mozilla/unicode-slugify

每个API的API支持略有不同的参数,因此您需要仔细检查以找出自己喜欢的参数。

特别是,请注意它们提供的用于处理非ASCII字符的不同选项。Pydanny写了一篇非常有用的博客文章,说明了这些slugifying库中的unicode处理差异:http ://www.pydanny.com/awesome-slugify-human-可读-url-slugs-from-any-string.html 这篇博客文章有些过时了,因为Mozilla unicode-slugify不再是特定于Django的。

还要注意,当前awesome-slugify是GPLv3,尽管存在一个开放的问题,作者说他们希望以MIT / BSD的形式发布,只是不确定其合法性:https : //github.com/dimka665/awesome-slugify/issues/ 24


1

您可以考虑将最后一行更改为

slug=re.sub(r'--+',r'-',slug)

由于模式[-]+与没什么不同-+,并且您实际上并不在乎只匹配一个连字符,也不希望匹配两个或多个。

但是,当然,这是相当小的。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.