uppercamelcase - ¿Cómo convierto CamelCase en nombres legibles para humanos en Java?
kebab_case (13)
Me gustaría escribir un método que convierta a CamelCase en un nombre legible para los humanos.
Aquí está el caso de prueba:
public void testSplitCamelCase() {
assertEquals("lowercase", splitCamelCase("lowercase"));
assertEquals("Class", splitCamelCase("Class"));
assertEquals("My Class", splitCamelCase("MyClass"));
assertEquals("HTML", splitCamelCase("HTML"));
assertEquals("PDF Loader", splitCamelCase("PDFLoader"));
assertEquals("A String", splitCamelCase("AString"));
assertEquals("Simple XML Parser", splitCamelCase("SimpleXMLParser"));
assertEquals("GL 11 Version", splitCamelCase("GL11Version"));
}
Creo que tendrá que iterar sobre la cadena y detectar los cambios de minúscula a mayúscula, de mayúscula a minúscula, de alfabético a numérico, de numérico a alfabético. En cada cambio que detecte, inserte un espacio con una excepción: en un cambio de mayúscula a minúscula, inserta el espacio de un carácter antes.
El siguiente Regex se puede usar para identificar las mayúsculas dentro de las palabras:
"((?<=[a-z0-9])[A-Z]|(?<=[a-zA-Z])[0-9]]|(?<=[A-Z])[A-Z](?=[a-z]))"
Coincide con cada letra mayúscula, es decir, éter después de una letra o un dígito que no sea mayúscula o seguido de una letra minúscula y cada dígito después de una letra.
Cómo insertar un espacio antes que ellos está más allá de mis habilidades de Java =)
Editado para incluir el caso de los dígitos y el caso de PDF Loader.
Esto funciona con tus testcases:
static String splitCamelCase(String s) {
return s.replaceAll(
String.format("%s|%s|%s",
"(?<=[A-Z])(?=[A-Z][a-z])",
"(?<=[^A-Z])(?=[A-Z])",
"(?<=[A-Za-z])(?=[^A-Za-z])"
),
" "
);
}
Aquí hay un arnés de prueba:
String[] tests = {
"lowercase", // [lowercase]
"Class", // [Class]
"MyClass", // [My Class]
"HTML", // [HTML]
"PDFLoader", // [PDF Loader]
"AString", // [A String]
"SimpleXMLParser", // [Simple XML Parser]
"GL11Version", // [GL 11 Version]
"99Bottles", // [99 Bottles]
"May5", // [May 5]
"BFG9000", // [BFG 9000]
};
for (String test : tests) {
System.out.println("[" + splitCamelCase(test) + "]");
}
Utiliza la expresión regular coincidente de longitud cero con lookbehind y lookforward para encontrar dónde insertar espacios. Básicamente hay 3 patrones, y utilizo String.format
para juntarlos y hacerlos más legibles.
Los tres patrones son:
UC detrás de mí, UC seguido de LC delante de mí
XMLParser AString PDFLoader
// // //
no-UC detrás de mí, UC delante de mí
MyClass 99Bottles
// //
Carta detrás de mí, sin letra delante de mí
GL11 May5 BFG9000
// // //
Referencias
Preguntas relacionadas
Usar alternativas de coincidencia de longitud cero para dividir:
Esto funciona en .NET ... optimiza a tu gusto. Agregué comentarios para que puedas entender lo que está haciendo cada pieza. (RegEx puede ser difícil de entender)
public static string SplitCamelCase(string str)
{
str = Regex.Replace(str, @"([A-Z])([A-Z][a-z])", "$1 $2"); // Capital followed by capital AND a lowercase.
str = Regex.Replace(str, @"([a-z])([A-Z])", "$1 $2"); // Lowercase followed by a capital.
str = Regex.Replace(str, @"(/D)(/d)", "$1 $2"); //Letter followed by a number.
str = Regex.Replace(str, @"(/d)(/D)", "$1 $2"); // Number followed by letter.
return str;
}
La solución ordenada y más corta:
StringUtils.capitalize(StringUtils.join(StringUtils.splitByCharacterTypeCamelCase("yourCamelCaseText"), StringUtils.SPACE)); // Your Camel Case Text
No soy un ninja regex, así que iteraría sobre la cuerda, manteniendo los índices de la posición actual siendo revisados y la posición anterior. Si la posición actual es una letra mayúscula, insertaría un espacio después de la posición anterior e incrementaría cada índice.
Para el registro, aquí hay una versión de Scala casi (*) compatible:
object Str { def unapplySeq(s: String): Option[Seq[Char]] = Some(s) }
def splitCamelCase(str: String) =
String.valueOf(
(str + "A" * 2) sliding (3) flatMap {
case Str(a, b, c) =>
(a.isUpper, b.isUpper, c.isUpper) match {
case (true, false, _) => " " + a
case (false, true, true) => a + " "
case _ => String.valueOf(a)
}
} toArray
).trim
Una vez compilado, se puede usar directamente desde Java si el scala-library.jar correspondiente está en el classpath.
(*) falla para la entrada "GL11Version"
para la que devuelve "G L11 Version"
.
Puede usar org.modeshape.common.text.Inflector .
Específicamente:
String humanize(String lowerCaseAndUnderscoredWords, String... removableTokens)
Escribe en mayúscula la primera palabra y convierte caracteres de subrayado en espacios y tiras que arrastran "_id" y cualquier token extraíble suministrado.
El artefacto de Maven es: org.modeshape: modeshape-common: 2.3.0.Final
en el repositorio de JBoss: https://repository.jboss.org/nexus/content/repositories/releases
Aquí está el archivo JAR: https://repository.jboss.org/nexus/content/repositories/releases/org/modeshape/modeshape-common/2.3.0.Final/modeshape-common-2.3.0.Final.jar
Puedes hacerlo usando org.apache.commons.lang.StringUtils
StringUtils.join(
StringUtils.splitByCharacterTypeCamelCase("ExampleTest"),
'' ''
);
RegEx debería funcionar, algo así como ([AZ]{1})
. Esto capturará todas las letras mayúsculas, después de eso puede reemplazarlas con /1
o siempre puede referirse a Grupos RegEx en Java.
Si no te gustan las expresiones regulares "complicadas" y no te molesta en absoluto la eficiencia, entonces he usado este ejemplo para lograr el mismo efecto en tres etapas.
String name =
camelName.replaceAll("([A-Z][a-z]+)", " $1") // Words beginning with UC
.replaceAll("([A-Z][A-Z]+)", " $1") // "Words" of only UC
.replaceAll("([^A-Za-z ]+)", " $1") // "Words" of non-letters
.trim();
Pasa todos los casos de prueba anteriores, incluidos aquellos con dígitos.
Como digo, esto no es tan bueno como usar una expresión regular en algunos otros ejemplos aquí, pero alguien podría encontrarlo útil.
Tomé Regex de polygenelubricants y lo convertí en un método de extensión en objetos:
/// <summary>
/// Turns a given object into a sentence by:
/// Converting the given object into a <see cref="string"/>.
/// Adding spaces before each capital letter except for the first letter of the string representation of the given object.
/// Makes the entire string lower case except for the first word and any acronyms.
/// </summary>
/// <param name="original">The object to turn into a proper sentence.</param>
/// <returns>A string representation of the original object that reads like a real sentence.</returns>
public static string ToProperSentence(this object original)
{
Regex addSpacesAtCapitalLettersRegEx = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
string[] words = addSpacesAtCapitalLettersRegEx.Split(original.ToString());
if (words.Length > 1)
{
List<string> wordsList = new List<string> { words[0] };
wordsList.AddRange(words.Skip(1).Select(word => word.Equals(word.ToUpper()) ? word : word.ToLower()));
words = wordsList.ToArray();
}
return string.Join(" ", words);
}
Esto convierte todo en una oración legible. Hace un ToString en el objeto pasado. Luego usa la Regex dada por polygenelubricants para dividir la cadena. Luego, ToLowers cada palabra a excepción de la primera palabra y cualquier acrónimo. Pensé que podría ser útil para alguien allá afuera.
http://code.google.com/p/inflection-js/
Puede encadenar los métodos String.underscore (). Humanize () para tomar una cadena CamelCase y convertirla en una cadena legible para humanos.