.NET- "대문자"로 구분 된 문자열을 배열로 분할하려면 어떻게해야합니까?


114

이 문자열에서 어떻게 이동합니까 : "ThisIsMyCapsDelimitedString"

...이 문자열에 : "This Is My Caps Delimited String"

VB.net의 적은 코드 줄이 선호되지만 C #도 환영합니다.

건배!


1
"OldMacDonaldAndMrO'TooleWentToMcDonalds"를 처리해야 할 때 어떻게됩니까?
Grant Wagner

2
제한된 사용 만 보게 될 것입니다. 나는 주로 이것을 사용하여 ThisIsMySpecialVariable과 같은 변수 이름을 구문 분석 할 것입니다.
Matias Nino

이것은 나를 위해 일했습니다 : Regex.Replace(s, "([A-Z0-9]+)", " $1").Trim(). 각 대문자로 나누려면 더하기 만 제거하면됩니다.
Mladen B.

답변:


173

나는 이것을 얼마 전에 만들었다. CamelCase 이름의 각 구성 요소와 일치합니다.

/([A-Z]+(?=$|[A-Z][a-z])|[A-Z]?[a-z]+)/g

예를 들면 :

"SimpleHTTPServer" => ["Simple", "HTTP", "Server"]
"camelCase" => ["camel", "Case"]

단어 사이에 공백을 삽입하도록 변환하려면 :

Regex.Replace(s, "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 ")

숫자를 처리해야하는 경우 :

/([A-Z]+(?=$|[A-Z][a-z]|[0-9])|[A-Z]?[a-z]+|[0-9]+)/g

Regex.Replace(s,"([a-z](?=[A-Z]|[0-9])|[A-Z](?=[A-Z][a-z]|[0-9])|[0-9](?=[^0-9]))","$1 ")

1
CamelCase! 그것이 바로 그 이름입니다! 나는 그것을 사랑한다! 고맙습니다!
Matias Nino

19
실제로 camelCase에는 선행 소문자가 있습니다. 여기서 말하는 것은 PascalCase입니다.
Drew Noakes

12
... 그리고 "낙타 케이스"또는 "파스칼 케이스"가 될 수있는 것을 언급 할 때 "인터 캡드"라고합니다
Chris

것 내 사용 사례를 실패 "Take5"분할하지 않습니다
PandaWood

1
@PandaWood Digits는 질문에 없었기 때문에 내 대답은 그들을 설명하지 않았습니다. 숫자를 설명하는 패턴의 변형을 추가했습니다.
Markus Jarderot

36
Regex.Replace("ThisIsMyCapsDelimitedString", "(\\B[A-Z])", " $1")

이것은 지금까지 최고의 솔루션이지만 컴파일하려면 \\ B를 사용해야합니다. 그렇지 않으면 컴파일러는 \ B를 이스케이프 시퀀스로 처리하려고합니다.
Ferruccio

좋은 솔루션입니다. 이것이 허용되는 대답이 아니어야하는 이유를 누구든지 생각할 수 있습니까? 능력이 부족하거나 성능이 떨어지는가?
Drew Noakes

8
이것은 연속 된 대문자를 별도의 단어로 취급합니다 (예 : ANZAC은 5 단어). MizardX의 대답은 (정확히 IMHO) 그것을 하나의 단어로 취급합니다.
Ray

2
@Ray, 나는 "ANZAC"이 영어 케이스가 아니기 때문에 파스칼 케이스 단어로 간주되기 위해 "Anzac"로 작성되어야한다고 주장하고 싶습니다.
Sam

1
@Neaox, 영어로는 그래야하지만 이것은 두문자어 케이스 나 일반 영어 케이스가 아닙니다. 대문자로 구분됩니다. 원본 텍스트를 일반 영어와 같은 방식으로 대문자로 표시해야하는 경우 다른 문자도 대문자로 표시해서는 안됩니다. 예를 들어, "is"의 "i"는 대문자로 구분 된 형식에 맞도록 대문자로 표시해야하지만 "ANZAC"의 "NZAC"는 사용하지 않아야하는 이유는 무엇입니까? 엄밀히 말하면 "ANZAC"을 대문자로 구분하는 것으로 해석하면 각 문자에 대해 하나씩 5 단어입니다.

19

좋은 대답, MizardX! "Address Line1"이 "Address Line1"대신 "Address Line 1"이되도록 숫자를 별도의 단어로 처리하도록 약간 조정했습니다.

