【问题标题】:Add spaces before Capital Letters在大写字母前添加空格
【发布时间】:2008-11-07 16:33:53
【问题描述】:

鉴于字符串“ThisStringHasNoSpacesButItDoesHaveCapitals”,在大写字母前添加空格的最佳方法是什么。所以结束字符串将是“这个字符串没有空格但它有大写字母”

这是我对 RegEx 的尝试

System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")

【问题讨论】:

  • 您对所采取的方法有什么特别的不满吗?这可能有助于我们改进您的方法。
  • 如果正则表达式有效,那么我会坚持下去。正则表达式针对字符串操作进行了优化。
  • 我只是好奇是否有更好的甚至是内置的方法。我什至很想看看其他语言的其他方法。
  • 您的代码根本不起作用,因为修改后的字符串是“替换”函数的返回值。使用此代码行:'System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", "$0").Trim();'它会完美地工作。 (只是评论,因为我偶然发现了这篇文章,没有人真正看到,你的代码有什么问题。)
  • Regex.Replace("ThisStringHasNoSpacesButItDoesHaveCapitals", @"\B[A-Z]", m => " " + m);

标签: c# regex string


【解决方案1】:

正则表达式可以正常工作(我什至投票支持 Martin Browns 的答案),但它们很昂贵(而且我个人发现任何长于几个字符的模式都非常迟钝)

这个功能

string AddSpacesToSentence(string text, bool preserveAcronyms)
{
        if (string.IsNullOrWhiteSpace(text))
           return string.Empty;
        StringBuilder newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]))
                if ((text[i - 1] != ' ' && !char.IsUpper(text[i - 1])) ||
                    (preserveAcronyms && char.IsUpper(text[i - 1]) && 
                     i < text.Length - 1 && !char.IsUpper(text[i + 1])))
                    newText.Append(' ');
            newText.Append(text[i]);
        }
        return newText.ToString();
}

将在 2,968,750 个滴答声中执行 100,000 次,正则表达式将花费 25,000,000 个滴答声(这就是编译的正则表达式)。

更好,对于给定的更好(即更快)值,但需要维护更多代码。 “更好”通常是对竞争要求的妥协。

希望这会有所帮助:)

更新
很久没看这个了,我才意识到自从代码改变后时间没有更新(它只改变了一点)。

在 'Abbbbbbbbbb' 重复 100 次(即 1,000 字节)的字符串上,运行 100,000 次转换需要手动编码函数 4,517,177 个滴答声,下面的正则表达式需要 59,435,719 次,使得手动编码函数在 7.6% 的时间内运行它需要正则表达式。

更新 2 它会考虑首字母缩略词吗?现在会的! if 语句的逻辑相当模糊,你可以看到将其扩展到这个......

if (char.IsUpper(text[i]))
    if (char.IsUpper(text[i - 1]))
        if (preserveAcronyms && i < text.Length - 1 && !char.IsUpper(text[i + 1]))
            newText.Append(' ');
        else ;
    else if (text[i - 1] != ' ')
        newText.Append(' ');

...根本没有帮助!

这是不用担心缩写词的原始简单方法

string AddSpacesToSentence(string text)
{
        if (string.IsNullOrWhiteSpace(text))
           return "";
        StringBuilder newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]) && text[i - 1] != ' ')
                newText.Append(' ');
            newText.Append(text[i]);
        }
        return newText.ToString();
}

【讨论】:

  • if (char.IsUpper (text [i]) && text[i - 1] != ' ') 如果您重新运行上面的代码,它会不断添加空格,这将停止添加空格如果大写字母前有空格。
  • 我不确定,所以我想我会问,这种方法是否处理 Martin Brown 的回答“DriveIsSCSICompatible”中描述的首字母缩略词,理想情况下会变成“Drive Is SCSI Compatible”
  • 通过用新更新的 if 语句替换你的 for 语句的内容使它变成了 1 个字符,我可能做错了什么?
  • 添加对 char.IsLetter(text[i + 1]) 的检查有助于处理带有特殊字符和数字的首字母缩写词(即 ABC_DEF 不会被拆分为 AB C_DEF)。
  • 我不确定首字母缩略词部分在关闭时是否正确。我刚刚运行了一个测试“ASentenceABC”扩展为“ASentence A B C”。应该是“A Sentence A B C”
【解决方案2】:

您的解决方案有一个问题,它在第一个字母 T 之前放置了一个空格,因此您得到 ​​p>

" This String..." instead of "This String..."

要解决这个问题,还要寻找它前面的小写字母,然后在中间插入空格:

newValue = Regex.Replace(value, "([a-z])([A-Z])", "$1 $2");

编辑 1:

如果您使用@"(\p{Ll})(\p{Lu})",它也会拾取重音字符。

编辑 2:

如果您的字符串可以包含首字母缩略词,您可能需要使用此:

newValue = Regex.Replace(value, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0");

所以“DriveIsSCSICompatible”变成了“Drive Is SCSI Compatible”

【讨论】:

  • 难道你不能也保留原来的 RegEx 和 Trim() 结果吗?
  • @PandaWood 你可以,但它需要另一个内存分配和字符串复制。也就是说,如果担心性能,Regex 可能无论如何都不是最好的方法。
  • 你也可以使用"([^A-Z\\s])([A-Z])",即使是首字母缩写词?
【解决方案3】:

没有测试性能,但这里与 linq 一致:

var val = "ThisIsAStringToTest";
val = string.Concat(val.Select(x => Char.IsUpper(x) ? " " + x : x.ToString())).TrimStart(' ');

【讨论】:

  • 谢谢!
【解决方案4】:

我知道这是一个旧的,但这是我在需要时使用的扩展:

public static class Extensions
{
    public static string ToSentence( this string Input )
    {
        return new string(Input.SelectMany((c, i) => i > 0 && char.IsUpper(c) ? new[] { ' ', c } : new[] { c }).ToArray());
    }
}

这将允许您使用MyCasedString.ToSentence()

【讨论】:

  • 我喜欢这个作为扩展方法的想法,如果你添加 TrimStart(' ') 它将删除前导空格。
  • 谢谢@user1069816。我已更改扩展名以使用包含索引的SelectMany 的重载,这样可以避免第一个字母和对TrimStart(' ') 的额外调用的不必要的潜在开销。罗伯。
【解决方案5】:

我开始基于 Binary 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();
}

这里是这个函数通过的单元测试用例。我将大部分 tchrist 的建议案例添加到此列表中。没有通过的三个(两个只是罗马数字)被注释掉:

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

【讨论】:

  • 与此处发布的其他解决方案类似,它以字符串“RegularOTs”失败。它返回“常规 O Ts”
【解决方案6】:

欢迎使用 Unicode

所有这些解决方案对于现代文本来说基本上都是错误的。你需要使用理解大小写的东西。由于 Bob 要求使用其他语言,我将提供一些 Perl 语言。

我提供了四种解决方案,从最差到最好。只有最好的永远是对的。其他人都有问题。这是一个测试运行,向您展示什么有效,什么无效,以及在哪里。我使用了下划线,以便您可以看到放置空格的位置,并且我将任何错误的东西都标记为错误。

Testing TheLoneRanger
               Worst:    The_Lone_Ranger
               Ok:       The_Lone_Ranger
               Better:   The_Lone_Ranger
               Best:     The_Lone_Ranger
Testing MountMᶜKinleyNationalPark
     [WRONG]   Worst:    Mount_MᶜKinley_National_Park
     [WRONG]   Ok:       Mount_MᶜKinley_National_Park
     [WRONG]   Better:   Mount_MᶜKinley_National_Park
               Best:     Mount_Mᶜ_Kinley_National_Park
Testing ElÁlamoTejano
     [WRONG]   Worst:    ElÁlamo_Tejano
               Ok:       El_Álamo_Tejano
               Better:   El_Álamo_Tejano
               Best:     El_Álamo_Tejano
Testing TheÆvarArnfjörðBjarmason
     [WRONG]   Worst:    TheÆvar_ArnfjörðBjarmason
               Ok:       The_Ævar_Arnfjörð_Bjarmason
               Better:   The_Ævar_Arnfjörð_Bjarmason
               Best:     The_Ævar_Arnfjörð_Bjarmason
Testing IlCaffèMacchiato
     [WRONG]   Worst:    Il_CaffèMacchiato
               Ok:       Il_Caffè_Macchiato
               Better:   Il_Caffè_Macchiato
               Best:     Il_Caffè_Macchiato
Testing MisterDženanLjubović
     [WRONG]   Worst:    MisterDženanLjubović
     [WRONG]   Ok:       MisterDženanLjubović
               Better:   Mister_Dženan_Ljubović
               Best:     Mister_Dženan_Ljubović
Testing OleKingHenryⅧ
     [WRONG]   Worst:    Ole_King_HenryⅧ
     [WRONG]   Ok:       Ole_King_HenryⅧ
     [WRONG]   Better:   Ole_King_HenryⅧ
               Best:     Ole_King_Henry_Ⅷ
