CamelCase 분할


80

이것은 모두 asp.net c #입니다.

열거 형이 있습니다

public enum ControlSelectionType 
{
    NotApplicable = 1,
    SingleSelectRadioButtons = 2,
    SingleSelectDropDownList = 3,
    MultiSelectCheckBox = 4,
    MultiSelectListBox = 5
}

이 수치는 내 데이터베이스에 저장됩니다. 이 값을 데이터 그리드에 표시합니다.

<asp:boundcolumn datafield="ControlSelectionTypeId" headertext="Control Type"></asp:boundcolumn>

ID는 사용자에게 아무런 의미가 없으므로 boundcolumn을 다음과 같이 템플릿 열로 변경했습니다.

<asp:TemplateColumn>
    <ItemTemplate>
        <%# Enum.Parse(typeof(ControlSelectionType), DataBinder.Eval(Container.DataItem, "ControlSelectionTypeId").ToString()).ToString()%>
    </ItemTemplate>
</asp:TemplateColumn>

이것은 훨씬 낫습니다 ... 그러나 Enum 주위에 넣어서 Camel 케이스로 분할하여 단어가 데이터 그리드에서 멋지게 래핑되도록 할 수있는 간단한 함수가 있다면 좋을 것입니다.

참고 :이 모든 작업을 수행하는 더 좋은 방법이 있다는 것을 잘 알고 있습니다. 이 화면은 순전히 내부적으로 사용되며 좀 더 잘 표시 할 수있는 빠른 해킹이 필요합니다.

답변:


76

실제로 정규식 / 교체는 다른 답변에 설명 된대로가는 방법이지만 다른 방향으로 가고 싶다면 이것은 또한 유용 할 수 있습니다

    using System.ComponentModel;
    using System.Reflection;

...

    public static string GetDescription(System.Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attributes.Length > 0)
            return attributes[0].Description;
        else
            return value.ToString();
    }

이렇게하면 Enum을 다음과 같이 정의 할 수 있습니다.

public enum ControlSelectionType 
{
    [Description("Not Applicable")]
    NotApplicable = 1,
    [Description("Single Select Radio Buttons")]
    SingleSelectRadioButtons = 2,
    [Description("Completely Different Display Text")]
    SingleSelectDropDownList = 3,
}

에서 가져옴

http://www.codeguru.com/forum/archive/index.php/t-412868.html


+1 좋은 대답, 더 빠르고 쉬운 정규식 대답을 사용할 것입니다. 그러나 이것은 훨씬 더 나은 솔루션이므로 받아 들일 수 있습니다.
Robin Day

열거 형 속성에 대한 많은 답변을 보았지만 이것이 가장 깨끗해 보입니다!
nawfal

2
영리하지만 단순한 정적 정규식 함수보다 훨씬 더 많은 작업입니다. "가장 깔끔함", "빠른 속도"또는 "쉬움"에 동의하는지 잘 모르겠습니다. 영리한? 확실히.
Todd Painton

1
이는 해당 열거 형을 제어 할 수있는 경우에만 작동하지만 열거 형 값의 철자가 적절하다고 가정하는 대신 표시 코드를 완전히 제어 할 수 있습니다.
Berin Loritsch

131

나는 사용했다 :

    public static string SplitCamelCase(string input)
    {
        return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
    }

http://weblogs.asp.net/jgalloway/archive/2005/09/27/426087.aspx 에서 가져옴

vb.net :

Public Shared Function SplitCamelCase(ByVal input As String) As String
    Return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim()
End Function

낙타 케이스 부분을 완성하는 간단한 방법 ... 더 많은 사용자 정의를 위해 다른 방법이 더 좋습니다. 감사합니다 @Tillito
rolivares

62
정규식을 "(? <= [az]) ([AZ])"로 약간 조정했습니다. 이로 인해 ProductID가 Product ID 대신 Product ID로 변환됩니다. 대문자 앞에 소문자가 와야 함을 지정합니다 (lookbehind 연산자 참고). 또한 트림이 필요하지 않습니다.
Ben Mills