Regex.Replace(s, "([a-z](?=[A-Z0-9])|[A-Z](?=[A-Z][a-z]))", "$1 ")

2
큰 추가! 나는 받아 들여지는 대답이 문자열의 숫자를 처리하는 것에 놀라지 않을 것이라고 생각합니다. :)
Jordan Gray

이 글을 올린 지 거의 8 년이 지났지 만 저에게도 완벽하게 작동했습니다. :) 처음에는 숫자가 나를 넘어 뜨 렸습니다.
Michael Armes

내 2 가지 특이 치 테스트를 통과 한 유일한 답변 : "Take5"-> "Take 5", "PublisherID"-> "Publisher ID". 나는이 두 번 upvote에 할
PandaWood

18

약간의 다양성을 위해 ... 여기 정규식을 사용하지 않는 확장 방법이 있습니다.

public static class CamelSpaceExtensions
{
    public static string SpaceCamelCase(this String input)
    {
        return new string(Enumerable.Concat(
            input.Take(1), // No space before initial cap
            InsertSpacesBeforeCaps(input.Skip(1))
        ).ToArray());
    }

    private static IEnumerable<char> InsertSpacesBeforeCaps(IEnumerable<char> input)
    {
        foreach (char c in input)
        {
            if (char.IsUpper(c)) 
            { 
                yield return ' '; 
            }

            yield return c;
        }
    }
}

Trim () 사용을 피하기 위해 foreach 앞에 다음을 입력했습니다. int counter = -1. 안에 counter ++를 ​​추가하세요. 체크를 다음과 같이 변경하십시오. if (char.IsUpper (c) && counter> 0)
Box 개발자 외부

첫 번째 문자 앞에 공백을 삽입합니다.
Zar Shardan

@ZarShardan이 지적한 문제를 자유롭게 수정할 수 있습니다. 변경 사항이 마음에 들지 않으면 언제든지 롤백하거나 수정 사항을 수정하십시오.
jpmc26

예를 들어 일련의 대문자에서 마지막 대문자 앞에 공백을 추가하여 약어를 처리하도록 개선 할 수 있습니까? 예를 들어 BOEForecast => BOE Forecast
Nepaluz

11

그랜트 와그너의 훌륭한 코멘트는 제쳐두고 :

Dim s As String = RegularExpressions.Regex.Replace("ThisIsMyCapsDelimitedString", "([A-Z])", " $1")

좋은 점 ... 원하는 .substring (), .trimstart (), .trim (), .remove () 등을 자유롭게 삽입하십시오. :)
의사 Masochist

9

약어와 숫자를 지원하는 솔루션이 필요했습니다. 이 Regex 기반 솔루션은 다음 패턴을 개별 "단어"로 취급합니다.

  • 대문자와 소문자
  • 연속 된 숫자
  • 연속 대문자 (두문자어로 해석 됨)-새 단어는 마지막 대문자를 사용하여 시작할 수 있습니다. 예 : HTMLGuide => "HTML Guide", "TheATeam"=> "The A Team"

한 줄로 수 있습니다.

Regex.Replace(value, @"(?<!^)((?<!\d)\d|(?(?<=[A-Z])[A-Z](?=[a-z])|[A-Z]))", " $1")

더 읽기 쉬운 접근 방식이 더 좋을 수 있습니다.

using System.Text.RegularExpressions;

namespace Demo
{
    public class IntercappedStringHelper
    {
        private static readonly Regex SeparatorRegex;

        static IntercappedStringHelper()
        {
            const string pattern = @"
                (?<!^) # Not start
                (
                    # Digit, not preceded by another digit
                    (?<!\d)\d 
                    |
                    # Upper-case letter, followed by lower-case letter if
                    # preceded by another upper-case letter, e.g. 'G' in HTMLGuide
                    (?(?<=[A-Z])[A-Z](?=[a-z])|[A-Z])
                )";

            var options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled;

            SeparatorRegex = new Regex(pattern, options);
        }

        public static string SeparateWords(string value, string separator = " ")
        {
            return SeparatorRegex.Replace(value, separator + "$1");
        }
    }
}

다음은 (XUnit) 테스트에서 발췌 한 것입니다.