Testing CarlosⅤºElEmperador
     [WRONG]   Worst:    CarlosⅤºEl_Emperador
     [WRONG]   Ok:       CarlosⅤº_El_Emperador
     [WRONG]   Better:   CarlosⅤº_El_Emperador
               Best:     Carlos_Ⅴº_El_Emperador

顺便说一句,这里几乎每个人都选择了第一种方式,即标记为“最差”的方式。少数人选择了第二种方式,标记为“OK”。但在我之前没有其他人向您展示过如何采用“更好”或“最佳”的方法。

下面是测试程序及其四种方法:

#!/usr/bin/env perl
use utf8;
use strict;
use warnings;

# First I'll prove these are fine variable names:
my (
    $TheLoneRanger              ,
    $MountMᶜKinleyNationalPark  ,
    $ElÁlamoTejano              ,
    $TheÆvarArnfjörðBjarmason   ,
    $IlCaffèMacchiato           ,
    $MisterDženanLjubović         ,
    $OleKingHenryⅧ              ,
    $CarlosⅤºElEmperador        ,
);

# Now I'll load up some string with those values in them:
my @strings = qw{
    TheLoneRanger
    MountMᶜKinleyNationalPark
    ElÁlamoTejano
    TheÆvarArnfjörðBjarmason
    IlCaffèMacchiato
    MisterDženanLjubović
    OleKingHenryⅧ
    CarlosⅤºElEmperador
};

my($new, $best, $ok);
my $mask = "  %10s   %-8s  %s\n";