7
이봐 벤, 대답으로 넣지 그래. 다른 (더 정교한) 정규식을 갖는 것은 새로운 해답 메이트를 구성합니다!
Nicholas Petersen 2013

3
Ben의 유용한 설명에 덧붙여, 정규 표현식을 사용하여 "HELLOWorld"와 같은 것을 "HELLO World"로 분할 할 수도 있습니다. (? <= [AZ]) ([AZ]) (? = [az] )
giangurgolo

6
Ben Mills와 giangurgolo의 식을 결합했습니다. Regex.Replace (input, @ "((? <= [AZ]) ([AZ]) (? = [az])) | ((? <= [az] +) ([AZ])) ", @"$ 0 ", RegexOptions.Compiled) .Trim ();
IceWarrior353

23

이 정규식 (^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+)은 camelCase 또는 PascalCase 이름에서 모든 단어를 추출하는 데 사용할 수 있습니다. 또한 이름 안의 모든 약어와 함께 작동합니다.

  • MyHTTPServer정확히 3 경기를 포함합니다 : My, HTTP,Server
  • myNewXMLFile4 경기를 포함합니다 : my, New, XML,File

그런 다음을 사용하여 단일 문자열로 결합 할 수 있습니다 string.Join.

string name = "myNewUIControl";
string[] words = Regex.Matches(name, "(^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+)")
    .OfType<Match>()
    .Select(m => m.Value)
    .ToArray();
string result = string.Join(" ", words);

2
나는 그것을 좋아한다. 그러나 우리는 현대에 살고 있습니다. 따라서 @"(^\p{Ll}+|\p{Lu}+(?!\p{Ll})|\p{Lu}\p{Ll}+)"식별자에서 유효하더라도 숫자를 전혀 사용하지 않는다는 점도 중요합니다.
Daniel B

간단하면서도 완벽합니다!
JC Raja

1
"(^ [az] + | [AZ] + (?! [az]) | [AZ] [az] + | [0-9 \. *] + | [az] +)"에 약간의 변경이 필요했습니다. "ITPortfolio12v2.0.13BMS"결과 "IT Portfolio 12 v 2.0.13 BMS"hth someone
Joe Johnston 19

15

C # 3.0이 옵션 인 경우 다음 한 줄을 사용하여 작업을 수행 할 수 있습니다.


Regex.Matches(YOUR_ENUM_VALUE_NAME, "[A-Z][a-z]+").OfType<Match>().Select(match => match.Value).Aggregate((acc, b) => acc + " " + b).TrimStart(' ');

1
이것은 AMACharter와 같은 텍스트의 Acroynms를 처리하지 않고 'AMA Charter'가 아닌 'Charter'를 반환합니다.
Adam Mills

이러한 경우를 처리하기위한 수정은 쉬울 수 있지만 (([AZ] *)와 같은 것을 앞에 붙이고 코드를 약간 수정하는 것을 생각해보십시오.) Microsoft의 코딩 지침을 기억할 때 이러한 전체 대문자 약어를 사용하지 않는 것이 좋습니다. 모두 대문자 두문자어 일반 두문자어는 2 자 이상이면 피해야합니다.
em70

1
나를 위해 작동하지 않습니다. "CamelCase"는 "Camel Case"가 아니라 "Camel"이됩니다.
Tillito

15

Tillito의 답변은 이미 공백 또는 약어를 포함하는 문자열을 처리하지 않습니다. 이것은 그것을 수정합니다 :

public static string SplitCamelCase(string input)
{
    return Regex.Replace(input, "(?<=[a-z])([A-Z])", " $1", RegexOptions.Compiled);
}