[Theory]
[InlineData("PurchaseOrders", "Purchase-Orders")]
[InlineData("purchaseOrders", "purchase-Orders")]
[InlineData("2Unlimited", "2-Unlimited")]
[InlineData("The2Unlimited", "The-2-Unlimited")]
[InlineData("Unlimited2", "Unlimited-2")]
[InlineData("222Unlimited", "222-Unlimited")]
[InlineData("The222Unlimited", "The-222-Unlimited")]
[InlineData("Unlimited222", "Unlimited-222")]
[InlineData("ATeam", "A-Team")]
[InlineData("TheATeam", "The-A-Team")]
[InlineData("TeamA", "Team-A")]
[InlineData("HTMLGuide", "HTML-Guide")]
[InlineData("TheHTMLGuide", "The-HTML-Guide")]
[InlineData("TheGuideToHTML", "The-Guide-To-HTML")]
[InlineData("HTMLGuide5", "HTML-Guide-5")]
[InlineData("TheHTML5Guide", "The-HTML-5-Guide")]
[InlineData("TheGuideToHTML5", "The-Guide-To-HTML-5")]
[InlineData("TheUKAllStars", "The-UK-All-Stars")]
[InlineData("AllStarsUK", "All-Stars-UK")]
[InlineData("UKAllStars", "UK-All-Stars")]

1
+ 1은 정규식을 설명하고 읽을 수 있도록합니다. 그리고 저는 새로운 것을 배웠습니다. .NET Regex에는 빈 공간 모드와 주석이 있습니다. 감사합니다!
Felix Keil

4

더 다양하게, 일반 오래된 C # 개체를 사용하여 다음은 @MizardX의 우수한 정규식과 동일한 출력을 생성합니다.

public string FromCamelCase(string camel)
{   // omitted checking camel for null
    StringBuilder sb = new StringBuilder();
    int upperCaseRun = 0;
    foreach (char c in camel)
    {   // append a space only if we're not at the start
        // and we're not already in an all caps string.
        if (char.IsUpper(c))
        {
            if (upperCaseRun == 0 && sb.Length != 0)
            {
                sb.Append(' ');
            }
            upperCaseRun++;
        }
        else if( char.IsLower(c) )
        {
            if (upperCaseRun > 1) //The first new word will also be capitalized.
            {
                sb.Insert(sb.Length - 1, ' ');
            }
            upperCaseRun = 0;
        }
        else
        {
            upperCaseRun = 0;
        }
        sb.Append(c);
    }

    return sb.ToString();
}

2
와, 못 생겼 네요. 이제 내가 왜 정규식을 너무 좋아하는지 기억합니다! 노력은 +1. ;)
Mark Brackett

3

다음은 다음을 제목 케이스로 변환하는 프로토 타입입니다.

  • 뱀 케이스
  • 낙타 케이스
  • PascalCase
  • 문장 케이스
  • 제목 대소 문자 (현재 형식 유지)

분명히 "ToTitleCase"메소드 만 필요합니다.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;

public class Program
{
    public static void Main()
    {
        var examples = new List<string> { 
            "THEQuickBrownFox",
            "theQUICKBrownFox",
            "TheQuickBrownFOX",
            "TheQuickBrownFox",
            "the_quick_brown_fox",
            "theFOX",
            "FOX",
            "QUICK"
        };

        foreach (var example in examples)
        {
            Console.WriteLine(ToTitleCase(example));
        }
    }

    private static string ToTitleCase(string example)
    {
        var fromSnakeCase = example.Replace("_", " ");
        var lowerToUpper = Regex.Replace(fromSnakeCase, @"(\p{Ll})(\p{Lu})", "$1 $2");
        var sentenceCase = Regex.Replace(lowerToUpper, @"(\p{Lu}+)(\p{Lu}\p{Ll})", "$1 $2");
        return new CultureInfo("en-US", false).TextInfo.ToTitleCase(sentenceCase);
    }
}

콘솔 출력은 다음과 같습니다.

THE Quick Brown Fox
The QUICK Brown Fox
The Quick Brown FOX
The Quick Brown Fox
The Quick Brown Fox
The FOX
FOX
QUICK

참조 된 블로그 게시물


2
string s = "ThisIsMyCapsDelimitedString";
string t = Regex.Replace(s, "([A-Z])", " $1").Substring(1);

나는 쉬운 RegEx 방법이있을 것이라는 것을 알고 있었다. 나는 그것을 더 사용하기 시작해야한다.
Max Schmeling

1
정규식 전문가는 아니지만 "HeresAWTFString"은 어떻게됩니까?
Nick

1
"Heres AWTF String"이 표시되지만 Matias Nino가 질문에서 요청한 것입니다.
Max Schmeling

네, 그는 "인접한 여러 대문자가 홀로 남아있다"고 덧붙여 야합니다. 예를 들어 "PublisherID"는 "Publisher I D"로갑니다. 이것은 끔찍한 일입니다
PandaWood

