用于生成漂亮URL的Java代码/库的“slug”生成器

46

像Rails和Django这样的Web框架内置了对“slug”的支持,用于生成可读性高且友好的SEO URL:

一个slug字符串通常仅包含字符a-z0-9-,因此可以在不需要URL转义(think "foo%20bar")的情况下编写。

我正在寻找一种Java slug函数,它能够接受任何有效的Unicode字符串并返回一个slug表示形式(a-z0-9-)。

一个简单的slug函数可能是这样的:

return input.toLowerCase().replaceAll("[^a-z0-9-]", "");

然而,这种实现方式无法处理国际化和重音符号(ë会被转换成e)。解决这个问题的一种方法是列举所有特殊情况,但那并不太优雅。我正在寻求更加周全和通用的方法。

我的问题是:

  • 在Java中生成类似于Django/Rails的slug,什么是最常见/实用的方法?
4个回答

58

使用规范分解来规范化您的字符串:

  private static final Pattern NONLATIN = Pattern.compile("[^\\w-]");
  private static final Pattern WHITESPACE = Pattern.compile("[\\s]");

  public static String toSlug(String input) {
    String nowhitespace = WHITESPACE.matcher(input).replaceAll("-");
    String normalized = Normalizer.normalize(nowhitespace, Form.NFD);
    String slug = NONLATIN.matcher(normalized).replaceAll("");
    return slug.toLowerCase(Locale.ENGLISH);
  }

这仍然是一个相当朴素的过程。它不会对带有s-sharp(ß - 用于德语)或任何非拉丁字母表(希腊、西里尔、CJK等)的文字起作用。

在更改字符串大小写时要小心。大小写形式取决于字母表。在土耳其语中,U+0069 (i) 的大写形式是 U+0130 (İ),而不是 U+0049 (I),因此,如果您在土耳其语环境下使用 String.toLowerCase(),就有可能将非 Latin1 字符引入到字符串中。


1
看起来很有前途,但是规范化似乎不起作用:“fóòbâr”被翻译成“fbr”,而不是预期的“foobar”。你知道为什么吗? - knorv
2
奇怪的是,当我将字符串“f\u00F3\u00F2b\u00e2r”传递给该方法时,我得到了“foobar”。也许您在源代码或数据文件中出现了编码错误,请参阅http://illegalargumentexception.blogspot.com/2009/05/java-rough-guide-to-character-encoding.html。 - McDowell
麦克道尔:你说得完全正确——这是一个编码错误。感谢你提供的出色答案! - knorv

18

到目前为止,这是最好的且可扩展的解决方案。 - Ankur

12

麦克道尔的建议几乎可行,但在像这样的情况下Hello World !!,它返回hello-world--(注意字符串末尾的--),而不是hello-world

修正版可以是:

private static final Pattern NONLATIN = Pattern.compile("[^\\w-]");
private static final Pattern WHITESPACE = Pattern.compile("[\\s]");
private static final Pattern EDGESDHASHES = Pattern.compile("(^-|-$)");

public static String toSlug(String input) {
    String nowhitespace = WHITESPACE.matcher(input).replaceAll("-");
    String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD);
    String slug = NONLATIN.matcher(normalized).replaceAll("");
    slug = EDGESDHASHES.matcher(slug).replaceAll("");
    return slug.toLowerCase(Locale.ENGLISH);
}

9

我扩展了@McDowell的答案,将标点符号转义为连字符,并删除重复和前导/尾随连字符。

  private static final Pattern NONLATIN = Pattern.compile("[^\\w_-]");  
  private static final Pattern SEPARATORS = Pattern.compile("[\\s\\p{Punct}&&[^-]]");  

  public static String makeSlug(String input) {  
    String noseparators = SEPARATORS.matcher(input).replaceAll("-");
    String normalized = Normalizer.normalize(noseparators, Form.NFD);
    String slug = NONLATIN.matcher(normalized).replaceAll("");
    return slug.toLowerCase(Locale.ENGLISH).replaceAll("-{2,}","-").replaceAll("^-|-$","");
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接