면책 조항 : 크레딧은 원래 답변을 제공 한 Tillito와 의견 개선을 제안한 Ben Mills에게 있습니다. 개선 된 답변이고 아무도 게시하거나 편집하지 않았기 때문에 별도의 답변이 필요합니다. 처음부터 코멘트 아래에 묻히지 않았다면 30 분의 디버깅 시간을 절약 할 수 있었을 것입니다.
Petrucio 2014 년

2
단순 테스트 케이스 "SMSMessage"로 실패합니다 (예상 : "SMS 메시지", 실제 : "SMSMessage").
Ian Kemp 2014

10

다음은 숫자와 여러 대문자를 깔끔하게 처리하고 최종 문자열에서 특정 약어를 대문자로 표시하는 확장 메서드입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Web.Configuration;

namespace System
{
    /// <summary>
    /// Extension methods for the string data type
    /// </summary>
    public static class ConventionBasedFormattingExtensions
    {
        /// <summary>
        /// Turn CamelCaseText into Camel Case Text.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <remarks>Use AppSettings["SplitCamelCase_AllCapsWords"] to specify a comma-delimited list of words that should be ALL CAPS after split</remarks>
        /// <example>
        /// wordWordIDWord1WordWORDWord32Word2
        /// Word Word ID Word 1 Word WORD Word 32 Word 2
        /// 
        /// wordWordIDWord1WordWORDWord32WordID2ID
        /// Word Word ID Word 1 Word WORD Word 32 Word ID 2 ID
        /// 
        /// WordWordIDWord1WordWORDWord32Word2Aa
        /// Word Word ID Word 1 Word WORD Word 32 Word 2 Aa
        /// 
        /// wordWordIDWord1WordWORDWord32Word2A
        /// Word Word ID Word 1 Word WORD Word 32 Word 2 A
        /// </example>
        public static string SplitCamelCase(this string input)
        {
            if (input == null) return null;
            if (string.IsNullOrWhiteSpace(input)) return "";

            var separated = input;

            separated = SplitCamelCaseRegex.Replace(separated, @" $1").Trim();

            //Set ALL CAPS words
            if (_SplitCamelCase_AllCapsWords.Any())
                foreach (var word in _SplitCamelCase_AllCapsWords)
                    separated = SplitCamelCase_AllCapsWords_Regexes[word].Replace(separated, word.ToUpper());

            //Capitalize first letter
            var firstChar = separated.First(); //NullOrWhiteSpace handled earlier
            if (char.IsLower(firstChar))
                separated = char.ToUpper(firstChar) + separated.Substring(1);

            return separated;
        }

