在大写字母之前添加空格


193

给定字符串“ ThisStringHasNoSpacesButItDoesHaveCapitals”,什么是在大写字母之前添加空格的最佳方法。因此,结束字符串将为“此字符串没有空格,但确实有大写字母”

这是我对RegEx的尝试

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

2
您对所采用的方法是否有特别的抱怨?这可能有助于我们改进您的方法。
布莱尔·康拉德

如果正则表达式有效,那么我会坚持下去。正则表达式可用于字符串操作。
Michael Meadows

我只是很好奇,是否有更好的甚至是内置的方法。我什至很好奇看到其他语言的其他方法。
鲍勃”

2
您的代码根本不起作用,因为修改后的字符串是“替换”函数的返回值。使用以下代码行:'System.Text.RegularExpressions.Regex.Replace(value,“ [AZ]”,“ $ 0”)。Trim();' 它会完美地工作。(只是发表评论,因为我偶然发现了这篇文章,却没有人真正看到,您的代码出了什么问题。)
Mattu475 2015年

Regex.Replace(“ ThisStringHasNoSpacesButItDoesHaveCapitals”,@“ \ B [AZ]”,m =>“” + m);
萨基布·阿迪尔

Answers:


203

正则表达式可以很好地工作(我什至投票赞成马丁·布朗的答案),但是它们很昂贵(而且我个人发现任何模式的长度都比几个字符令人讨厌的更长)

该功能

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

它将在9686.850个滴答声中执行100,000次,正则表达式将花费25,000,000个滴答声(并且已编译正则表达式)。

给定更好(即更快)的值会更好,但是需要维护更多的代码。“更好”通常是竞争需求的折衷方案。

希望这可以帮助 :)

更新
自从我看了好一阵子以来,我才意识到自从代码更改(只是更改了一点)以来,时序没有更新。

在重复“ Abbbbbbbbb”的字符串上重复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();
}

8
if(char.IsUpper(text [i])&& text [i-1]!='')如果重新运行上面的代码,它会继续添加空格,如果大写字母之前有空格,这将停止添加空格信。
Paul Talbot

我不确定,所以我想问一下,此方法是否可以处理马丁·布朗的回答“ DriveIsSCSICompatible”中所述的首字母缩写词,因此理想情况下将成为“ Drive Is SCSI Compatible”
Coops 2013年

通过用新更新的if语句替换for语句的内容,使它变成1个字符,我可能做错了什么?
Coops 2013年

1
为char.IsLetter(text [i + 1])添加支票有助于使用带有特殊字符和数字的首字母缩写(即ABC_DEF不会被拆分为AB C_DEF)。
HeXanon

1
我不确定首字母缩写词部分在关闭时是否正确。我刚刚运行了一个测试,“ ASentenceABC”扩展为“ ASentence AB C”。应该是“ A Sentence AB C”
Tim Rutter,

149

您的解决方案存在一个问题,因为它在第一个字母T之前放置了一个空格,所以您得到

" 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”


3
您还不能只保留原始RegEx和Trim()的结果吗?
PandaWood

3
@PandaWood您可以,但是它需要另一个内存分配和字符串副本。就是说,如果担心性能,则Regex可能不是最佳选择。
马丁·布朗

您是否还可以使用"([^A-Z\\s])([A-Z])",甚至使用首字母缩写词?
Ruben9922

82

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

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

18

我知道这是一个旧的,但这是我需要执行的扩展:

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 2015年

1
感谢@ user1069816。我已经更改了扩展名,以使用SelectMany包含索引的重载,这样就避免了第一个字母和对的额外调用的不必要的潜在开销TrimStart(' ')。抢。
Rob Hardy 2015年

8

欢迎使用Unicode

所有这些解决方案对于现代文本基本上都是错误的。您需要使用一些可以理解大小写的东西。由于Bob要求使用其他语言,因此我将为Perl讲几句。

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

Testing TheLoneRanger
               Worst:    The_Lone_Ranger
               Ok:       The_Lone_Ranger
               Better:   The_Lone_Ranger
               Best:     The_Lone_Ranger
Testing MountMKinleyNationalPark
     [WRONG]   Worst:    Mount_MKinley_National_Park
     [WRONG]   Ok:       Mount_MKinley_National_Park
     [WRONG]   Better:   Mount_MKinley_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

顺便说一句,这里几乎每个人都选择了第一种方式,一个标有“最差”的方式。一些人选择了第二种方式,标记为“确定”。但是在我之前,没有其他人向您展示了如何做“更好”或“最好”的方法。

这是具有四种方法的测试程序:

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

