Java에서 CamelCase를 사람이 읽을 수있는 이름으로 어떻게 변환합니까?


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
먼저 변환 규칙을 지정해야합니다. 예를 들어, 어떻게 PDFLoaderPDF Loader?
Jørn Schou-Rode

2
이 형식을 "PascalCase"라고합니다. "camelCase"에서 첫 글자는 소문자 여야합니다. 최소한 개발자에 관한 한. msdn.microsoft.com/ko-kr/library/x2dbyw72(v=vs.71).aspx
Muhd

답변:


337

이것은 테스트 케이스와 함께 작동합니다.

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더 읽기 쉽게 만들기 위해 패턴 을 조합하는 데 사용합니다.

세 가지 패턴은 다음과 같습니다.

내 뒤의 UC, 내 뒤의 LC, 뒤의 LC

  XMLParser   AString    PDFLoader
    /\        /\           /\

내 뒤에 비 UC, 내 앞에 UC

 MyClass   99Bottles
  /\        /\

내 뒤에 편지, 내 앞에 편지가 아닌

 GL11    May5    BFG9000
  /\       /\      /\

참고 문헌

관련 질문

길이가 일치하지 않는 lookaround를 사용하여 분할 :


1
이 개념은 C #에서도 작동합니다 (물론 동일한 정규 표현식이지만 약간 다른 정규 표현식 프레임 워크). 잘했습니다. 감사!
gmm

파이썬에서 저에게 효과가없는 것 같습니다. 정규식 엔진이 같지 않기 때문일 수 있습니다. 덜 우아한 일을 시도해야 할 것 같습니다. :)
MarioVilas

2
테스트 케이스와 관련하여 % s | % s | % s가 무엇을 의미하는지 일반적으로 설명해 주시겠습니까?
Ari53nN3o

1
@ Ari53nN3o : " %s"String.format(String format, args...)인수의 자리 표시 자입니다 . 색인으로 전화 할 수도 있습니다.String.format("%$1s|%$2s|%$3s", ...
Mr. Polywhirl

이것이 C #에서 어떻게 작동합니까? relaceAll문자열에 " ." 가 있으면 split을 추가하고 싶지 않습니다 .
sarojanand

119

당신은 그것을 사용하여 그것을 할 수 있습니다 org.apache.commons.lang.StringUtils

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

9
a) 바퀴를 재발 명하지 않습니다. commons-lang은 사실상의 표준이며 성능에 매우 중점을두고 작동합니다. b) 변환이 여러 번 수행되면이 방법은 정규식 기반 방법보다 훨씬 빠릅니다. 이는 앞서 언급 한 테스트를 100,000 번 실행하는 벤치 마크입니다.```정규식 기반 방법은 4820 밀리 초 ///// ///// commons-lang 기반 방법은 232 밀리 초를 사용했습니다 .```` 정규식을 사용하는 것보다 약 20 배 빠릅니다 !!!!
클린트 이스트우드

2
나는 이것에 대해 Clint에 분명히 동의합니다. 성능은 중요한 것이지만 전투 테스트를 거친 라이브러리를 사용하는 것은 좋은 프로그래밍 습관입니다.
Julien

1
또는 Java 8의 String.join () 메소드를 사용하여 : String.join ( "", StringUtils.splitByCharacterTypeCamelCase ( "ExampleTest"));
dk7

클린트 이스트우드에 어떻게 동의 할 수 없습니까? :)
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
고마워요. JavaScript 버전을 만들었습니다 .
Mr. Polywhirl

이것은 lookbehind / lookforward를 지원하지 않는 정규 표현식 라이브러리 / 도구로 작업하는 경우 (golang의 정규 표현식 패키지와 같은) 유일한 방법입니다. 잘 하셨어요.
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 로더 케이스를 포함하도록 편집되었습니다.


@Yaneeve : 방금 숫자를 보았습니다 ... 이것이 더 복잡해질 수 있습니다. 아마도 다른 정규 표현식을 사용하는 것이 쉬운 방법 일 것입니다.
Jens

@Jens : L에서 일치 PDFLoader합니까?
Jørn Schou-Rode

어때요? (? <= [a-z0-9]) [A-Z0-9]?
Yaneeve

3
지금, 나는 당신의 정규식 기술에 크게 감탄하지만 그것을 유지 해야하는 것을 싫어합니다.
크리스 나이트

1
@Chris : 그렇습니다. 정규식은 쓰기 전용 언어입니다. =)이 특정 표현을 읽기가 어렵지는 않지만 |"또는"으로 읽는 경우 . 글쎄요 ... 어쩌면 ... 더 나빠 졌어요 = /
Jens

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

기록을 위해 다음은 거의 (*) 호환되는 스칼라 버전입니다.

  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

나는 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를 사용하여 문자열을 분할합니다. 그런 다음 첫 단어와 두문자어를 제외한 각 단어를 내립니다. 그것은 누군가에게 유용 할 것이라고 생각했습니다.


-2

나는 정규식 닌자가 아니므로 현재 위치와 이전 위치의 색인을 유지하면서 문자열을 반복합니다. 현재 위치가 대문자 인 경우 이전 위치 뒤에 공백을 삽입하고 각 인덱스를 증가시킵니다.


2
프시! 그 재미는 어디에 있습니까?
vbullinger

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.