        private static readonly Regex SplitCamelCaseRegex = new Regex(@"
            (
                (?<=[a-z])[A-Z0-9] (?# lower-to-other boundaries )
                |
                (?<=[0-9])[a-zA-Z] (?# number-to-other boundaries )
                |
                (?<=[A-Z])[0-9] (?# cap-to-number boundaries; handles a specific issue with the next condition )
                |
                (?<=[A-Z])[A-Z](?=[a-z]) (?# handles longer strings of caps like ID or CMS by splitting off the last capital )
            )"
            , RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace
        );

        private static readonly string[] _SplitCamelCase_AllCapsWords =
            (WebConfigurationManager.AppSettings["SplitCamelCase_AllCapsWords"] ?? "")
                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(a => a.ToLowerInvariant().Trim())
                .ToArray()
                ;

        private static Dictionary<string, Regex> _SplitCamelCase_AllCapsWords_Regexes;
        private static Dictionary<string, Regex> SplitCamelCase_AllCapsWords_Regexes
        {
            get
            {
                if (_SplitCamelCase_AllCapsWords_Regexes == null)
                {
                    _SplitCamelCase_AllCapsWords_Regexes = new Dictionary<string,Regex>();
                    foreach(var word in _SplitCamelCase_AllCapsWords)
                        _SplitCamelCase_AllCapsWords_Regexes.Add(word, new Regex(@"\b" + word + @"\b", RegexOptions.Compiled | RegexOptions.IgnoreCase));
                }

                return _SplitCamelCase_AllCapsWords_Regexes;
            }
        }
    }
}

6

C # 확장 메서드를 사용할 수 있습니다.

        public static string SpacesFromCamel(this string value)
        {
            if (value.Length > 0)
            {
                var result = new List<char>();
                char[] array = value.ToCharArray();
                foreach (var item in array)
                {
                    if (char.IsUpper(item) && result.Count > 0)
                    {
                        result.Add(' ');
                    }
                    result.Add(item);
                }

                return new string(result.ToArray());
            }
            return value;
        }

그런 다음 다음과 같이 사용할 수 있습니다.

var result = "TestString".SpacesFromCamel();

결과는

테스트 문자열


1
이것은 실제로 코드를 고정 시작 부분에 공간을 만들어
마틴 Zikmund

3

나도 enum분리 해야하는 것도 있습니다 . 제 경우에는이 방법이 문제를 해결했습니다.

string SeparateCamelCase(string str)
{
    for (int i = 1; i < str.Length; i++)
    {
        if (char.IsUpper(str[i]))
        {
            str = str.Insert(i, " ");
            i++;
        }
    }
    return str;
}

2

LINQ 사용 :

var chars = ControlSelectionType.NotApplicable.ToString().SelectMany((x, i) => i > 0 && char.IsUpper(x) ? new char[] { ' ', x } : new char[] { x });

Console.WriteLine(new string(chars.ToArray()));

1
C \ C ++로 다시 코딩해야합니다. : D-C #에 비해 너무 더러움
데이터

1
나는 그것이 빠르고 더러운 해킹이라고 말했습니다. 다음은 더 깨끗한 LINQ 버전입니다.
Andy Rose

이 AMACharter 같은 텍스트 Acroynms을 처리하지 않습니다, 반환 'AMA 헌장'하지 'AMA 헌장
아담 밀스

2
public enum ControlSelectionType    
{   
    NotApplicable = 1,   
    SingleSelectRadioButtons = 2,   
    SingleSelectDropDownList = 3,   
    MultiSelectCheckBox = 4,   
    MultiSelectListBox = 5   
} 
public class NameValue
{
    public string Name { get; set; }
    public object Value { get; set; }
}    
public static List<NameValue> EnumToList<T>(bool camelcase)
        {
            var array = (T[])(Enum.GetValues(typeof(T)).Cast<T>()); 
            var array2 = Enum.GetNames(typeof(T)).ToArray<string>(); 
            List<NameValue> lst = null;
            for (int i = 0; i < array.Length; i++)
            {
                if (lst == null)
                    lst = new List<NameValue>();
                string name = "";
                if (camelcase)
                {
                    name = array2[i].CamelCaseFriendly();
                }
                else
                    name = array2[i];
                T value = array[i];
                lst.Add(new NameValue { Name = name, Value = value });
            }
            return lst;
        }
        public static string CamelCaseFriendly(this string pascalCaseString)
        {
            Regex r = new Regex("(?<=[a-z])(?<x>[A-Z])|(?<=.)(?<x>[A-Z])(?=[a-z])");
            return r.Replace(pascalCaseString, " ${x}");
        }

//In  your form 
protected void Button1_Click1(object sender, EventArgs e)
        {
            DropDownList1.DataSource = GeneralClass.EnumToList<ControlSelectionType  >(true); ;
            DropDownList1.DataTextField = "Name";
            DropDownList1.DataValueField = "Value";

            DropDownList1.DataBind();
        }

2

Eoin Campbell의 솔루션은 웹 서비스가있는 경우를 제외하고는 잘 작동합니다.

설명 속성이 직렬화 가능하지 않으므로 다음을 수행해야합니다.

[DataContract]
public enum ControlSelectionType
{
    [EnumMember(Value = "Not Applicable")]
    NotApplicable = 1,
    [EnumMember(Value = "Single Select Radio Buttons")]
    SingleSelectRadioButtons = 2,
    [EnumMember(Value = "Completely Different Display Text")]
    SingleSelectDropDownList = 3,
}


public static string GetDescriptionFromEnumValue(Enum value)
{
    EnumMemberAttribute attribute = value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(typeof(EnumMemberAttribute), false)
        .SingleOrDefault() as EnumMemberAttribute;
    return attribute == null ? value.ToString() : attribute.Value;
}

2

정규식을 사용하고 싶지 않다면 다음을 시도하십시오.

public static string SeperateByCamelCase(this string text, char splitChar = ' ') {

        var output = new StringBuilder();

        for (int i = 0; i < text.Length; i++)
        {
            var c = text[i];

            //if not the first and the char is upper
            if (i > 0 && char.IsUpper(c)) {

                var wasLastLower = char.IsLower(text[i - 1]);

                if (i + 1 < text.Length) //is there a next
                {
                    var isNextUpper = char.IsUpper(text[i + 1]);

                    if (!isNextUpper) //if next is not upper (start of a word).
                    {
                        output.Append(splitChar);
                    }
                    else if (wasLastLower) //last was lower but i'm upper and my next is an upper (start of an achromin). 'abcdHTTP' 'abcd HTTP'
                    {
                        output.Append(splitChar);
                    }
                }
                else
                {
                    //last letter - if its upper and the last letter was lower 'abcd' to 'abcd A'
                    if (wasLastLower)
                    {
                        output.Append(splitChar);
                    }
                }
            }

            output.Append(c);
        }


        return output.ToString();

    }

이 테스트를 통과하면 숫자를 좋아하지 않지만 필요하지 않았습니다.

    [TestMethod()]
    public void ToCamelCaseTest()
    {

        var testData = new string[] { "AAACamel", "AAA", "SplitThisByCamel", "AnA", "doesnothing", "a", "A", "aasdasdAAA" };
        var expectedData = new string[] { "AAA Camel", "AAA", "Split This By Camel", "An A", "doesnothing", "a", "A", "aasdasd AAA" };

        for (int i = 0; i < testData.Length; i++)
        {
            var actual = testData[i].SeperateByCamelCase();
            var expected = expectedData[i];
            Assert.AreEqual(actual, expected);
        }

    }

2

#JustSayNoToRegex

uderscores 및 숫자가있는 C # 식별자를 가져 와서 공백으로 구분 된 문자열로 변환합니다.

public static class StringExtensions
{
    public static string SplitOnCase(this string identifier)
    {
        if (identifier == null || identifier.Length == 0) return string.Empty;
        var sb = new StringBuilder();

        if (identifier.Length == 1) sb.Append(char.ToUpperInvariant(identifier[0]));

        else if (identifier.Length == 2) sb.Append(char.ToUpperInvariant(identifier[0])).Append(identifier[1]);

        else {
            if (identifier[0] != '_') sb.Append(char.ToUpperInvariant(identifier[0]));
            for (int i = 1; i < identifier.Length; i++) {
                var current = identifier[i];
                var previous = identifier[i - 1];

                if (current == '_' && previous == '_') continue;

                else if (current == '_') {
                    sb.Append(' ');
                }

                else if (char.IsLetter(current) && previous == '_') {
                    sb.Append(char.ToUpperInvariant(current));
                }

                else if (char.IsDigit(current) && char.IsLetter(previous)) {
                    sb.Append(' ').Append(current);
                }

                else if (char.IsLetter(current) && char.IsDigit(previous)) {
                    sb.Append(' ').Append(char.ToUpperInvariant(current));
                }

                else if (char.IsUpper(current) && char.IsLower(previous) 
                    && (i < identifier.Length - 1 && char.IsUpper(identifier[i + 1]) || i == identifier.Length - 1)) {
                        sb.Append(' ').Append(current);
                }

                else if (char.IsUpper(current) && i < identifier.Length - 1 && char.IsLower(identifier[i + 1])) {
                    sb.Append(' ').Append(current);
                }

                else {
                    sb.Append(current);
                }
            }
        }
        return sb.ToString();
    }

}

테스트 :

[TestFixture]
static class HelpersTests
{
    [Test]
    public static void Basic()
    {
        Assert.AreEqual("Foo", "foo".SplitOnCase());
        Assert.AreEqual("Foo", "_foo".SplitOnCase());
        Assert.AreEqual("Foo", "__foo".SplitOnCase());
        Assert.AreEqual("Foo", "___foo".SplitOnCase());
        Assert.AreEqual("Foo 2", "foo2".SplitOnCase());
        Assert.AreEqual("Foo 23", "foo23".SplitOnCase());
        Assert.AreEqual("Foo 23 A", "foo23A".SplitOnCase());
        Assert.AreEqual("Foo 23 Ab", "foo23Ab".SplitOnCase());
        Assert.AreEqual("Foo 23 Ab", "foo23_ab".SplitOnCase());
        Assert.AreEqual("Foo 23 Ab", "foo23___ab".SplitOnCase());
        Assert.AreEqual("Foo 23", "foo__23".SplitOnCase());
        Assert.AreEqual("Foo Bar", "Foo_bar".SplitOnCase());
        Assert.AreEqual("Foo Bar", "Foo____bar".SplitOnCase());
        Assert.AreEqual("AAA", "AAA".SplitOnCase());
        Assert.AreEqual("Foo A Aa", "fooAAa".SplitOnCase());
        Assert.AreEqual("Foo AAA", "fooAAA".SplitOnCase());
        Assert.AreEqual("Foo Bar", "FooBar".SplitOnCase());
        Assert.AreEqual("Mn M", "MnM".SplitOnCase());
        Assert.AreEqual("AS", "aS".SplitOnCase());
        Assert.AreEqual("As", "as".SplitOnCase());
        Assert.AreEqual("A", "a".SplitOnCase());
        Assert.AreEqual("_", "_".SplitOnCase());

    }
}

1

위의 일부와 유사한 간단한 버전이지만 현재 위치에 이미 하나가있는 경우 구분 기호 (기본적으로 공백이지만 문자가 될 수 있음)를 자동 삽입하지 않는 논리가 있습니다.

StringBuilder'변이하는'문자열 대신 사용합니다 .

public static string SeparateCamelCase(this string value, char separator = ' ') {

    var sb = new StringBuilder();
    var lastChar = separator;

    foreach (var currentChar in value) {

        if (char.IsUpper(currentChar) && lastChar != separator)
            sb.Append(separator);

        sb.Append(currentChar);

        lastChar = currentChar;
    }

    return sb.ToString();
}

예:

Input  : 'ThisIsATest'
Output : 'This Is A Test'

Input  : 'This IsATest'
Output : 'This Is A Test' (Note: Still only one space between 'This' and 'Is')

Input  : 'ThisIsATest' (with separator '_')
Output : 'This_Is_A_Test'

0

이 시도:

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        Console
            .WriteLine(
                SeparateByCamelCase("TestString") == "Test String" // True
            );
    }

    public static string SeparateByCamelCase(string str)
    {
        return String.Join(" ", SplitByCamelCase(str));
    }

    public static IEnumerable<string> SplitByCamelCase(string str) 
    {
        if (str.Length == 0) 
            return new List<string>();

        return 
            new List<string> 
            { 
                Head(str) 
            }
            .Concat(
                SplitByCamelCase(
                    Tail(str)
                )
            );
    }

    public static string Head(string str)
    {
        return new String(
                    str
                        .Take(1)
                        .Concat(
                            str
                                .Skip(1)
                                .TakeWhile(IsLower)
                        )
                        .ToArray()
                );
    }

    public static string Tail(string str)
    {
        return new String(
                    str
                        .Skip(
                            Head(str).Length
                        )
                        .ToArray()
                );
    }

    public static bool IsLower(char ch) 
    {
        return ch >= 'a' && ch <= 'z';
    }
}

온라인으로 샘플보기

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