Como faço para converter o CamelCase em nomes legíveis por humanos em Java?

Eu gostaria de escrever um método que converta o CamelCase em um nome legível para humanos.

Aqui está o caso de teste:

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

Isso funciona com seus testcases:

 static String splitCamelCase(String s) { return s.replaceAll( String.format("%s|%s|%s", "(?< =[AZ])(?=[AZ][az])", "(?<=[^AZ])(?=[AZ])", "(?<=[A-Za-z])(?=[^A-Za-z])" ), " " ); } 

Aqui está um equipamento de teste:

  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) + "]"); } 

Ele usa regex de correspondência de comprimento zero com lookbehind e lookforward para encontrar onde inserir espaços. Basicamente, existem 3 padrões, e eu uso String.format para colocá-los juntos para torná-lo mais legível.

Os três padrões são:

UC atrás de mim, UC seguido por LC na minha frente

  XMLParser AString PDFLoader /\ /\ /\ 

não-UC atrás de mim, UC na minha frente

  MyClass 99Bottles /\ /\ 

Carta atrás de mim, sem letra na minha frente

  GL11 May5 BFG9000 /\ /\ /\ 

Referências

  • regular-expressions.info/Lookarounds

Perguntas relacionadas

Usando lookarounds de correspondência de tamanho zero para dividir:

  • Cadeia de divisão de regex, mas mantenha os separadores
  • Java split está comendo meus personagens

Você pode fazer isso usando org.apache.commons.lang.StringUtils

 StringUtils.join( StringUtils.splitByCharacterTypeCamelCase("ExampleTest"), ' ' ); 

Se você não gosta de regex “complicado” e não se incomoda com eficiência, usei este exemplo para obter o mesmo efeito em três etapas.

 String name = camelName.replaceAll("([AZ][az]+)", " $1") // Words beginning with UC .replaceAll("([AZ][AZ]+)", " $1") // "Words" of only UC .replaceAll("([^A-Za-z ]+)", " $1") // "Words" of non-letters .trim(); 

Ele passa todos os casos de teste acima, incluindo aqueles com dígitos.

Como eu digo, isso não é tão bom quanto usar a expressão regular em outros exemplos aqui – mas alguém pode achar útil.

Você pode usar org.modeshape.common.text.Inflector .

Especificamente:

 String humanize(String lowerCaseAndUnderscoredWords, String... removableTokens) 

Capitaliza a primeira palavra e transforma sublinhados em espaços e tiras à direita de “_id” e quaisquer tokens removíveis fornecidos.

O artefato Maven é: org.modeshape: modeshape-common: 2.3.0.Final

no repository do JBoss: https://repository.jboss.org/nexus/content/repositories/releases

Aqui está o arquivo JAR: https://repository.jboss.org/nexus/content/repositories/releases/org/modeshape/modeshape-common/2.3.0.Final/modeshape-common-2.3.0.Final.jar

A solução pura e mais curta:

 StringUtils.capitalize(StringUtils.join(StringUtils.splitByCharacterTypeCamelCase("yourCamelCaseText"), StringUtils.SPACE)); // Your Camel Case Text 

O Regex a seguir pode ser usado para identificar as maiúsculas dentro de palavras:

 "((?< =[a-z0-9])[AZ]|(?<=[a-zA-Z])[0-9]]|(?<=[AZ])[AZ](?=[az]))" 

Ele corresponde a todas as letras maiúsculas, ou seja, éter após uma letra ou dígito não maiúscula ou seguido por uma letra minúscula e por cada dígito após uma letra.

Como inserir um espaço antes deles está além das minhas habilidades em Java =)

Editado para include o caso de dígitos e o caso do PDF Loader.

Eu acho que você terá que percorrer a string e detectar alterações de minúsculas para maiúsculas, maiúsculas para minúsculas, alfabético para numérico, numérico para alfabético. Em cada mudança detectada, insira um espaço com uma exceção: em uma alteração de maiúscula para minúscula, você insere o espaço um caractere antes.

Isso funciona em .NET … otimizar ao seu gosto. Eu adicionei comentários para que você possa entender o que cada peça está fazendo. (RegEx pode ser difícil de entender)

 public static string SplitCamelCase(string str) { str = Regex.Replace(str, @"([AZ])([AZ][az])", "$1 $2"); // Capital followed by capital AND a lowercase. str = Regex.Replace(str, @"([az])([AZ])", "$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; } 

Para o registro, aqui está uma versão Scala quase (*) compatível:

  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 

Uma vez compilado, ele pode ser usado diretamente do Java se o arquivo scala-library.jar correspondente estiver no classpath.

(*) falha para a input "GL11Version" para a qual retorna "G L11 Version" .

Eu peguei o Regex de poligilubrificantes e o transformei em um método de extensão em objects:

  ///  /// Turns a given object into a sentence by: /// Converting the given object into a . /// 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. ///  /// The object to turn into a proper sentence. /// A string representation of the original object that reads like a real sentence. public static string ToProperSentence(this object original) { Regex addSpacesAtCapitalLettersRegEx = new Regex(@"(?< =[AZ])(?=[AZ][az]) | (?<=[^AZ])(?=[AZ]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); string[] words = addSpacesAtCapitalLettersRegEx.Split(original.ToString()); if (words.Length > 1) { List wordsList = new List { words[0] }; wordsList.AddRange(words.Skip(1).Select(word => word.Equals(word.ToUpper()) ? word : word.ToLower())); words = wordsList.ToArray(); } return string.Join(" ", words); } 

Isso transforma tudo em uma frase legível. Faz um ToString no object passado. Em seguida, ele usa o Regex dado por poligenelubrificantes para dividir a string. Em seguida, ele lança cada palavra, exceto a primeira palavra e quaisquer siglas. Pensei que poderia ser útil para alguém lá fora.

Eu não sou um ninja regex, então eu iria percorrer a string, mantendo os índices da posição atual sendo verificada e a posição anterior. Se a posição atual for uma letra maiúscula, eu inseriria um espaço após a posição anterior e incrementaria cada índice.

http://code.google.com/p/inflection-js/

Você poderia encadear os methods String.underscore (). Humanize () para pegar uma string CamelCase e convertê-la em uma string legível.