给定字符串“ThisStringHasNoSpacesButItDoesHaveCapitals”,什么是在大写字母之前添加空格的最好方法。所以结尾字符串是"This string Has No space But It Does Have大写"
下面是我使用正则表达式的尝试
System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")
给定字符串“ThisStringHasNoSpacesButItDoesHaveCapitals”,什么是在大写字母之前添加空格的最好方法。所以结尾字符串是"This string Has No space But It Does Have大写"
下面是我使用正则表达式的尝试
System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")
当前回答
我开始做一个简单的扩展方法,基于二进制Worrier的代码,它将正确地处理首字母缩略词,并且是可重复的(不会破坏已经间隔的单词)。这是我的结果。
public static string UnPascalCase(this string text)
{
if (string.IsNullOrWhiteSpace(text))
return "";
var newText = new StringBuilder(text.Length * 2);
newText.Append(text[0]);
for (int i = 1; i < text.Length; i++)
{
var currentUpper = char.IsUpper(text[i]);
var prevUpper = char.IsUpper(text[i - 1]);
var nextUpper = (text.Length > i + 1) ? char.IsUpper(text[i + 1]) || char.IsWhiteSpace(text[i + 1]): prevUpper;
var spaceExists = char.IsWhiteSpace(text[i - 1]);
if (currentUpper && !spaceExists && (!nextUpper || !prevUpper))
newText.Append(' ');
newText.Append(text[i]);
}
return newText.ToString();
}
下面是这个函数通过的单元测试用例。我把他建议的大部分案例都加到了这个清单上。其中三个没有通过的(两个只是罗马数字)被注释掉了:
Assert.AreEqual("For You And I", "ForYouAndI".UnPascalCase());
Assert.AreEqual("For You And The FBI", "ForYouAndTheFBI".UnPascalCase());
Assert.AreEqual("A Man A Plan A Canal Panama", "AManAPlanACanalPanama".UnPascalCase());
Assert.AreEqual("DNS Server", "DNSServer".UnPascalCase());
Assert.AreEqual("For You And I", "For You And I".UnPascalCase());
Assert.AreEqual("Mount Mᶜ Kinley National Park", "MountMᶜKinleyNationalPark".UnPascalCase());
Assert.AreEqual("El Álamo Tejano", "ElÁlamoTejano".UnPascalCase());
Assert.AreEqual("The Ævar Arnfjörð Bjarmason", "TheÆvarArnfjörðBjarmason".UnPascalCase());
Assert.AreEqual("Il Caffè Macchiato", "IlCaffèMacchiato".UnPascalCase());
//Assert.AreEqual("Mister Dženan Ljubović", "MisterDženanLjubović".UnPascalCase());
//Assert.AreEqual("Ole King Henry Ⅷ", "OleKingHenryⅧ".UnPascalCase());
//Assert.AreEqual("Carlos Ⅴº El Emperador", "CarlosⅤºElEmperador".UnPascalCase());
Assert.AreEqual("For You And The FBI", "For You And The FBI".UnPascalCase());
Assert.AreEqual("A Man A Plan A Canal Panama", "A Man A Plan A Canal Panama".UnPascalCase());
Assert.AreEqual("DNS Server", "DNS Server".UnPascalCase());
Assert.AreEqual("Mount Mᶜ Kinley National Park", "Mount Mᶜ Kinley National Park".UnPascalCase());
其他回答
除了马丁·布朗的回答,我也有一个关于数字的问题。例如:“Location2”或“Jan22”应该分别是“Location2”和“Jan22”。
下面是我的正则表达式,用的是Martin Brown的答案:
"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))|((?<=[\p{Ll}\p{Lu}])\p{Nd})|((?<=\p{Nd})\p{Lu})"
这里有几个很好的网站,可以帮助你弄清楚每个部分的意思:
基于Java的正则表达式分析器(但适用于大多数。net正则表达式)
基于动作脚本的分析器
上面的正则表达式不能在动作脚本站点上工作,除非您将所有的\p{Ll}替换为[a-z],将\p{Lu}替换为[a-z],并将\p{Nd}替换为[0-9]。
在小写字母、大写字母或数字后添加空格的简单方法。
string AddSpacesToSentence(string value, bool spaceLowerChar = true, bool spaceDigitChar = true, bool spaceSymbolChar = false)
{
var result = "";
for (int i = 0; i < value.Length; i++)
{
char currentChar = value[i];
char nextChar = value[i < value.Length - 1 ? i + 1 : value.Length - 1];
if (spaceLowerChar && char.IsLower(currentChar) && !char.IsLower(nextChar))
{
result += value[i] + " ";
}
else if (spaceDigitChar && char.IsDigit(currentChar) && !char.IsDigit(nextChar))
{
result += value[i] + " ";
}
else if(spaceSymbolChar && char.IsSymbol(currentChar) && !char.IsSymbol(nextChar))
{
result += value[i];
}
else
{
result += value[i];
}
}
return result;
}
二进制担忧者,我已经使用了你建议的代码,它是相当好的,我只是有一个小的增加:
public static string AddSpacesToSentence(string text)
{
if (string.IsNullOrEmpty(text))
return "";
StringBuilder newText = new StringBuilder(text.Length * 2);
newText.Append(text[0]);
for (int i = 1; i < result.Length; i++)
{
if (char.IsUpper(result[i]) && !char.IsUpper(result[i - 1]))
{
newText.Append(' ');
}
else if (i < result.Length)
{
if (char.IsUpper(result[i]) && !char.IsUpper(result[i + 1]))
newText.Append(' ');
}
newText.Append(result[i]);
}
return newText.ToString();
}
我添加了一个条件!char。IsUpper(text[i - 1])。这修复了一个错误,会导致'AverageNOX'变成'平均NOX',这显然是错误的,因为它应该读'平均NOX'。
遗憾的是,这仍然有错误,如果你有文本'FromAStart',你会得到'FromAStart'出来。
有什么解决办法吗?
这对聚合来说是个好机会。它被设计成可读的,但不一定特别快。
someString
.Aggregate(
new StringBuilder(),
(str, ch) => {
if (char.IsUpper(ch) && str.Length > 0)
str.Append(" ");
str.Append(ch);
return str;
}
).ToString();
请确保您没有在字符串的开头放置空格,而是将它们放在连续的大写字母之间。这里的一些答案并没有解决其中的一个或两个问题。除了regex,还有其他方法,但如果你更喜欢使用它,试试这个:
Regex.Replace(value, @"\B[A-Z]", " $0")
\B是一个负的\B,所以它代表一个非单词边界。这意味着模式匹配XYzabc中的“Y”,但不匹配Yzabc或XYzabc。作为一个小奖励,你可以在一个有空格的字符串上使用它,它不会使它们加倍。