【问题标题】:Splitting CamelCase拆分 CamelCase
【发布时间】:2009-04-21 15:49:00
【问题描述】:

这都是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 周围放置一个简单的函数,将它按骆驼大小写拆分,这样单词就可以很好地包裹在数据网格中。

注意:我完全清楚有更好的方法来做这一切。这个屏幕纯粹是在内部使用的,我只是想快速修改一下以更好地显示它。

【问题讨论】:

  • CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value.ToLower())

标签: c# asp.net string


【解决方案1】:

我用过:

    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
  • 我将正则表达式稍微调整为“(?
  • 嘿,Ben,你为什么不把它作为答案。拥有一个不同的(更复杂的)正则表达式构成了一个新的答案伴侣!
  • 作为对 Ben 有用评论的补充,我应该提一下,您还可以使用正则表达式将“HELLOWorld”之类的内容拆分为“HELLO World”:(?
  • 我结合了 Ben Mills 和 giangurgolo 的表达式:Regex.Replace(input, @"((?
【解决方案2】:

确实,正则表达式/替换是其他答案中描述的方法,但是如果您想走不同的方向,这也可能对您有用

    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 很好的答案,我可能会使用正则表达式答案,因为它更快更容易,但是,这是一个更好的解决方案,因此被接受了。
  • 我看过很多关于枚举属性的答案,但这看起来最干净!
  • 聪明,但它比简单的静态正则表达式函数要多得多。我不确定我是否同意“最干净”或“更快”或“更容易”。聪明人?当然。
  • 这仅在您可以控制该枚举时才有效,但我宁愿完全控制显示代码而不是假设枚举值将被正确拼写。
【解决方案3】:

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

  • MyHTTPServer 将包含 3 个匹配项:MyHTTPServer
  • myNewXMLFile 将包含 4 个匹配项:myNewXMLFile

然后您可以使用 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 在 cmets 中指出的那样,正则表达式不适用于数字(以及带有下划线),因此这是一个改进的版本,支持任何带有单词、首字母缩写词、数字、下划线的标识符 (稍微修改了@JoeJohnston 的版本),见online demo (fiddle)

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

极端例子:__snake_case12_camelCase_TLA1ABCsnakecase12camelCaseTLA1ABC

【讨论】:

  • 我喜欢它。然而,我们生活在现代。因此:@"(^\p{Ll}+|\p{Lu}+(?!\p{Ll})|\p{Lu}\p{Ll}+)" 同样重要的是要注意,这根本不会处理数字,即使它们在标识符中是有效的。
  • 简单而完美!
  • 我需要对“(^[az]+|[AZ]+(?![az])|[AZ][az]+|[0-9\.*] +|[az]+)" "ITPortfolio12v2.0.13BMS" 结果是 "IT Portfolio 12 v 2.0.13 BMS" hth某人
  • (^[\p{Ll}]+|[\p{Lu}\p{N}]+(?![\p{Ll}])|\p{P}? [\p{Lu}][\p{Ll}]+) 泛化的 ot 与 Unicode 单词一起工作。 dotnetfiddle.net/SyZzmm
  • 在您的新改进版本中,您应该能够删除 '^[a-z]+',因为您还有 '[a-z]+' :P
【解决方案4】:

Tillito 的答案不能很好地处理已经包含空格的字符串或首字母缩略词。这解决了它:

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

【讨论】:

  • 免责声明:感谢原始答案的提供者 Tillito 和在评论中提出改进建议的 Ben Mills。由于这是一个改进的答案,并且没有人发布或编辑它,因此值得单独回答。如果不是一开始就被埋在 cmets 下,本来可以为我节省半个小时的调试时间。
  • 简单测试用例“SMSMessage”失败(预期:“SMS Message”,实际:“SMSMessage”)。
【解决方案5】:

如果 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(' ');

【讨论】:

  • 这不会像 AMACharter 那样处理文本中的 Acroynms,返回 'Charter' 而不是 'AMA Charter'。
  • 虽然处理这种情况的修改会很容易(考虑添加类似 ([AZ]*) 之类的东西并稍微修改代码),但我记得微软的编码指南中使用了所有这些不鼓励使用 -caps 首字母缩略词,如果长度超过 2 个字母,则应避免使用全大写首字母缩略词。
  • 对我不起作用。 “CamelCase”变成“Camel”而不是“Camel Case”。
【解决方案6】:

这是一个扩展方法,它可以很好地处理数字和多个大写字符,并且还允许在最终字符串中使用大写特定首字母缩略词:

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

【讨论】:

    【解决方案7】:

    您可以使用 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();
    

    结果将是

    测试字符串

    【讨论】:

    • 这实际上是在开头创建了一个空格,修复了代码
    【解决方案8】:

    使用 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()));
    

    【讨论】:

    • 你应该回到使用 C\C++ 进行编码:D - 对 C# 来说太脏了
    • 好吧,我确实说过这是一个快速而肮脏的黑客攻击。这是一个更简洁的 LINQ 版本。
    • 这不会像 AMACharter 那样处理文本中的 Acroynms,返回 'A M A Charter' 而不是 'AMA Charter
    【解决方案9】:

    我还有一个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;
    }
    

    【讨论】:

      【解决方案10】:
      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();
              }
      

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

          如果你不喜欢使用正则表达式 - 试试这个:

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

          【讨论】:

            【解决方案13】:

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

            【讨论】:

              【解决方案14】:

              与上述一些类似的简单版本,但如果当前位置已有分隔符,则逻辑不自动插入分隔符(默认为空格,但可以是任何字符)。

              使用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'
              

              【讨论】:

                【解决方案15】:

                试试这个:

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

                See sample online

                【讨论】:

                  猜你喜欢
                  • 2012-01-14
                  • 2014-02-15
                  • 1970-01-01
                  • 2015-07-07
                  • 2021-04-06
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多