for my $old (@strings) {
    print "Testing $old\n";
    ($best = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;

    ($new = $old) =~ s/(?<=[a-z])(?=[A-Z])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Worst:", $new;

    ($new = $old) =~ s/(?<=\p{Ll})(?=\p{Lu})/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Ok:", $new;

    ($new = $old) =~ s/(?<=\p{Ll})(?=[\p{Lu}\p{Lt}])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Better:", $new;

    ($new = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Best:", $new;
}

当您在此数据集上获得与“最佳”相同的分数时,您就会知道自己做得对。在那之前,你还没有。这里没有其他人做得比“好的”更好,而且大多数人都做得“最差”。我期待看到有人发布正确的ℂ♯代码。

我注意到 StackOverflow 的高亮代码再次变得非常愚蠢。他们所做的一切都与这里提到的其他糟糕方法(大多数但不是全部)一样陈旧。让 ASCII 休息的时间不是很长吗?它不再有意义,假装它就是你所拥有的一切都是错误的。这会导致糟糕的代码。

【讨论】:

  • 您的“最佳”答案似乎是迄今为止最接近的,但它似乎并没有考虑到前导标点符号或其他前导非小写字母。这似乎对我最有效(在java中):replaceAll("(?
  • 嗯。在这个例子中,我不确定罗马数字是否真的应该算作大写。字母修饰符示例绝对不应该被计算在内。如果您访问 McDonalds.com,您会看到它没有空格。
  • 还应该注意的是,您永远无法做到完美。例如,我希望看到一个整理出“AlexandervonHumboldt”的示例,该示例最终应为“Alexander von Humboldt”。当然还有一些语言没有大写和小写的定义。
【解决方案7】:

Binary Worrier,我已经使用了你建议的代码,它相当不错,我只是添加了一点:

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”之类的内容变成“Average NOX”,这显然是错误的,因为它应该显示为“Average NOX”。

遗憾的是,这仍然存在一个错误,即如果你有文本“FromAStart”,你会得到“From AStart”。

有解决这个问题的想法吗?

【讨论】:

  • 也许这样的事情会起作用: char.IsUpper(text[i]) && (char.IsLower(text[i - 1]) || (char.IsLower(text[i+1] ))
  • 这是正确的:if (char.IsUpper(text[i]) &amp;&amp; !(char.IsUpper(text[i - 1]) &amp;&amp; char.IsUpper(text[i + 1]))) 测试结果:"From Start"、"From THE Start"、"From A Start" 但您需要在 for 循环条件中使用 i &lt; text.Length - 1 以忽略最后一个字符并防止超出范围异常。
  • 哦,还是一样。 !(a && b) 和 (!a || !b) 因为 lower = !upper。
  • 结果如何?
【解决方案8】:

此正则表达式在每个大写字母前放置一个空格字符:

using System.Text.RegularExpressions;

const string myStringWithoutSpaces = "ThisIsAStringWithoutSpaces";
var myStringWithSpaces = Regex.Replace(myStringWithoutSpaces, "([A-Z])([a-z]*)", " $1$2");

注意前面的空格,如果“$1$2”,这将完成它。

这是结果:

"This Is A String Without Spaces"

【讨论】:

  • 如果您还想分隔数字,请改用此正则表达式模式:"([A-Z0-9])([a-z]*)"
【解决方案9】:

这是我的:

private string SplitCamelCase(string s) 
{ 
    Regex upperCaseRegex = new Regex(@"[A-Z]{1}[a-z]*"); 
    MatchCollection matches = upperCaseRegex.Matches(s); 
    List<string> words = new List<string>(); 
    foreach (Match match in matches) 
    { 
        words.Add(match.Value); 
    } 
    return String.Join(" ", words.ToArray()); 
}

【讨论】:

  • 那应该是 C# 吗?如果是这样,List 在哪个名称空间中?你的意思是 ArrayList 还是 List
  • List 就可以了。对此感到抱歉。
  • @Martin 他总是有正确的语法,只是隐藏在 &lt;pre&gt;&lt;code&gt;code&lt;/code&gt;&lt;/pre&gt; 块中,而不是 Markdown 语法。无需对他投反对票(如果那是你的话)。
【解决方案10】:

确保您没有在字符串的开头放置空格,但您正在将它们放在连续的大写字母之间。这里的一些答案没有解决其中一个或两个问题。除了正则表达式还有其他方法,但如果你更喜欢使用它,试试这个:

Regex.Replace(value, @"\B[A-Z]", " $0")

\B 是一个否定的\b,因此它表示一个非单词边界。这意味着模式匹配XYzabc 中的“Y”,但不匹配YzabcX Yzabc。作为一个小奖励,您可以在其中包含空格的字符串上使用它,并且不会将它们加倍。

【讨论】:

    【解决方案11】:

    灵感来自@MartinBrown, 两行简单正则表达式,将解析您的姓名,包括字符串中任何位置的缩略语。

    public string ResolveName(string name)
    {
       var tmpDisplay = Regex.Replace(name, "([^A-Z ])([A-Z])", "$1 $2");
       return Regex.Replace(tmpDisplay, "([A-Z]+)([A-Z][^A-Z$])", "$1 $2").Trim();
    }
    

    【讨论】:

    • 我喜欢这个解决方案。它又短又快。但是,与其他解决方案类似,它以字符串“RegularOTs”失败。我在这里尝试的每个解决方案都返回“Regular O Ts”
    • @PateeGutee OP 想要在首都之前有空间,他没有提到缩写,我们在生产 cod 中对此进行了修复
    • 你能显示修复吗?我的数据中有这样的字符串,它给了我不正确的结果。谢谢。
    • @PateeGutee 抱歉,我误读了您想要的内容。多元化是一个不同的问题,`RegularOTs' 你期望发生什么“Regular OTs”或“Regular OTs”
    • @PateeGutee 我已经为你更新了我的答案,我相信应该可以
    【解决方案12】:

    你所拥有的一切都很完美。只需记住将value 重新分配给此函数的返回值即可。

    value = System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0");
    

    【讨论】:

      【解决方案13】:

      以下是您可以在 SQL 中执行此操作的方法

      create  FUNCTION dbo.PascalCaseWithSpace(@pInput AS VARCHAR(MAX)) RETURNS VARCHAR(MAX)
      BEGIN
          declare @output varchar(8000)
      
      set @output = ''
      
      
      Declare @vInputLength        INT
      Declare @vIndex              INT
      Declare @vCount              INT
      Declare @PrevLetter varchar(50)
      SET @PrevLetter = ''
      
      SET @vCount = 0
      SET @vIndex = 1
      SET @vInputLength = LEN(@pInput)
      
      WHILE @vIndex <= @vInputLength
      BEGIN
          IF ASCII(SUBSTRING(@pInput, @vIndex, 1)) = ASCII(Upper(SUBSTRING(@pInput, @vIndex, 1)))
             begin 
      
              if(@PrevLetter != '' and ASCII(@PrevLetter) = ASCII(Lower(@PrevLetter)))
                  SET @output = @output + ' ' + SUBSTRING(@pInput, @vIndex, 1)
                  else
                  SET @output = @output +  SUBSTRING(@pInput, @vIndex, 1) 
      
              end
          else
              begin
              SET @output = @output +  SUBSTRING(@pInput, @vIndex, 1) 
      
              end
      
      set @PrevLetter = SUBSTRING(@pInput, @vIndex, 1) 
      
          SET @vIndex = @vIndex + 1
      END
      
      
      return @output
      END
      

      【讨论】:

        【解决方案14】:
        replaceAll("(?<=[^^\\p{Uppercase}])(?=[\\p{Uppercase}])"," ");
        

        【讨论】:

          【解决方案15】:
          static string AddSpacesToColumnName(string columnCaption)
              {
                  if (string.IsNullOrWhiteSpace(columnCaption))
                      return "";
                  StringBuilder newCaption = new StringBuilder(columnCaption.Length * 2);
                  newCaption.Append(columnCaption[0]);
                  int pos = 1;
                  for (pos = 1; pos < columnCaption.Length-1; pos++)
                  {               
                      if (char.IsUpper(columnCaption[pos]) && !(char.IsUpper(columnCaption[pos - 1]) && char.IsUpper(columnCaption[pos + 1])))
                          newCaption.Append(' ');
                      newCaption.Append(columnCaption[pos]);
                  }
                  newCaption.Append(columnCaption[pos]);
                  return newCaption.ToString();
              }
          

          【讨论】:

            【解决方案16】:

            在 Ruby 中,通过正则表达式:

            "FooBarBaz".gsub(/(?!^)(?=[A-Z])/, ' ') # => "Foo Bar Baz"
            

            【讨论】:

            • 糟糕,抱歉。我错过了这是特定于 C# 的问题,并在此处发布了 Ruby 答案:(
            【解决方案17】:

            我采用了 Kevin Strikers 出色的解决方案并转换为 VB。由于我被锁定在 .NET 3.5 中,我还必须编写 IsNullOrWhiteSpace。这通过了他的所有测试。

            <Extension()>
            Public Function IsNullOrWhiteSpace(value As String) As Boolean
                If value Is Nothing Then
                    Return True
                End If
                For i As Integer = 0 To value.Length - 1
                    If Not Char.IsWhiteSpace(value(i)) Then
                        Return False
                    End If
                Next
                Return True
            End Function
            
            <Extension()>
            Public Function UnPascalCase(text As String) As String
                If text.IsNullOrWhiteSpace Then
                    Return String.Empty
                End If
            
                Dim newText = New StringBuilder()
                newText.Append(text(0))
                For i As Integer = 1 To text.Length - 1
                    Dim currentUpper = Char.IsUpper(text(i))
                    Dim prevUpper = Char.IsUpper(text(i - 1))
                    Dim nextUpper = If(text.Length > i + 1, Char.IsUpper(text(i + 1)) Or Char.IsWhiteSpace(text(i + 1)), prevUpper)
                    Dim spaceExists = Char.IsWhiteSpace(text(i - 1))
                    If (currentUpper And Not spaceExists And (Not nextUpper Or Not prevUpper)) Then
                        newText.Append(" ")
                    End If
                    newText.Append(text(i))
                Next
                Return newText.ToString()
            End Function
            

            【讨论】:

              【解决方案18】:

              这个问题有点老了,但现在 Nuget 上有一个不错的库,它可以做到这一点以及许多其他对人类可读文本的转换。

              GitHub 或 Nuget 上查看Humanizer

              示例

              "PascalCaseInputStringIsTurnedIntoSentence".Humanize() => "Pascal case input string is turned into sentence"
              "Underscored_input_string_is_turned_into_sentence".Humanize() => "Underscored input string is turned into sentence"
              "Underscored_input_String_is_turned_INTO_sentence".Humanize() => "Underscored input String is turned INTO sentence"
              
              // acronyms are left intact
              "HTML".Humanize() => "HTML"
              

              【讨论】:

              • 刚试过,第一个链接现在坏了。 NuGet 有效,但该包无法在我的解决方案中编译。一个好主意,如果可行的话。
              【解决方案19】:

              对于Aggregate 来说似乎是一个好机会。这是为了可读性而设计的,不一定特别快。

              someString
              .Aggregate(
                 new StringBuilder(),
                 (str, ch) => {
                    if (char.IsUpper(ch) && str.Length > 0)
                       str.Append(" ");
                    str.Append(ch);
                    return str;
                 }
              ).ToString();
              

              【讨论】:

                【解决方案20】:

                发现很多这些答案相当迟钝,但我还没有完全测试我的解决方案,但它可以满足我的需要,应该处理首字母缩略词,并且比其他 IMO 更紧凑/可读:

                private string CamelCaseToSpaces(string s)
                    {
                        if (string.IsNullOrEmpty(s)) return string.Empty;
                
                        StringBuilder stringBuilder = new StringBuilder();
                        for (int i = 0; i < s.Length; i++)
                        {
                            stringBuilder.Append(s[i]);
                
                            int nextChar = i + 1;
                            if (nextChar < s.Length && char.IsUpper(s[nextChar]) && !char.IsUpper(s[i]))
                            {
                                stringBuilder.Append(" ");
                            }
                        }
                
                        return stringBuilder.ToString();
                    }
                

                【讨论】:

                  【解决方案21】:

                  除了 Martin Brown 的回答之外,我还遇到了数字问题。例如:“Location2”或“Jan22”应分别为“Location 2”和“Jan 22”。

                  这是我的正则表达式,使用 Martin Brown 的回答:

                  "((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))|((?<=[\p{Ll}\p{Lu}])\p{Nd})|((?<=\p{Nd})\p{Lu})"
                  

                  这里有几个很好的网站,可以用来弄清楚每个部分的含义:

                  Java Based Regular Expression Analyzer (but works for most .net regex's)

                  Action Script Based Analyzer

                  除非您将所有 \p{Ll} 替换为 [a-z],将 \p{Lu} 替换为 [A-Z],并将 \p{Nd} 替换为 [0-9],否则上述正则表达式将无法在操作脚本站点上运行。

                  【讨论】:

                    【解决方案22】:

                    这是我的解决方案,基于 Binary Worriers 的建议并在 Richard Priddys 的 cmets 中构建,但也考虑到提供的字符串中可能存在空格,因此它不会在现有空格旁边添加空格。

                    public string AddSpacesBeforeUpperCase(string nonSpacedString)
                        {
                            if (string.IsNullOrEmpty(nonSpacedString))
                                return string.Empty;
                    
                            StringBuilder newText = new StringBuilder(nonSpacedString.Length * 2);
                            newText.Append(nonSpacedString[0]);
                    
                            for (int i = 1; i < nonSpacedString.Length; i++)
                            {
                                char currentChar = nonSpacedString[i];
                    
                                // If it is whitespace, we do not need to add another next to it
                                if(char.IsWhiteSpace(currentChar))
                                {
                                    continue;
                                }
                    
                                char previousChar = nonSpacedString[i - 1];
                                char nextChar = i < nonSpacedString.Length - 1 ? nonSpacedString[i + 1] : nonSpacedString[i];
                    
                                if (char.IsUpper(currentChar) && !char.IsWhiteSpace(nextChar) 
                                    && !(char.IsUpper(previousChar) && char.IsUpper(nextChar)))
                                {
                                    newText.Append(' ');
                                }
                                else if (i < nonSpacedString.Length)
                                {
                                    if (char.IsUpper(currentChar) && !char.IsWhiteSpace(nextChar) && !char.IsUpper(nextChar))
                                    {
                                        newText.Append(' ');
                                    }
                                }
                    
                                newText.Append(currentChar);
                            }
                    
                            return newText.ToString();
                        }
                    

                    【讨论】:

                      【解决方案23】:

                      对于正在寻找 C++ 函数来回答相同问题的任何人,您可以使用以下内容。这是根据@Binary Worrier 给出的答案建模的。此方法仅自动保留首字母缩略词。

                      using namespace std;
                      
                      void AddSpacesToSentence(string& testString)
                              stringstream ss;
                              ss << testString.at(0);
                              for (auto it = testString.begin() + 1; it != testString.end(); ++it )
                              {
                                  int index = it - testString.begin();
                                  char c = (*it);
                                  if (isupper(c))
                                  {
                                      char prev = testString.at(index - 1);
                                      if (isupper(prev))
                                      {
                                          if (index < testString.length() - 1)
                                          {
                                              char next = testString.at(index + 1);
                                              if (!isupper(next) && next != ' ')
                                              {
                                                  ss << ' ';
                                              }
                                          }
                                      }
                                      else if (islower(prev)) 
                                      {
                                         ss << ' ';
                                      }
                                  }
                      
                                  ss << c;
                              }
                      
                              cout << ss.str() << endl;
                      

                      我用于这个函数的测试字符串,结果是:

                      • “helloWorld”->“hello World”
                      • “HelloWorld”->“Hello World”
                      • “HelloABCWorld”->“Hello ABC World”
                      • “HelloWorldABC”->“Hello World ABC”
                      • “ABCHelloWorld”->“ABC Hello World”
                      • “ABC 你好世界”->“ABC 你好世界”
                      • “ABCHELLOWORLD”->“ABCHELLOWORLD”
                      • “A”->“A”

                      【讨论】:

                        【解决方案24】:

                        仅由 ASCII 字符组成的输入字符串的 C# 解决方案。 regex 包含 negative lookbehind 以忽略出现在字符串开头的大写(大写)字母。使用Regex.Replace() 返回所需的字符串。

                        另见regex101.com demo

                        using System;
                        using System.Text.RegularExpressions;
                        
                        public class RegexExample
                        {
                            public static void Main()
                            {
                                var text = "ThisStringHasNoSpacesButItDoesHaveCapitals";
                        
                                // Use negative lookbehind to match all capital letters
                                // that do not appear at the beginning of the string.
                                var pattern = "(?<!^)([A-Z])";
                        
                                var rgx = new Regex(pattern);
                                var result = rgx.Replace(text, " $1");
                                Console.WriteLine("Input: [{0}]\nOutput: [{1}]", text, result);
                            }
                        }
                        

                        预期输出:

                        Input: [ThisStringHasNoSpacesButItDoesHaveCapitals]
                        Output: [This String Has No Spaces But It Does Have Capitals]
                        

                        更新:这里有一个变体,也可以处理首字母缩略词(大写字母序列)。

                        另见regex101.com demoideone.com demo

                        using System;
                        using System.Text.RegularExpressions;
                        
                        public class RegexExample
                        {
                            public static void Main()
                            {
                                var text = "ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ";
                        
                                // Use positive lookbehind to locate all upper-case letters
                                // that are preceded by a lower-case letter.
                                var patternPart1 = "(?<=[a-z])([A-Z])";
                        
                                // Used positive lookbehind and lookahead to locate all
                                // upper-case letters that are preceded by an upper-case
                                // letter and followed by a lower-case letter.
                                var patternPart2 = "(?<=[A-Z])([A-Z])(?=[a-z])";
                        
                                var pattern = patternPart1 + "|" + patternPart2;
                                var rgx = new Regex(pattern);
                                var result = rgx.Replace(text, " $1$2");
                        
                                Console.WriteLine("Input: [{0}]\nOutput: [{1}]", text, result);
                            }
                        }
                        

                        预期输出:

                        Input: [ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ]
                        Output: [This String Has No Spaces ASCII But It Does Have Capitals LINQ]
                        

                        【讨论】:

                          【解决方案25】:

                          这是一个更彻底的解决方案,它不会在单词前面放置空格:

                          注意:我使用了多个正则表达式(不简洁,但它也可以处理首字母缩写词和单字母词)

                          Dim s As String = "ThisStringHasNoSpacesButItDoesHaveCapitals"
                          s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z](?=[A-Z])[a-z]*)", "$1 $2")
                          s = System.Text.RegularExpressions.Regex.Replace(s, "([A-Z])([A-Z][a-z])", "$1 $2")
                          s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z][a-z])", "$1 $2")
                          s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z][a-z])", "$1 $2") // repeat a second time
                          

                          "ThisStringHasNoSpacesButItDoesHaveCapitals"
                          "IAmNotAGoat"
                          "LOLThatsHilarious!"
                          "ThisIsASMSMessage"
                          

                          出局

                          "This String Has No Spaces But It Does Have Capitals"
                          "I Am Not A Goat"
                          "LOL Thats Hilarious!"
                          "This Is ASMS Message" // (Difficult to handle single letter words when they are next to acronyms.)
                          

                          【讨论】:

                          • 这输出“这个字符串没有空格但它有大写字母”
                          • 嗨@AndyRobinson,谢谢。我改为使用多个正则表达式替换。不确定是否有更简洁的方法,但现在可以使用。
                          【解决方案26】:

                          之前的所有回复看起来都过于复杂了。

                          我有一个混合了大写字母和 _ 的字符串,因此使用 string.Replace() 来制作 _、“”,并使用以下内容在大写字母处添加空格。

                          for (int i = 0; i < result.Length; i++)
                          {
                              if (char.IsUpper(result[i]))
                              {
                                  counter++;
                                  if (i > 1) //stops from adding a space at if string starts with Capital
                                  {
                                      result = result.Insert(i, " ");
                                      i++; //Required** otherwise stuck in infinite 
                                           //add space loop over a single capital letter.
                                  }
                              }
                          }
                          

                          【讨论】:

                            【解决方案27】:

                            受到 Binary Worrier 回答的启发,我对此有所了解。

                            结果如下:

                            /// <summary>
                            /// String Extension Method
                            /// Adds white space to strings based on Upper Case Letters
                            /// </summary>
                            /// <example>
                            /// strIn => "HateJPMorgan"
                            /// preserveAcronyms false => "Hate JP Morgan"
                            /// preserveAcronyms true => "Hate JPMorgan"
                            /// </example>
                            /// <param name="strIn">to evaluate</param>
                            /// <param name="preserveAcronyms" >determines saving acronyms (Optional => false) </param>
                            public static string AddSpaces(this string strIn, bool preserveAcronyms = false)
                            {
                                if (string.IsNullOrWhiteSpace(strIn))
                                    return String.Empty;
                            
                                var stringBuilder = new StringBuilder(strIn.Length * 2)
                                    .Append(strIn[0]);
                            
                                int i;
                            
                                for (i = 1; i < strIn.Length - 1; i++)
                                {
                                    var c = strIn[i];
                            
                                    if (Char.IsUpper(c) && (Char.IsLower(strIn[i - 1]) || (preserveAcronyms && Char.IsLower(strIn[i + 1]))))
                                        stringBuilder.Append(' ');
                            
                                    stringBuilder.Append(c);
                                }
                            
                                return stringBuilder.Append(strIn[i]).ToString();
                            }
                            

                            使用运行 10000000 次迭代和各种字符串长度和组合的秒表进行了测试。

                            平均比 Binary Worrier 答案快 50%(可能更多)。

                            【讨论】:

                              【解决方案28】:
                                  private string GetProperName(string Header)
                                  {
                                      if (Header.ToCharArray().Where(c => Char.IsUpper(c)).Count() == 1)
                                      {
                                          return Header;
                                      }
                                      else
                                      {
                                          string ReturnHeader = Header[0].ToString();
                                          for(int i=1; i<Header.Length;i++)
                                          {
                                              if (char.IsLower(Header[i-1]) && char.IsUpper(Header[i]))
                                              {
                                                  ReturnHeader += " " + Header[i].ToString();
                                              }
                                              else
                                              {
                                                  ReturnHeader += Header[i].ToString();
                                              }
                                          }
                              
                                          return ReturnHeader;
                                      }
                              
                                      return Header;
                                  }
                              

                              【讨论】:

                                【解决方案29】:

                                这个包括首字母缩写词和首字母缩写词复数,比公认的答案要快一点:

                                public string Sentencify(string value)
                                {
                                    if (string.IsNullOrWhiteSpace(value))
                                        return string.Empty;
                                
                                    string final = string.Empty;
                                    for (int i = 0; i < value.Length; i++)
                                    {
                                        if (i != 0 && Char.IsUpper(value[i]))
                                        {
                                            if (!Char.IsUpper(value[i - 1]))
                                                final += " ";
                                            else if (i < (value.Length - 1))
                                            {
                                                if (!Char.IsUpper(value[i + 1]) && !((value.Length >= i && value[i + 1] == 's') ||
                                                                                     (value.Length >= i + 1 && value[i + 1] == 'e' && value[i + 2] == 's')))
                                                    final += " ";
                                            }
                                        }
                                
                                        final += value[i];
                                    }
                                
                                    return final;
                                }
                                

                                通过这些测试:

                                string test1 = "RegularOTs";
                                string test2 = "ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ";
                                string test3 = "ThisStringHasNoSpacesButItDoesHaveCapitals";
                                

                                【讨论】:

                                • 接受的答案处理值为空的情况
                                • 这会在输出前添加一个额外的空格,即 HireDate => " Hire Date"。需要 final.TrimStart 什么的。我认为这就是下面其他答案之一所指出的,但是由于重新排序,我不确定他是否在与您交谈,因为他的答案是基于 RegEx 的。
                                • 很好...应该在我的测试中添加开始和结束标记...现在已修复。
                                • 与此处发布的其他解决方案类似,它以字符串“RegularOTs”失败。它返回“常规 O Ts”
                                • 感谢您提出缩写复数形式,我也已更新为此工作。
                                【解决方案30】:

                                使用fold 的实现,也称为Aggregate

                                    public static string SpaceCapitals(this string arg) =>
                                       new string(arg.Aggregate(new List<Char>(),
                                                      (accum, x) => 
                                                      {
                                                          if (Char.IsUpper(x) &&
                                                              accum.Any() &&
                                                              // prevent double spacing
                                                              accum.Last() != ' ' &&
                                                              // prevent spacing acronyms (ASCII, SCSI)
                                                              !Char.IsUpper(accum.Last()))
                                                          {
                                                              accum.Add(' ');
                                                          }
                                
                                                          accum.Add(x);
                                
                                                          return accum;
                                                      }).ToArray());
                                

                                除了请求之外,此实现还正确保存了前导、内部、尾随空格和首字母缩略词,例如,

                                " SpacedWord " => " Spaced Word ",  
                                
                                "Inner Space" => "Inner Space",  
                                
                                "SomeACRONYM" => "Some ACRONYM".
                                

                                【讨论】:

                                  猜你喜欢
                                  • 2011-07-31
                                  • 1970-01-01
                                  • 2021-08-18
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多