CamelCaseをJavaで判読可能な名前に変換するにはどうすればよいですか?


157

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

5
まず、変換のルールを指定する必要があります。たとえば、どのようにPDFLoaderなりPDF Loaderますか?
–JørnSchou-Rode、2010

2
その形式を「パスカルケース」と呼びます。「camelCase」では、最初の文字は小文字でなければなりません。少なくとも開発者に関する限り。msdn.microsoft.com/en-us/library/x2dbyw72(v=vs.71).aspx
Muhd

回答:


336

これはあなたのテストケースで動作します:

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

スペースを挿入する場所を見つけるために、lookbehindとlookforwardで長さがゼロの一致する正規表現を使用します。基本的に3つのパターンがあり、String.formatそれらを組み合わせて読みやすくしています。

3つのパターンは次のとおりです。

UCが私の後ろ、UCが私の前にLCが続く

  XMLParser   AString    PDFLoader
    /\        /\           /\

UCが私の後ろ、UCが私の前

 MyClass   99Bottles
  /\        /\

私の後ろに手紙、私の前に手紙はありません

 GL11    May5    BFG9000
  /\       /\      /\

参考文献

関連する質問

長さゼロの一致するルックアラウンドを使用して分割する:


1
この概念はC#でも機能します(もちろん、正規表現は同じですが、正規表現のフレームワークが少し異なります)。素晴らしい仕事。ありがとう!
gmm 2013年

Pythonで私のために働いているようではない、それは正規表現エンジンが同じではない可能性があります。エレガントではないことをやってみないといけないと思います。:)
MarioVilas 2013

2
テストケースに関して、また一般的に、%s |%s |%sの意味を誰かが説明してもらえますか?
Ari53​​nN3o 2014

1
@ Ari53​​nN3o:" %s"String.format(String format, args...)引数のプレースホルダーです。また、インデックスで呼び出すことができます:String.format("%$1s|%$2s|%$3s", ...
氏Polywhirl

これはC#でどのように機能しますか?relaceAll文字列に " ."が含まれている場合、分割を追加する必要もありません。
サロジャナンド2015年

119

あなたはそれを使ってそれを行うことができます org.apache.commons.lang.StringUtils

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

9
このソリューションは、最も支持されているソリューションよりもはるかに優れています。理由は次のとおりです。a)ホイールを再発明しない:commons-langは事実上の標準であり、正常に機能し、パフォーマンスに非常に重点を置いています。b)変換が何度も行われる場合、この方法は正規表現ベースの方法よりもはるかに高速です。これは、前述のテストを100,000回実行するための私のベンチマークです: `` `正規表現ベースのメソッドは4820ミリ秒かかりました///// ///// commons-langベースのメソッドは232ミリ秒かかりました `` `正規表現を使用するものより約20倍高速です!!!!
クリントイーストウッド

2
私は間違いなくこれに関してクリントに同意します、これは受け入れられる答えになるはずです。パフォーマンスは重要なことですが、戦いでテストされたライブラリを使用することは、間違いなく優れたプログラミング手法です。
ジュリアン

1
または、Java 8のString.join()メソッドを使用して:String.join( ""、StringUtils.splitByCharacterTypeCamelCase( "ExampleTest"));
dk7 2018年

どうしてクリント・イーストウッドに同意できないのですか?:)
daneejela

19

きちんとした短い解決策:

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

最初assertの質問で示したように、大文字は望ましくありません。
slartidan

バグをキャッチしていただきありがとうございます。回答を更新します。
Sahil Chhabra

10

「複雑な」正規表現が気に入らず、効率がまったく気にならない場合は、この例を使用して3つの段階で同じ効果を実現しています。

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

数字を含むものを含め、上記のすべてのテストケースに合格します。

私が言うように、これはここの他のいくつかの例で1つの正規表現を使用するよりも良いものではありませんが、誰かがそれを便利だと思うかもしれません。


1
ありがとう、これは素晴らしかった。JavaScript版を作りました。
Polywhirl氏、2015年

これは、(golangのregexpパッケージのように)lookbehind / lookforwardをサポートしていない正規表現ライブラリ/ツールを使用している場合の唯一の方法でもあります。よくやった。
mdwhatcott

6

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


1

次の正規表現は、単語内の大文字を識別するために使用できます。

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

これは、すべての大文字に一致します。つまり、大文字以外の文字または数字の後のエーテル、または小文字と文字の後のすべての数字がそれに続きます。

それらの前にスペースを挿入する方法は私のJavaスキルを超えています=)

数字のケースとPDFローダーのケースを含むように編集されました。


@ヤニーブ:私は数字を見ただけです...これは物事をより複雑にするかもしれません。おそらく、それらをキャッチする別の正規表現が簡単な方法でしょう。
イェンス、2010

@イェンス:それはで一致LPDFLoaderますか?
–JørnSchou-Rode、2010

(?<= [a-z0-9])[A-Z0-9]はどうですか?
Yaneeve、2010

3
今、私はあなたの正規表現のスキルに非常に感心していますが、それを維持する必要はありません。
クリスナイト

1
@クリス:うん、そうだね。正規表現は、書き込み専用の言語です。=)この特定の式は|、「または」と読んだ場合、読みにくいものではありません。まあ...多分それは...私はもっと悪いことを見た= /
イェンス

1

文字列を繰り返し処理して、小文字から大文字、大文字から小文字、アルファベットから数字、数字からアルファベットへの変更を検出する必要があると思います。ただし、変更が検出されるたびに1つの例外を除いてスペースが挿入されます。大文字から小文字への変更では、スペースを1文字前に挿入します。


1

これは.NETで動作します...好みに合わせて最適化してください。コメントを追加して、各ピースの動作を理解できるようにしました。(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;
}

0

参考までに、ほぼ(*)互換の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"


0

私は、多重潤滑剤から正規表現を取得し、それをオブジェクトの拡張メソッドに変換しました。

    /// <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によって指定された正規表現を使用して文字列を分割します。次に、最初の単語と頭字語を除く各単語をToLowersします。それはそこにいる誰かにとって役立つかもしれないと思った。


-2

私は正規表現の忍者ではないので、チェックされている現在の位置と前の位置のインデックスを保持しながら、文字列を繰り返し処理します。現在の位置が大文字の場合、前の位置の後にスペースを挿入して、各インデックスをインクリメントします。


2
やった!どこが楽しいの?
vbullinger 2012

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.