我正在寻找一种将 PascalCase 字符串,例如 "MyString",拆分为单独单词的方法 - "My", "String"。另一位用户 在 bash
中提出了这个问题,但我想知道如何使用通用正则表达式或至少在 .NET 中实现它。
如果您能找到一种方法来拆分(并可选择大写)camelCase字符串,那就更好了:例如,"myString" 变成 "my" 和 "String",并可选择大小写其中一个或两个字符串。
请参考这个问题:有没有一种优雅的方法来解析一个单词并在大写字母前添加空格? 其中被接受的答案涵盖了你所需要的内容,包括连续数字和几个大写字母。虽然示例中的单词以大写字母开头,但当第一个单词为小写字母时,它同样有效。
string[] tests = {
"AutomaticTrackingSystem",
"XMLEditor",
"AnXMLAndXSLT2.0Tool",
};
Regex r = new Regex(
@"(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[^A-Za-z])"
);
foreach (string s in tests)
r.Replace(s, " ");
上述代码将输出:[Automatic][Tracking][System]
[XML][Editor]
[An][XML][And][XSLT][2.0][Tool]
这里提供了一种使用LINQ的替代正则表达式和循环解决方案,它还可以处理驼峰命名法和缩略语:
string[] testCollection = new string[] { "AutomaticTrackingSystem", "XSLT", "aCamelCaseWord" };
foreach (string test in testCollection)
{
// if it is not the first character and it is uppercase
// and the previous character is not uppercase then insert a space
var result = test.SelectMany((c, i) => i != 0 && char.IsUpper(c) && !char.IsUpper(test[i - 1]) ? new char[] { ' ', c } : new char[] { c });
Console.WriteLine(new String(result.ToArray()));
}
这是输出结果:Automatic Tracking System
XSLT
a Camel Case Word
HTTPResponseException
转换为 HTTPResponse Exception
。 - dvlsgvoid Main()
{
"aCamelCaseWord".ToFriendlyCase().Dump();
}
public static class Extensions
{
public static string ToFriendlyCase(this string PascalString)
{
return Regex.Replace(PascalString, "(?!^)([A-Z])", " $1");
}
}
输出一个驼峰式单词 (.Dump()
只是打印到控制台)。
aCamelCaseXML
,必须发生什么?阅读问题,我会期望得到a Camel Case XML
。但是实际上,它给出了a Camel Case X M L
。 - Arseni Mourzenkonew Regex( @" (?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace
,如https://dev59.com/9nA75IYBdhLWcg3wy8Qi 所述。 - Custodiostatic IEnumerable<string> SplitPascalCase(this string text)
{
var sb = new StringBuilder();
using (var reader = new StringReader(text))
{
while (reader.Peek() != -1)
{
char c = (char)reader.Read();
if (char.IsUpper(c) && sb.Length > 0)
{
yield return sb.ToString();
sb.Length = 0;
}
sb.Append(c);
}
}
if (sb.Length > 0)
yield return sb.ToString();
}
旨在:
因此,我创建了以下(非正则表达式、冗长但性能导向的)函数:
public static string ToSeparateWords(this string value)
{
if (value==null){return null;}
if(value.Length <=1){return value;}
char[] inChars = value.ToCharArray();
List<int> uCWithAnyLC = new List<int>();
int i = 0;
while (i < inChars.Length && char.IsUpper(inChars[i])) { ++i; }
for (; i < inChars.Length; i++)
{
if (char.IsUpper(inChars[i]))
{
uCWithAnyLC.Add(i);
if (++i < inChars.Length && char.IsUpper(inChars[i]))
{
while (++i < inChars.Length)
{
if (!char.IsUpper(inChars[i]))
{
uCWithAnyLC.Add(i - 1);
break;
}
}
}
}
}
char[] outChars = new char[inChars.Length + uCWithAnyLC.Count];
int lastIndex = 0;
for (i=0;i<uCWithAnyLC.Count;i++)
{
int currentIndex = uCWithAnyLC[i];
Array.Copy(inChars, lastIndex, outChars, lastIndex + i, currentIndex - lastIndex);
outChars[currentIndex + i] = ' ';
lastIndex = currentIndex;
}
int lastPos = lastIndex + uCWithAnyLC.Count;
Array.Copy(inChars, lastIndex, outChars, lastPos, outChars.Length - lastPos);
return new string(outChars);
}
最让人惊讶的是性能测试,每个函数使用100万次迭代。
regex pattern used = "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))"
test string = "TestTLAContainingCamelCase":
static regex: 13 302ms
Regex instance: 12 398ms
compiled regex: 12 663ms
brent(above): 345ms
AndyRose: 1 764ms
DanTao: 995ms
即使在一百万次迭代中,Regex实例方法的速度仅比静态方法略快(而我看不出使用RegexOptions.Compiled标志的好处),丹·陶(Dan Tao)非常简洁的代码几乎与我不太清晰的代码一样快!
var regex = new Regex("([A-Z]+[^A-Z]+)");
var matches = regex.Matches("aCamelCaseWord")
.Cast<Match>()
.Select(match => match.Value);
foreach (var element in matches)
{
Console.WriteLine(element);
}
打印
Camel
Case
Word
正如您所看到的,它无法处理驼峰式命名——它省略了前导的“a”。
string.Concat(str.Select(x => Char.IsUpper(x) ? " " + x : x.ToString())).TrimStart(' ').Dump();
public static string PascalCaseToSentence(string input)
{
if (input == null) return "";
string output = Regex.Replace(input, @"(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[^A-Za-z])", m => " " + m.Value);
return output;
}
基于Shimmy的回答。
使用 \W
检查正则表达式开头是否有非单词字符,并将单个字符串保持在一起,然后拆分单词。
例如:\W([A-Z][A-Za-z]+)+
对于:sdcsds sd aCamelCaseWord as dasd as aSscdcacdcdc PascelCase DfsadSsdd sd
输出:
48: PascelCase
59: DfsadSsdd
\b
(单词边界)来匹配单词的开头,而不是\W
。 - Alan Moore"aCamelCaseWord".split /(?=[[:upper:]])/
=> ["a", "Camel", "Case", "Word"]
[A-Z]
替换 [[:upper:]]
(http://en.wikipedia.org/wiki/Regular_expression),我也无法在 .NET 中找到等效的工作方式。 - Pat\p{Lu}
代替,但[A-Z]
可能已经足够了。无论如何,这种方法过于简单化。请查看其他问题,特别是Poly提出的“split”正则表达式。确实很复杂。 - Alan Moore
AnXMLAndXSLT2.0Tool
拆分为[An][XML][And][XSLT][2.0][Tool]
。它使用了环视,可以说是相当易读的。 - polygenelubricants