# First I'll prove these are fine variable names:
my (
    $TheLoneRanger              ,
    $MountMKinleyNationalPark  ,
    $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
    MountMKinleyNationalPark
    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的突出显示代码再次变得愚蠢。他们正在与这里提到的其余所有可怜方法一样(几乎但不是全部)一样old脚。使ASCII停止工作过去了很长时间吗?它不再有意义了,假装这就是你所拥有的完全是错误的。它造成了错误的代码。


您的“最佳”答案似乎是目前为止最接近的答案,但似乎并不能说明开头的标点符号或其他开头的非小写字母。这似乎最适合我(在Java中):replaceAll(“(?<= [^^ \\ p {javaUpperCase}])(?= [\\ p {javaUpperCase}])”,“”);
Randyaa 2011年

嗯 在此示例中,我不确定罗马数字是否应该算作大写。字母修饰符示例绝对不应该计算在内。如果您访问McDonalds.com,将看到它写的没有空格。
马丁·布朗

还应注意,您将永远无法做到完美。例如,我想看一个示例,该示例整理出“ AlexandervonHumboldt”,其最终应为“ Alexander von Humboldt”。当然,有些语言没有大写字母和小写字母。
马丁·布朗

8

我着手根据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”。它将返回“ Regular O Ts”
Patee Gutee,

4

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 NO X”,这显然是错误的,因为它应显示为“ Average NOX”。

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

有什么想法解决吗?


也许类似的方法会起作用:char.IsUpper(text [i])&&(char.IsLower(text [i-1])||(char.IsLower(text [i + 1]))
马丁·布朗

1
这是正确的:if (char.IsUpper(text[i]) && !(char.IsUpper(text[i - 1]) && char.IsUpper(text[i + 1])))测试结果:“从开始”,“从开始”,“从开始”,但是您需要i < text.Length - 1在for循环条件下忽略最后一个字符并防止超出范围的异常。
CallMeLaNN

哦,一样。!(a && b)和(!a ||!b),因为lower =!upper。
CallMeLaNN

3

这是我的:

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 <string>?
马丁·布朗

List <string>可以。对于那个很抱歉。
科里·福伊

@Martin他始终具有正确的语法,只是隐藏在<pre><code>code</code></pre>块中,而不是Markdown语法。无需对他投反对票(如果那是您的话)。
乔治·斯托克

3

确保你没有在字符串的开头插入空格,但你把它们连续的首都之间。这里的一些答案不能解决其中的一个或两个问题。除了正则表达式外,还有其他方法,但是如果您喜欢使用它,请尝试以下方法:

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

\B是否定的\b,所以它代表了一个非单词边界。这意味着模式在中匹配“ Y” XYzabc,但在Yzabc或中不匹配X Yzabc。另外,您可以在带有空格的字符串上使用它,并且不会使它们加倍。


3

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

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"

1
如果您还希望分隔数字,请改用此正则表达式模式:"([A-Z0-9])([a-z]*)"
Matthias Thomann

2

您所拥有的完美。只要记住要重新分配value该函数的返回值即可。

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

2

这是您可以在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

2

源自@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”
Patee Gutee,

@PateeGutee OP想要在国会大厦前摆放空间,他没有提到缩写,我们已经解决了生产鳕鱼的问题
约翰尼

您可以显示此修复程序吗?我的数据中有这样的字符串,它给了我错误的结果。谢谢。
Patee Gutee

@PateeGutee对不起,我看错了您想要的内容。多元化是一个不同的问题,`RegularOTs'您期望发生什么“ Regular OTs”或“ Regular OTs”
约翰尼

1
@PateeGutee我已经为您更新了我的答案,我认为应该可以使用
约翰尼

1
replaceAll("(?<=[^^\\p{Uppercase}])(?=[\\p{Uppercase}])"," ");

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

1

在Ruby中,通过Regexp:

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

1
哎呀,对不起。我错过了特定于C#的问题,并在此处发布了Ruby答案:(
Artem 2012年

1

我采用了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

1

这个问题有点老了,但是如今在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可以工作,但是该软件包未在我的解决方案中编译。一个好主意,如果可行。
2014年

1

似乎是一个很好的机会Aggregate。这被设计为可读的,不一定特别快。

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

0

除了马丁·布朗(Martin Brown)的答案之外,我还有一个数字问题。例如:“ Location2”或“ Jan22”应分别为“ Location 2”和“ Jan 22”。

这是我使用马丁·布朗的答案做的正则表达式:

"((?<=\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]


0

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

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

0

对于正在寻找回答同一问题的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 HELLO WORLD”->“ ABC HELLO WORLD”
  • “ ABCHELLOWORLD”->“ ABCHELLOWORLD”
  • “ A”->“ A”

0

C#为只包含ASCII字符的输入字符串的解决方案。在正则表达式包含负回顾后忽略出现在字符串的开头大写字母(大写)字母。使用Regex.Replace()返回所需的字符串。

另请参阅regex101.com演示

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演示ideone.com演示

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]

0

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

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

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.)

这将输出“此字符串没有空格,但确实有大写字母”
Andy Robinson

嗨@AndyRobinson,谢谢。我更改为使用多个正则表达式替换。不确定是否有更简洁的方法,但是现在可以使用。
CrazyTim 2015年

0

先前的所有答复看起来都过于复杂。

我有一个混合使用大写字母和_的字符串,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.
        }
    }
}

0

受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%(也许更多)。


0
    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;
    }

0

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

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";

接受的答案涉及值为null的情况
Chris F Carroll 2014年

这会在输出前面添加一个额外的空间,即HireDate =>“ Hire Date”。需要final.TrimStart之类的东西。我认为这就是下面其他答案之一,但是由于重新排序,我不确定他是否在和您聊天,因为他的答案基于RegEx。
b_levitt 2015年

好捕获...应该在我的测试中添加了开始和结束标记...现在已修复。
Serj Sagan

与此处发布的其他解决方案类似,它失败,并带有字符串“ RegularOTs”。它将返回“ Regular O Ts”
Patee Gutee,

感谢您提出缩写复数形式,我也为此进行了更新。
Serj Sagan

0

的实现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".

0

在小写字母,大写字母或数字后添加空格的简单方法。

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

1
不鼓励仅使用代码的答案。请单击“ 编辑”并添加一些单词,以概括您的代码如何解决该问题,或者说明您的答案与以前的答案有何不同。评论来自
尼克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.