2

Regex는 간단한 루프보다 약 10-12 배 느립니다.

    public static string CamelCaseToSpaceSeparated(this string str)
    {
        if (string.IsNullOrEmpty(str))
        {
            return str;
        }

        var res = new StringBuilder();

        res.Append(str[0]);
        for (var i = 1; i < str.Length; i++)
        {
            if (char.IsUpper(str[i]))
            {
                res.Append(' ');
            }
            res.Append(str[i]);

        }
        return res.ToString();
    }

1

순진한 정규식 솔루션. O'Conner를 처리하지 않으며 문자열 시작 부분에도 공백을 추가합니다.

s = "ThisIsMyCapsDelimitedString"
split = Regex.Replace(s, "[A-Z0-9]", " $&");

나는 당신을 개조했지만 사람들은 일반적으로 "순진한"으로 시작하지 않으면 더 나은 smackdown을 취합니다.
MusiGenesis

나는 그것이 smackdown이라고 생각하지 않습니다. 이러한 맥락에서 순진함은 일반적으로 명백하거나 단순함을 의미합니다 (즉, 반드시 최상의 솔루션은 아님). 모욕의 의도가 없습니다.
Ferruccio

0

아마도 더 우아한 해결책이있을 것입니다. 그러나 이것이 제가 머리 위에서 생각 해낸 것입니다.

string myString = "ThisIsMyCapsDelimitedString";

for (int i = 1; i < myString.Length; i++)
{
     if (myString[i].ToString().ToUpper() == myString[i].ToString())
     {
          myString = myString.Insert(i, " ");
          i++;
     }
}

0

사용해보십시오

"([A-Z]*[^A-Z]*)"

결과는 숫자와 알파벳 혼합에 적합합니다.

Regex.Replace("AbcDefGH123Weh", "([A-Z]*[^A-Z]*)", "$1 ");
Abc Def GH123 Weh  

Regex.Replace("camelCase", "([A-Z]*[^A-Z]*)", "$1 ");
camel Case  

0

https://stackoverflow.com/a/5796394/4279201 에서 의사 코드 구현

    private static StringBuilder camelCaseToRegular(string i_String)
    {
        StringBuilder output = new StringBuilder();
        int i = 0;
        foreach (char character in i_String)
        {
            if (character <= 'Z' && character >= 'A' && i > 0)
            {
                output.Append(" ");
            }
            output.Append(character);
            i++;
        }
        return output;
    }


0

절차적이고 빠른 impl :

  /// <summary>
  /// Get the words in a code <paramref name="identifier"/>.
  /// </summary>
  /// <param name="identifier">The code <paramref name="identifier"/></param> to extract words from.
  public static string[] GetWords(this string identifier) {
     Contract.Ensures(Contract.Result<string[]>() != null, "returned array of string is not null but can be empty");
     if (identifier == null) { return new string[0]; }
     if (identifier.Length == 0) { return new string[0]; }

     const int MIN_WORD_LENGTH = 2;  //  Ignore one letter or one digit words

     var length = identifier.Length;
     var list = new List<string>(1 + length/2); // Set capacity, not possible more words since we discard one char words
     var sb = new StringBuilder();
     CharKind cKindCurrent = GetCharKind(identifier[0]); // length is not zero here
     CharKind cKindNext = length == 1 ? CharKind.End : GetCharKind(identifier[1]);

     for (var i = 0; i < length; i++) {
        var c = identifier[i];
        CharKind cKindNextNext = (i >= length - 2) ? CharKind.End : GetCharKind(identifier[i + 2]);

        // Process cKindCurrent
        switch (cKindCurrent) {
           case CharKind.Digit:
           case CharKind.LowerCaseLetter:
              sb.Append(c); // Append digit or lowerCaseLetter to sb
              if (cKindNext == CharKind.UpperCaseLetter) {
                 goto TURN_SB_INTO_WORD; // Finish word if next char is upper
              }
              goto CHAR_PROCESSED;
           case CharKind.Other:
              goto TURN_SB_INTO_WORD;
           default:  // charCurrent is never Start or End
              Debug.Assert(cKindCurrent == CharKind.UpperCaseLetter);
              break;
        }

        // Here cKindCurrent is UpperCaseLetter
        // Append UpperCaseLetter to sb anyway
        sb.Append(c); 

        switch (cKindNext) {
           default:
              goto CHAR_PROCESSED;

           case CharKind.UpperCaseLetter: 
              //  "SimpleHTTPServer"  when we are at 'P' we need to see that NextNext is 'e' to get the word!
              if (cKindNextNext == CharKind.LowerCaseLetter) {
                 goto TURN_SB_INTO_WORD;
              }
              goto CHAR_PROCESSED;

           case CharKind.End:
           case CharKind.Other:
              break; // goto TURN_SB_INTO_WORD;
        }

        //------------------------------------------------

     TURN_SB_INTO_WORD:
        string word = sb.ToString();
        sb.Length = 0;
        if (word.Length >= MIN_WORD_LENGTH) {  
           list.Add(word);
        }

     CHAR_PROCESSED:
        // Shift left for next iteration!
        cKindCurrent = cKindNext;
        cKindNext = cKindNextNext;
     }

     string lastWord = sb.ToString();
     if (lastWord.Length >= MIN_WORD_LENGTH) {
        list.Add(lastWord);
     }
     return list.ToArray();
  }
  private static CharKind GetCharKind(char c) {
     if (char.IsDigit(c)) { return CharKind.Digit; }
     if (char.IsLetter(c)) {
        if (char.IsUpper(c)) { return CharKind.UpperCaseLetter; }
        Debug.Assert(char.IsLower(c));
        return CharKind.LowerCaseLetter;
     }
     return CharKind.Other;
  }
  enum CharKind {
     End, // For end of string
     Digit,
     UpperCaseLetter,
     LowerCaseLetter,
     Other
  }

