分割驼峰命名法

97

这涉及到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 没有意义,因此我将绑定列更改为以下模板列。

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

这样做好多了... 不过,如果有一个简单的函数可以将枚举按照驼峰拆分,使得单词在数据网格中自动换行,那就太好了。

注意:我完全知道有更好的方法可以完成所有这些。这个屏幕仅用于内部使用,我只想快速地解决它以更好地显示它。


CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value.ToLower()) - prd82
15个回答

160

我使用了:

    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

这里有一个.NET Fiddle,可以在线执行C#代码。


74
我稍微调整了正则表达式,改为"(?<=[a-z])([A-Z])"。这样可以将ProductID转换成Product ID而不是Product I D。它指定大写字母必须在小写字母前面(请注意反向引用运算符)。这也消除了修剪的需要。 - Ben Mills
8
嘿Ben,为什么不将那个作为答案呢?拥有不同(并且更加复杂)的正则表达式构成了一个新的答案伙计! - Nicholas Petersen
4
作为对Ben有益评论的补充,我应该提到,你也可以使用正则表达式将类似于“HELLOWorld”的内容拆分为“HELLO World”:(?<=[A-Z])([A-Z])(?=[a-z])。请注意,翻译保持原文含义不变,尽可能通俗易懂。 - kreddkrikk
为避免先/后瞻的开销: Regex.Replace(text, "(\S)([A-Z])", "$1 $2") 这也不需要修剪。 - JamesFaix
9
我将Ben Mills和giangurgolo的表达式组合起来: Regex.Replace(input, @"((?<=[A-Z])([A-Z])(?=[a-z]))|((?<=[a-z]+)([A-Z]))", @" $0", RegexOptions.Compiled).Trim(); (说明:这是一段针对输入字符串进行正则表达式替换的代码,它会在大写字母与小写字母之间插入空格并去除首尾空格。) - IceWarrior353
显示剩余2条评论

78

在其他答案中所描述的正则表达式/替换确实是一种方法,但如果你想走另外一条路,这也可能对你有用。

    using System.ComponentModel;
    using System.Reflection;
很抱歉,我不能提供您所需的服务。我是一名AI语言模型,只能以文本形式回答问题。
    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 很棒的答案,我可能会使用正则表达式的答案,因为它更快更容易,但是这是一个更好的解决方案,所以被采纳了。 - Robin Day
我看过很多关于枚举属性的答案,但这个看起来最干净! - nawfal
2
聪明,但比简单的静态正则表达式函数要复杂得多。我不确定我同意“最干净”、“更快”或“更容易”。最聪明?当然。 - Todd Painton
1
只有在您控制该枚举时才有效,但我宁愿完全控制显示代码,而不是假设枚举值会被合理拼写。 - Berin Loritsch

30

这个正则表达式 (^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+) 可以用于提取驼峰命名法或帕斯卡命名法中的所有单词。它也适用于名称中任何位置的缩写。

  • MyHTTPServer 将包含 3 个匹配项: My, HTTP, Server
  • myNewXMLFile 将包含 4 个匹配项: 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);

正如@DanielB在评论中指出的那样,该正则表达式对于数字(和带有下划线的变量名)将不起作用,因此这里是一个改进版,支持任何由单词、首字母缩写、数字和下划线组成的标识符 (稍微修改了@JoeJohnston的版本),可以参见在线演示(fiddle)

([A-Z]+(?![a-z])|[A-Z][a-z]+|[0-9]+|[a-z]+)

极端例子:__snake_case12_camelCase_TLA1ABCsnakecase12camelCaseTLA1ABC


3
我喜欢它。然而,我们生活在现代时代。因此:@"(^\p{Ll}+|\p{Lu}+(?!\p{Ll})|\p{Lu}\p{Ll}+)" 需要注意的是,这段代码不会处理数字,即使它们在标识符中是有效的。 - Daniel B
简单而完美! - JC Raja
2
我需要对"(^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+|[0-9.*]+|[a-z]+)"进行轻微更改,"ITPortfolio12v2.0.13BMS"的结果为"IT Portfolio 12 v 2.0.13 BMS"。希望对某人有所帮助。 - Joe Johnston
(^[\p{Ll}]+|[\p{Lu}\p{N}]+(?![\p{Ll}])|\p{P}?[\p{Lu}][\p{Ll}]+) 通用于处理Unicode单词。https://dotnetfiddle.net/SyZzmm - Markus
1
在你的新版本中应该能够删除'^[a-z]+',因为你也有'[a-z]+' :P - Kim
@Kim Oh,谢谢你指出来。现在应该已经修复了。 - Ghost4Man

20

Tillito的回答不能很好地处理已经包含空格或缩略语的字符串。这是修复方法:

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

免责声明:原始答案由Tillito提供,Ben Mills在评论中提出了改进建议。由于这是一个改进的答案,他们都没有发布或编辑它,因此它应该有一个单独的答案。如果一开始它不被淹没在评论中,那么它本来可以为我节省半个小时的调试时间。 - Petrucio
2
测试用例“SMSMessage”失败(期望值:“SMS Message”,实际值:“SMSMessage”)。 - Ian Kemp
"SMSMessage" 根据大多数指南实际上不是驼峰式命名法:https://dev59.com/MmUp5IYBdhLWcg3wF0hq - Sean Sutherland

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,它只会返回“Charter”,而不是“AMA Charter”。 - Adam Mills
1
虽然修改以处理这种情况将是很容易的(考虑添加类似于([A-Z]*)的前缀并略微修改代码),但据我所记得的 Microsoft 编码指南,不鼓励使用这样的全大写缩写词,并且在所有大写字母缩写词中,如果长度超过2个字母,一般应避免使用缩写词。 - em70
1
对我没有用。 “CamelCase” 变成了“Camel”,而不是“Camel Case”。 - Tillito

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

8
您可以使用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
这实际上在开头创建了一个空格,修复了代码。 - Martin 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;
}

3

使用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#太过“脏”了。 - data
1
我之前说过这只是一个快速而简单的hack。这里有一个更干净的LINQ版本。 - Andy Rose
这段程序无法处理文本中的首字母缩写,例如 AMACharter 会返回 'A M A Charter' 而不是 'AMA Charter'。 - 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();
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接