2009-11-01 3 views
31

cadres Web tels que Rails et Django a un support intégré pour les « limaces » qui sont utilisés pour générer des URL lisibles et SEO-friendly:code Java/bibliothèque pour générer des limaces (pour une utilisation dans les URL jolies)

Une chaîne de limaces contient généralement que des caractères a-z, 0-9 et - et peut donc être écrit sans-escaping URL (pensez « foo% 20ba r ").

Je suis à la recherche d'une fonction de limace Java qui donne une chaîne Unicode valide retourne une représentation de limace (a-z, 0-9 et -).

Une fonction de limace trivial serait quelque chose le long des lignes de:

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

Cependant, cette mise en œuvre ne traiterait pas l'internationalisation et des accents (ë>e). Un moyen de contourner cela serait d'énumérer tous les cas spéciaux, mais cela ne serait pas très élégant. Je cherche quelque chose de plus réfléchi et général.

Ma question:

  • Quelle est la façon la plus générale/pratique pour générer des limaces de type Django/Rails en Java?

Répondre

34

Normalize votre chaîne en utilisant la décomposition canonique:

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

Ceci est encore un processus assez naïf, cependant. Il ne va rien faire pour s dièse (ß - utilisé en allemand), ou tout alphabet à base de caractères non latins (grec, cyrillique, CJC, etc.).

Soyez prudent lorsque vous modifiez le cas d'une chaîne. Les formes majuscules et minuscules dépendent des alphabets. En Turquie, la capitalisation de U + 0069 (i) est U + 0130 (İ) et U + 0049 (I) de sorte que vous risquez de l'introduction d'un caractère non-latin1 de nouveau dans votre chaîne si vous utilisez String.toLowerCase() dans un lieu turc.

+1

semble prometteur, mais la normalisation ne semble pas fonctionner: « foobar » se traduit en « FBR » au lieu de prévu « foobar ». Est-ce que tu sais pourquoi? – knorv

+1

Etrange - quand je mets la chaîne '" f \ u00F3 \ u00F2b \ u00e2r "' par la méthode, j'obtiens '' foobar ''. Vous faites peut-être une erreur d'encodage dans votre source ou fichier de données; voir http://illegalargumentexception.blogspot.com/2009/05/java-rough-guide-to-character-encoding.html – McDowell

+0

McDowell: Vous avez absolument raison - c'était une erreur d'encodage. Merci pour une excellente réponse! – knorv

1

J'ai étendu la réponse par @McDowell pour inclure la ponctuation d'échappement comme des traits d'union et pour supprimer les traits d'union dupliqués et avant/arrière.

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("^-|-$",""); 
    } 
1

La proposition de McDowel fonctionne presque, mais dans des cas comme celui-ci Hello World !! retourne hello-world-- (notez le -- à la fin de la chaîne) au lieu de hello-world.

Une version fixe pourrait être:

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(text).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); 
} 
Questions connexes