테스트 :

  [TestCase((string)null, "")]
  [TestCase("", "")]

  // Ignore one letter or one digit words
  [TestCase("A", "")]
  [TestCase("4", "")]
  [TestCase("_", "")]
  [TestCase("Word_m_Field", "Word Field")]
  [TestCase("Word_4_Field", "Word Field")]

  [TestCase("a4", "a4")]
  [TestCase("ABC", "ABC")]
  [TestCase("abc", "abc")]
  [TestCase("AbCd", "Ab Cd")]
  [TestCase("AbcCde", "Abc Cde")]
  [TestCase("ABCCde", "ABC Cde")]

  [TestCase("Abc42Cde", "Abc42 Cde")]
  [TestCase("Abc42cde", "Abc42cde")]
  [TestCase("ABC42Cde", "ABC42 Cde")]
  [TestCase("42ABC", "42 ABC")]
  [TestCase("42abc", "42abc")]

  [TestCase("abc_cde", "abc cde")]
  [TestCase("Abc_Cde", "Abc Cde")]
  [TestCase("_Abc__Cde_", "Abc Cde")]
  [TestCase("ABC_CDE_FGH", "ABC CDE FGH")]
  [TestCase("ABC CDE FGH", "ABC CDE FGH")] // Should not happend (white char) anything that is not a letter/digit/'_' is considered as a separator
  [TestCase("ABC,CDE;FGH", "ABC CDE FGH")] // Should not happend (,;) anything that is not a letter/digit/'_' is considered as a separator
  [TestCase("abc<cde", "abc cde")]
  [TestCase("abc<>cde", "abc cde")]
  [TestCase("abc<D>cde", "abc cde")]  // Ignore one letter or one digit words
  [TestCase("abc<Da>cde", "abc Da cde")]
  [TestCase("abc<cde>", "abc cde")]

  [TestCase("SimpleHTTPServer", "Simple HTTP Server")]
  [TestCase("SimpleHTTPS2erver", "Simple HTTPS2erver")]
  [TestCase("camelCase", "camel Case")]
  [TestCase("m_Field", "Field")]
  [TestCase("mm_Field", "mm Field")]
  public void Test_GetWords(string identifier, string expectedWordsStr) {
     var expectedWords = expectedWordsStr.Split(' ');
     if (identifier == null || identifier.Length <= 1) {
        expectedWords = new string[0];
     }

     var words = identifier.GetWords();
     Assert.IsTrue(words.SequenceEqual(expectedWords));
  }

0

특히 입력 문자열의 크기가 커짐에 따라 정규식 솔루션 (이 스레드의 상위 솔루션에 대해 실행 한 테스트를 기반으로 함)보다 훨씬 더 빠른 간단한 솔루션입니다.

string s1 = "ThisIsATestStringAbcDefGhiJklMnoPqrStuVwxYz";
string s2;
StringBuilder sb = new StringBuilder();

foreach (char c in s1)
    sb.Append(char.IsUpper(c)
        ? " " + c.ToString()
        : c.ToString());

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