【问题标题】:How do I convert CamelCase into human-readable names in Java?如何在 Java 中将 CamelCase 转换为人类可读的名称?
【发布时间】:2023-03-20 06:54:01
【问题描述】:

我想编写一个将 CamelCase 转换为人类可读名称的方法。

这是测试用例:

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

【问题讨论】:

标签: java regex string pascalcasing humanize


【解决方案1】:

简洁简洁的解决方案:

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

【讨论】:

  • 如问题的第一个assert所示,不需要大写。
  • 感谢您发现错误,将更新答案。
【解决方案2】:

这适用于 .NET...根据您的喜好进行优化。我添加了 cmets,这样您就可以了解每个部分在做什么。 (RegEx 可能很难理解)

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

【讨论】:

    【解决方案3】:

    您可以使用org.apache.commons.lang.StringUtils 完成此操作

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

    【讨论】:

    • 此解决方案比最受好评的解决方案好得多,因为:a) 它不会重新发明轮子:commons-lang 是事实上的标准,它工作正常,非常注重性能. b)当转换完成很多次时,这种方法比基于正则表达式的方法快得多:这是我执行上述测试 100,000 次的基准:``` 基于正则表达式的方法花了 4820 毫秒 ///// ///// 基于 commons-lang 的方法耗时 232 毫秒 ``` 这比使用正则表达式的方法快 20 倍!!!!
    • 我绝对同意克林特的观点,这应该是公认的答案。性能是一回事,但使用久经考验的库绝对是一种很好的编程习惯。
    • 或者使用 Java 8 的 String.join() 方法:String.join(" ", StringUtils.splitByCharacterTypeCamelCase("ExampleTest"));
    • 你怎么能不同意克林特·伊斯特伍德的观点? :)
    【解决方案4】:

    我从 polygenelubricants 中提取了 Regex,并将其变成了对象的扩展方法:

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

    这会将所有内容变成可读的句子。它对传递的对象执行 ToString。然后它使用 polygenelubricants 给出的 Regex 来拆分字符串。然后它降低除第一个单词和任何首字母缩写词之外的每个单词。认为它可能对那里的人有用。

    【讨论】:

      【解决方案5】:

      如果您不喜欢“复杂”的正则表达式,并且完全不关心效率,那么我使用这个示例分三个阶段来达到相同的效果。

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

      它通过了上面所有的测试用例,包括那些有数字的。

      正如我所说,这不如在此处的其他示例中使用一个正则表达式好 - 但有人可能会发现它很有用。

      【讨论】:

      • 谢谢,太好了。我做了一个JavaScript version
      • 如果您正在使用不支持后向/前向的正则表达式库/工具(如 golang 的正则表达式包),这也是唯一的方法。干得好。
      【解决方案6】:

      为了记录,这是一个几乎 (*) 兼容的 Scala 版本:

        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
      

      如果相应的 scala-library.jar 在类路径中,则编译后可以直接从 Java 中使用。

      (*) 输入 "GL11Version" 失败,并返回 "G L11 Version"

      【讨论】:

        【解决方案7】:

        您可以使用org.modeshape.common.text.Inflector

        具体来说:

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

        将第一个单词大写并将下划线转换为空格,并去除尾随“_id”和任何提供的可移动标记。

        Maven 工件是:org.modeshape:modeshape-common:2.3.0.Final

        在 JBoss 存储库上:https://repository.jboss.org/nexus/content/repositories/releases

        这是 JAR 文件:https://repository.jboss.org/nexus/content/repositories/releases/org/modeshape/modeshape-common/2.3.0.Final/modeshape-common-2.3.0.Final.jar

        【讨论】:

          【解决方案8】:

          我不是正则表达式忍者,所以我会遍历字符串,保持当前位置的索引被检查和前一个位置。如果当前位置是大写字母,我会在前一个位置之后插入一个空格并增加每个索引。

          【讨论】:

          • 嘘!那里的乐趣在哪里?
          【解决方案9】:

          这适用于您的测试用例:

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

          这是一个测试工具:

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

          它使用零长度匹配正则表达式,向后查找并向前查找插入空格的位置。基本上有 3 种模式,我使用String.format 将它们组合在一起以使其更具可读性。

          三种模式分别是:

          UC 在我身后,UC 跟在我前面的 LC

            XMLParser   AString    PDFLoader
              /\        /\           /\
          

          我后面的非 UC,我前面的 UC

           MyClass   99Bottles
            /\        /\
          

          字母在我身后,非字母在我面前

           GL11    May5    BFG9000
            /\       /\      /\
          

          参考文献

          相关问题

          使用零长度匹配的lookarounds进行拆分:

          【讨论】:

          • 这个概念也适用于 C#(当然,正则表达式相同,但正则表达式框架略有不同)。优秀作品。谢谢!
          • 在 Python 上似乎对我不起作用,这可能是因为正则表达式引擎不一样。恐怕我不得不尝试做一些不那么优雅的事情。 :)
          • 有人能解释一下 %s|%s|%s 对测试用例和一般情况的含义吗?
          • @Ari53​​nN3o: "%s"String.format(String format, args...) 参数的占位符。也可以按索引调用:String.format("%$1s|%$2s|%$3s", ...
          • 这将如何在 c# 中工作?没有relaceAll,如果字符串中包含“.”,我也想添加拆分。
          【解决方案10】:

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

          您可以链接 String.underscore().humanize() 方法来获取 CamelCase 字符串并将其转换为人类可读的字符串。

          【讨论】:

          • inflection-js 在 Javascript 中。我正在寻找 Java 解决方案。
          【解决方案11】:

          我认为您将不得不遍历字符串并检测从小写到大写、从大写到小写、从字母到数字、从数字到字母的变化。但是,在您检测到的每次更改中都插入一个空格,但有一个例外:在从大写变为小写时,您在前一个字符插入空格。

          【讨论】:

            【解决方案12】:

            以下正则表达式可用于识别单词中的大写:

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

            它匹配每个大写字母,即非大写字母或数字后的ether或小写字母和字母后的每个数字。

            如何在它们之前插入空格超出了我的 Java 技能 =)

            已编辑为包含数字大小写和 PDF 加载器大小写。

            【讨论】:

            • @Yaneeve:我刚看到数字……这可能会让事情变得更复杂。可能是另一个正则表达式来捕捉那些将是简单的方法。
            • @Jens:它会匹配PDFLoader中的L吗?
            • (?
            • 现在,我非常钦佩您的正则表达式技巧,但我不希望不得不保持这一点。
            • @Chris:是的,没错。正则表达式更像是一种只写语言。 =) 虽然这个特定的表达式不是很难阅读,但如果您将| 阅读为“或”。嗯......也许是......我见过更糟的=/
            猜你喜欢
            • 1970-01-01
            • 2011-01-23
            • 1970-01-01
            • 1970-01-01
            • 2021-01-09
            • 1970-01-01
            • 2011-04-15
            • 2019-05-21
            • 1970-01-01
            相关资源
            最近更新 更多