web-dev-qa-db-fra.com

Comment convertir CamelCase en noms lisibles par l'homme en Java?

J'aimerais écrire une méthode qui convertit CamelCase en un nom lisible par l'homme.

Voici le cas de test:

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

Cela fonctionne avec vos cas de test:

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

Voici un harnais de test:

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

Il utilise une expression rationnelle de longueur nulle avec lookbehind et lookforward pour trouver où insérer des espaces. Fondamentalement, il existe 3 modèles, et j'utilise String.format de les assembler pour le rendre plus lisible.

Les trois modèles sont:

UC derrière moi, UC suivi de LC devant moi

  XMLParser   AString    PDFLoader
    /\        /\           /\

non-UC derrière moi, UC en face de moi

 MyClass   99Bottles
  /\        /\

Lettre derrière moi, non-lettre devant moi

 GL11    May5    BFG9000
  /\       /\      /\

Les références

Questions connexes

Utilisation de comparaisons de longueur nulle pour la scission:

318

Vous pouvez le faire en utilisant org.Apache.commons.lang.StringUtils

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

La solution soignée et plus courte:

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

Si vous n'aimez pas les expressions rationnelles "compliquées" et si vous ne vous souciez pas de l'efficacité, j'ai utilisé cet exemple pour obtenir le même effet en trois étapes.

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

Il passe tous les tests ci-dessus, y compris ceux avec des chiffres.

Comme je l'ai dit, cela ne vaut pas l'utilisation de l'expression régulière unique dans d'autres exemples ici - mais quelqu'un pourrait trouver cela utile.

9
jlb83

Vous pouvez utiliser org.modeshape.common.text.Inflector .

Plus précisément:

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

Met en majuscule le premier mot et transforme les traits de soulignement en espaces et supprime "_id" et tous les jetons amovibles fournis.

Artefact Maven est: org.modeshape: modeshape-common: 2.3.0.Final

sur le référentiel JBoss: https://repository.jboss.org/nexus/content/repositories/releases

Voici le fichier JAR: https://repository.jboss.org/nexus/content/repositories/releases/org/modeshape/modeshape-common/2.3.0.Final/modeshape-common-2.3.0.Final .jar

6
Hendy Irawan

Les expressions rationnelles suivantes peuvent être utilisées pour identifier les majuscules dans les mots:

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

Il correspond à chaque lettre majuscule, c'est-à-dire l'éther après une lettre non majuscule ou un chiffre ou suivi d'une lettre minuscule et de chaque chiffre après une lettre.

Comment insérer un espace devant eux est au-delà de mon Java compétences =)

Edité pour inclure le cas numérique et le PDF cas du chargeur.

1
Jens

Je pense que vous devrez parcourir la chaîne et détecter les changements de minuscule à majuscule, de majuscule à minuscule, alphabétique à numérique, numérique à alphabétique. Lors de chaque modification, vous détectez l'insertion d'un espace, à une exception près: lorsque vous passez d'une majuscule à une minuscule, vous insérez l'espace avant un caractère.

1
Felix

Cela fonctionne en .NET ... optimisez à votre convenance. J'ai ajouté des commentaires pour que vous puissiez comprendre ce que chaque pièce fait. (RegEx peut être difficile à comprendre)

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;
}
1
Xinbi

J'ai pris le regex de polygenelubricants et l'ai transformé en une méthode d'extension sur les objets:

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

Cela transforme tout en une phrase lisible. Il fait un ToString sur l'objet passé. Ensuite, il utilise la regex donnée par les polygenelubricants pour scinder la chaîne. Ensuite, il perd chaque mot, à l'exception du premier mot et des acronymes. Je pensais que cela pourrait être utile pour quelqu'un.

0
vbullinger

Pour mémoire, voici une version presque (*) compatible Scala version:

  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

Une fois compilé, il peut être utilisé directement à partir de Java si le fichier scala-library.jar correspondant se trouve dans le chemin de classe.

(*) il échoue pour l'entrée "GL11Version" pour lequel il retourne "G L11 Version".

0
gerferra