2009-11-01 6 views
31

marcos web como Rails y Django ha incorporado soporte para "babosas", que se utilizan para generar URL legibles y SEO-amigable:código Java/librería para generar babosas (para su uso en URLs ricas)

Una cadena babosas contiene típicamente sólo de los personajes a-z, 0-9 y - y por lo tanto se puede escribir sin URL-escape (piensa "foo% 20ba r ").

Busco una función babosa de Java que da cualquier cadena Unicode válida devolverá una representación babosa (a-z, 0-9 y -).

Una función babosa trivial sería algo a lo largo de las líneas de:

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

Sin embargo, esta implementación no se ocuparía de la internacionalización y acentos (ë>e). Una forma de evitar esto sería enumerar todos los casos especiales, pero eso no sería muy elegante. Estoy buscando algo más bien pensado y general.

Mi pregunta:

  • Cuál es la forma más general/práctico para generar babosas tipo Django/Rails en Java?

Respuesta

34

Normalize su cadena mediante la descomposición canónica:

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

Esto sigue siendo un proceso bastante ingenuo, sin embargo. No va a hacer nada para s-sharp (ß - usado en alemán), o cualquier alfabeto no latino (griego, cirílico, CJK, etc.).

Tenga cuidado al cambiar la caja de una cuerda. Las formas mayúsculas y minúsculas dependen de los alfabetos. En turco, la capitalización de U + 0069 (i) es U + 0130 (İ), no U + 0049 (I) por lo que corre el riesgo de introducir un carácter no latin1 de nuevo en su cadena si se utiliza String.toLowerCase() bajo un local turco.

+1

parece prometedor, pero la normalización no parece funcionar: "foobar" se traduce en "FBR" en lugar de esperar "foobar". ¿Sabes por qué? – knorv

+1

Extraño: cuando pongo la cadena '" f \ u00F3 \ u00F2b \ u00e2r "' a través del método, obtengo '" foobar "'. Tal vez esté cometiendo un error de codificación en su fuente o archivo de datos; ver http://illegalargumentexception.blogspot.com/2009/05/java-rough-guide-to-character-encoding.html – McDowell

+0

McDowell: Tiene toda la razón, fue un error de codificación. Gracias por una excelente respuesta! – knorv

1

tengo extendió la respuesta por @McDowell para incluir la escapatoria de puntuación como guiones y para eliminar los guiones duplicados y de adelanto/adelanto.

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 proposición de McDowel casi funciona, pero en casos como este Hello World !! vuelve hello-world-- (nótese el -- al final de la cadena) en lugar de hello-world.

Una versión fija podría ser:

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