【问题标题】:Ignore existing spaces in converting CamelCase to string with spaces在将 CamelCase 转换为带空格的字符串时忽略现有空格
【发布时间】:2015-06-05 08:33:50
【问题描述】:

我想将 camelCasePascalCase 单词拆分为单独的单词集合。

到目前为止,我有:

Regex.Replace(value, @"(\B[A-Z]+?(?=[A-Z][^A-Z])|\B[A-Z]+?(?=[^A-Z]))", " $0", RegexOptions.Compiled);

它适用于将“TestWord”转换为“Test Word”并保持单个单词不变,例如Testing 仍然是 Testing

但是,当我更喜欢 ABC Test 时,ABCTest 会转换为 A B C Test

【问题讨论】:

    标签: c# .net regex


    【解决方案1】:

    试试:

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

    An example on Regex101


    在 CS 中是如何使用的?

    string strText = " TestWord asdfDasdf  ABCDef";
            
    string[] matches = Regex.Matches(strText, @"[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z])|[a-z]+|[A-Z]+")
                    .Cast<Match>()
                    .Select(m => m.Value)
                    .ToArray();
                
    string result = String.Join(" ", matches);
    

    result = 'Test Word asdf Dasdf ABC Def'


    工作原理

    在示例字符串中:

    TestWord qwerDasdf
    ABCTest Testing    ((*&^%$CamelCase!"£$%^^))
    asdfAasdf
    AaBbbCD
    

    [A-Z][a-z]+ 匹配:

    • [0-4]Test
    • [4-8]Word
    • [13-18]Dasdf
    • [22-26]Test
    • [27-34]Testing
    • [45-50]Camel
    • [50-54]Case
    • [68-73]Aasdf
    • [74-76]Aa
    • [76-79]Bbb

    [A-Z]+(?=[A-Z][a-z]) 匹配:

    • [19-22]ABC

    [a-z]+ 匹配:

    • [9-13]qwer
    • [64-68]asdf

    [A-Z]+ 匹配:

    • [79-81]CD

    【讨论】:

    • 感谢您的回复,但是当值已经有空格时它不起作用。它最终将空格加倍,即“ABC Test”变为“ABC Test”,同样,“ABCTest”变为“ABC Test”,即保留前导空格。
    • @CiaranMartin 我会使用Regex.match() 来获得上面列出的所有匹配项的MatchCollection。然后,您可以将其转换为数组并使用单个空格分隔符将其连接到字符串中。这样可以避免正则表达式中的大量逻辑,并使代码更具可读性。
    • @CiaranMartin 查看我对 C# 实现的编辑。
    • 如果输入字符串包含数字或字母数字,它们将在最终输出中丢失或损坏。
    • 谢谢@OhAuth 现在一切都说得通了。我同意,获得匹配然后加入它们更直接,而不是尝试处理 RegEx 中的空格
    【解决方案2】:

    这是我的尝试:

    (?<!^|\b|\p{Lu})\p{Lu}+(?=\p{Ll}|\b)|(?<!^\p{Lu}*|\b)\p{Lu}(?=\p{Ll}|(?<!\p{Lu}*)\b)
    

    此正则表达式可与Regex.Replace $0 一起用作替换字符串。

    Regex.Replace(value, @"(?<!^|\b|\p{Lu})\p{Lu}+(?=\p{Ll}|\b)|(?<!^\p{Lu}*|\b)\p{Lu}(?=\p{Ll}|(?<!\p{Lu}*)\b)", " $0", RegexOptions.Compiled);
    

    demo

    正则表达式解释:

    • 包含 2 个替代项来说明小写字母之前或之后的大写字母链。
    • (?&lt;!^|\b|\p{Lu})\p{Lu}+(?=\p{Ll}|\b) - 第一个匹配多个大写字母的替代方案,这些字母前面没有字符串开头、单词边界或另一个大写字母,并且后跟一个小写字母或单词边界,
    • (?&lt;!^\p{Lu}*|\b)\p{Lu}(?=\p{Ll}|(?&lt;!\p{Lu}*)\b) - 匹配单个大写字母的第二个替代方案,该大写字母前面没有带有可选大写字母的字符串开头或单词边界,后跟一个小写字母或前面没有可选大写字母的单词边界字母。

    【讨论】:

    • 你有时间检查我的方法吗?
    • 我做到了,但 RegEx 已经足够难了,无需破译 \p{Lu} 语法。我已经对其进行了测试,但它在“ABC 测试”中无法正常工作,因为当我预期“ABC 测试”时它会输出“ABC 测试”
    • 并非如此,请仔细查看 RegexStorm 演示中的 Context 选项卡。它显示​ABC Test\p{Lu} 是大写字母,\p{Ll} 是小写字母,这支持Unicode。它适用于俄语或波兰语以及其他具有大写/小写字母的脚本。
    • 我在 LinqPad 中使用以下代码对其进行了测试,结果显示为 "ABC Test": string value = "ABC Test";字符串匹配 = Regex.Replace(value, @"(?
    【解决方案3】:

    您有使用正则表达式的要求吗?老实说,我根本不会使用正则表达式。它们很难调试,也不是特别可读。

    我会选择一种小型、可重复使用、易于测试的扩展方法:

    class Program
    {
        static void Main(string[] args)
        {
            string[] inputs = new[]
            {
                "ABCTest",
                "HelloWorld",
                "testTest$Test",
                "aaҚbb"
            };
    
            var output = inputs.Select(x => x.SplitWithSpaces(CultureInfo.CurrentUICulture));
    
            foreach (string x in output)
            {
                Console.WriteLine(x);
            }
    
            Console.Read();
        }
    }
    
    public static class StringExtensions
    {
        public static bool IsLowerCase(this TextInfo textInfo, char input)
        {
            return textInfo.ToLower(input) == input;
        }
    
        public static string SplitWithSpaces(this string input, CultureInfo culture = null)
        {
            if (culture == null)
            {
                culture = CultureInfo.InvariantCulture;
            }
            TextInfo textInfo = culture.TextInfo;
    
            StringBuilder sb = new StringBuilder(input);
    
            for (int i = 1; i < sb.Length; i++)
            {
                int previous = i - 1;
    
                if (textInfo.IsLowerCase(sb[previous]))
                {
                    int insertLocation = previous - 1;
    
                    if (insertLocation > 0)
                    {
                        sb.Insert(insertLocation, ' ');
                    }
    
                    while (i < sb.Length && textInfo.IsLowerCase(sb[i]))
                    {
                        i++;
                    }
                }                
            }
    
            return sb.ToString();
        }
    }
    

    【讨论】:

    • 感谢您的回答,但我不需要处理文化差异,并且觉得 OhAuth 的回答更直接、更简洁,即使它的正则表达式在最好的时光!
    猜你喜欢
    • 1970-01-01
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多