CamelCase'i Java'da insan tarafından okunabilir isimlere nasıl dönüştürebilirim?


157

CamelCase insan tarafından okunabilir bir isme dönüştüren bir yöntem yazmak istiyorum.

İşte test örneği:

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
İlk olarak, dönüşüm kurallarını belirtmeniz gerekecektir. Mesela nasıl PDFLoaderolur PDF Loader?
Jørn Schou-Rode

2
Bu formata "PascalCase" diyorum. "CamelCase" de ilk harf küçük olmalıdır. En azından geliştiriciler söz konusu olduğunda. msdn.microsoft.com/en-us/library/x2dbyw72(v=vs.71).aspx
Muhd

Yanıtlar:


337

Bu testcase'lerinizle çalışır:

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

İşte bir test takımı:

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

Nereye boşluk ekleyeceğinizi bulmak için lookbehind ve lookforward ile sıfır uzunluklu regex kullanır. Temelde 3 desen var ve String.formatbunları daha okunabilir hale getirmek için bir araya getirmek için kullanıyorum.

Üç desen:

Arkamdaki UC, UC arkamdan LC

  XMLParser   AString    PDFLoader
    /\        /\           /\

arkamda UC olmayan, önümdeki UC

 MyClass   99Bottles
  /\        /\

Arkamdaki mektup, önümdeki mektup olmayan

 GL11    May5    BFG9000
  /\       /\      /\

Referanslar

İlgili sorular

Bölmek için sıfır uzunluklu eşleme bakış açılarını kullanma:


1
Kavram C # 'da da çalışır (aynı düzenli ifadelerle, ancak elbette biraz farklı düzenli ifade çerçevesiyle). Harika iş. Teşekkürler!
gmm

Python'da benim için çalışmıyor gibi görünüyor, bunun nedeni normal ifade motorunun aynı olmaması olabilir. Daha az zarif bir şey yapmayı denemek zorundayım, korkarım. :)
MarioVilas

2
Birisi% s |% s |% s testislerine ve ayrıca genel olarak ne anlama geldiğini açıklayabilir mi?
Ari53nN3o

1
@ Ari53nN3o: " %s" ler String.format(String format, args...)argümanlar için yer tutuculardır . Ayrıca dizinine göre arayabilirsiniz:String.format("%$1s|%$2s|%$3s", ...
Bay Polywhirl

Bu nasıl c # çalışır? Hiçbir yoktur relaceAllda ben dize "varsa bölünmeyi eklemek istediğiniz .ki".
sarojanand

119

Kullanarak yapabilirsiniz org.apache.commons.lang.StringUtils

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

9
Bu çözüm en çok öne sürülenden daha iyidir çünkü: a) Tekerleği yeniden icat etmez: commons-lang fiili bir standarttır ve iyi çalışır, performansa çok odaklanır. b) Dönüşüm çok kez yapıldığında, bu yöntem normal ifade tabanlı olandan çok daha hızlıdır: bu, yukarıda belirtilen testleri 100.000 kez yürütmek için benim ölçütüm: `` `` normal ifade tabanlı yöntem 4820 milisaniye aldı ///// ///// commons-lang tabanlı yöntem 232 milisaniye aldı `` `bu normal ifade kullanandan yaklaşık 20 kat daha hızlı !!!!
Clint Eastwood

2
Bu konuda Clint'e kesinlikle katılıyorum, bu kabul edilen cevap olmalı. Performans bir şeydir, ancak savaşta test edilmiş bir kütüphane kullanmak kesinlikle iyi bir programlama uygulamasıdır.
Julien

1
Veya Java 8'in String.join () yöntemini kullanarak: String.join ("", StringUtils.splitByCharacterTypeCamelCase ("SampleTest"));
dk7

Clint Eastwood ile nasıl anlaşamadınız? :)
daneejela

19

Temiz ve daha kısa çözüm:

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

assertSorunun ilkinde gösterildiği gibi , büyük harf kullanımı istenmez.
slartidan

Hata yakalamak için teşekkürler, cevap güncelleyecek.
Sahil Chhabra

10

"Karmaşık" normal ifadelerden hoşlanmıyorsanız ve verimlilikle hiç uğraşmıyorsanız, bu örneği üç aşamada aynı etkiyi elde etmek için kullandım.

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

