キャメルケースの分割


80

これはすべてasp.netc#です。

私は列挙型を持っています

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>

これははるかに優れています...ただし、列挙型をキャメルケースで分割してデータグリッドで単語が適切にラップされるように、列挙型を囲むことができる単純な関数があれば素晴らしいと思います。

注:私は、これらすべてを行うためのより良い方法があることを十分に認識しています。この画面は純粋に内部で使用されているので、少しだけ見栄えを良くするために簡単にハックしてください。

回答:


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

これにより、列挙型を次のように定義できます。

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すばらしい答えです。より速くて簡単なので、おそらく正規表現の答えを使用しますが、これははるかに優れた解決策であり、受け入れられます。
ロビン・デイ

私は列挙型属性について多くの答えを見てきましたが、これは最もきれいに見えます!
nawfal 2012

2
賢いですが、単純な静的正規表現関数よりもはるかに多くの作業が必要です。「最もクリーン」または「クイック」または「より簡単」に同意するかどうかはわかりません。賢い?確かに。
Todd Painton 2015年

1
これは、その列挙型を制御できる場合にのみ機能しますが、列挙型の値が正しくスペルされていると想定するのではなく、表示コードを完全に制御したいと思います。
BerinLoritsch18年

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ではなくProductIDに変換されます。これは、大文字の前に小文字を付ける必要があることを指定します(後読み演算子に注意してください)。また、トリムの必要性を排除します。
ベン・ミルズ

7
ベンさん、それを答えてみませんか。別の(そしてより洗練された)正規表現を持つことは、新しい答えの仲間を構成します!
ニコラスピーターセン

3
ベンの有益なコメントに加えて、正規表現を使用して「HELLOWorld」のようなものを「HELLOWorld」に分割することもできます:(?<= [AZ])([AZ])(?= [az] )
giangurgolo 2015

6
ベンミルズとジャングルゴロの式を組み合わせました:Regex.Replace(input、@ "((?<= [AZ])([AZ])(?= [az]))|((?<= [az] +) ([AZ])) "、@" $ 0 "、RegexOptions.Compiled).Trim();
IceWarrior353 2017

23

この正規表現(^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+)を使用して、キャメルケースまたはパスカルケースの名前からすべての単語を抽出できます。また、名前内の任意の場所の略語でも機能します。

  • MyHTTPServer正確に3つの試合が含まれます:MyHTTPServer
  • myNewXMLFile4つの試合が含まれます:myNewXMLFile

次に、を使用してそれらを1つの文字列に結合できます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}+)"、識別子で有効であっても、これでは数値がまったく機能しないことに注意することも重要です。
ダニエルB

シンプルでありながら完璧!
JCラジャ

1
「(^ [az] + | [AZ] +(?![az])| [AZ] [az] + | [0-9 \。*] + | [az] +)」に少し変更する必要がありました"12 V 2.0.13 BMS ITポートフォリオ"の"ITPortfolio12v2.0.13BMS"結果HTH誰か
ジョー・ジョンストン

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を処理せず、「AMACharter」ではなく「Charter」を返します。
Adam Mills

このような場合を処理するための変更は簡単ですが(([AZ] *)のようなものを追加し、コードを少し変更することを考えてください)、Microsoftのコーディングガイドラインを思い出すと、このようなすべて大文字の頭字語の使用は推奨されていません。オールキャップスの頭字語一般的な頭字語は、2文字より長い場合は避けてください。
em70 2010年

1
私にはうまくいきません。「CamelCase」は「CamelCase」ではなく「Camel」になります。
Tillito 2011年

15

Tillitoの答えは、すでにスペースを含む文字列や頭字語をうまく処理しません。これはそれを修正します:

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

免責事項:クレジットは、元の回答の提供者であるTillitoと、コメントで改善を提案したBenMillsに帰属します。これは改善された回答であり、投稿も編集もされていないため、別の回答に値します。そもそもコメントに埋もれていなければ、30分のデバッグを節約できただろう。
ペトルシオ2014

2
単純なテストケース「SMSMessage」で失敗します(予想:「SMSメッセージ」、実際:「SMSMessage」)。
イアン・ケンプ

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
これは実際に最初にスペースを作成し、コードを修正しました
MartinZikmund19年

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バージョンです。
アンディローズ

これは、AMACharterのようにテキスト内のAcroynmsを処理せず、「AMACharter」ではなく「AMACharter」を返します
Adam Mills

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のソリューションは、Webサービスがある場合を除いてうまく機能します。

説明属性はシリアル化できないため、次のことを行う必要があります。

[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とnumbersを含む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.