在将CamelCase转换为带空格的字符串时忽略现有空格

本文关键字:空格 字符串 CamelCase 转换 在将 | 更新日期: 2023-09-27 17:53:41

我想将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

在将CamelCase转换为带空格的字符串时忽略现有空格

尝试:

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

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

这是我的尝试:

(?<!^|'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);

参见演示

Regex解释:

  • 包含两个备选方案,用于说明小写字母前后的大写字母链
  • (?<!^|'b|'p{Lu})'p{Lu}+(?='p{Ll}|'b)-第一个匹配前面没有字符串开头、单词边界或另一个大写字母,后面跟着小写字母或单词边界的大写字母的选项
  • (?<!^'p{Lu}*|'b)'p{Lu}(?='p{Ll}|(?<!'p{Lu}*)'b)-第二个备选方案,它匹配一个没有以字符串开头的大写字母,字符串后面有可选的大写字母或单词边界,后面有小写字母或单词边境,单词边界前面没有可选的大写字符

您有使用Regex的要求吗?老实说,我根本不会用Regex。它们很难调试,可读性也不强。

  • 有时你也会遇到这样的乐趣:Regex问题:IsMatch方法永远不会返回
  • 上面的正则表达式不会处理unicode的奇妙世界,例如Cyrillics(http://en.wikipedia.org/wiki/Cyrillic_script)(并不是说你的特定问题领域可能需要这个,而是为了完整性…(

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

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