Rakamlı olanlar da dahil olmak üzere yukarıdaki tüm test senaryolarını geçer.

Dediğim gibi, buradaki diğer bazı örneklerde tek bir düzenli ifadeyi kullanmak kadar iyi değil - ama birisi bunu yararlı bulabilir.


1
Teşekkürler, bu harikaydı. Bir JavaScript sürümü oluşturdum .
Bay Polywhirl

Bu, lookbehind / lookforward'ı (golang'ın regexp paketi gibi) desteklemeyen bir regex kütüphanesi / aracıyla çalışıyorsanız da gitmenin tek yoludur. İyi iş.
mdwhatcott

6

Org.modeshape.common.text.Inflector kullanabilirsiniz .

özellikle:

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

İlk kelimeyi büyük harfe çevirir ve alt çizgileri boşluklara dönüştürür ve "_id" ve sağlanan tüm çıkarılabilir belirteçleri izler.

Maven artefaktı: org.modeshape: modeshape-ortak: 2.3.0.

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

İşte JAR dosyası: 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şağıdaki Regex kelimelerin içindeki büyük harfleri tanımlamak için kullanılabilir:

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

Büyük harf olmayan bir harf veya rakamdan sonraki eter veya küçük harf ve ardından gelen harflerden sonraki her büyük harfle eşleşir.

Onlardan önce nasıl boşluk ekleyeceğim Java becerilerimin ötesinde =)

Rakam muhafazasını ve PDF Yükleyici muhafazasını içerecek şekilde düzenlendi.


@Yaneeve: Rakamları gördüm ... bu işleri daha karmaşık hale getirebilir. Muhtemelen bunları yakalamak için başka bir Regex kolay yol olurdu.
Jens

@Jens: o maç Will Liçinde PDFLoader?
Jørn Schou-Rode

ne dersin (? <= [a-z0-9]) [A-Z0-9]?
Yaneeve

3
Şimdi, Regex yeteneğinize büyük bir hayranlık duyuyorum, ama bunu korumak zorunda kalmaktan nefret ediyorum.
Chris Knight

1
@Chris: Evet, bu doğru. Regex daha çok salt yazılan bir dildir. =) Bu ifadeyi okumak çok zor olmasa |da, "veya" olarak okursanız . Şey ... belki de ... Daha kötü gördüm = /
Jens

1

Ben dize yineleme ve küçük harf büyük harf, büyük harf küçük harf, alfabetik sayısal, sayısal, alfabetik değişiklikleri tespit etmek gerektiğini düşünüyorum. Tespit ettiğiniz her değişiklikte bir istisna dışında boşluk ekleyin: büyükten küçük harfe geçişte boşluktan önce bir karakter eklersiniz.


1

Bu .NET'te çalışır ... beğeninize göre optimize edin. Her parçanın ne yaptığını anlayabilmeniz için yorumlar ekledim. (RegEx'i anlamak zor olabilir)

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

Kayıt için, neredeyse (*) uyumlu bir Scala sürümü:

  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

Derlendiğinde, karşılık gelen scala-library.jar sınıf yolundaysa doğrudan Java'dan kullanılabilir.

(*) "GL11Version"geri döndüğü giriş için başarısız olur "G L11 Version".


0

Regex'i poligeno-yağlayıcılardan aldım ve nesneler üzerinde bir uzatma yöntemine dönüştürdüm:

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

Bu her şeyi okunabilir bir cümleye dönüştürür. Geçirilen nesne üzerinde bir ToString yapar. Daha sonra ipi bölmek için poligenel-yağlayıcılar tarafından verilen Regex'i kullanır. Daha sonra, ilk kelime ve herhangi bir kısaltma hariç her kelimeyi azaltır. Orada birisi için yararlı olabileceğini düşündüm.


-2

Ben bir regex ninja değilim, bu yüzden geçerli pozisyonun dizinleri ve önceki pozisyonu tutarak, dize üzerinde yineleme olur. Geçerli konum büyük harfse, önceki konumdan sonra bir boşluk ekler ve her dizini arttırırdım.


2
Psssh! Bunun içindeki eğlence nerede?
vbullinger

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.