web-dev-qa-db-ger.com

Fügen Sie Leerzeichen vor Großbuchstaben hinzu

Angesichts der Zeichenfolge "ThisStringHasNoSpacesButItDoesHaveCapitals" ist dies der beste Weg, Leerzeichen vor den Großbuchstaben einzufügen. Der Endstring wäre also "Dieser String hat keine Leerzeichen, aber er hat Großbuchstaben"

Hier ist mein Versuch mit einem RegEx

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

Die Regexen werden gut funktionieren (ich habe sogar Martin Browns Antwort gewählt), aber sie sind teuer (und ich persönlich finde jedes Muster länger als ein paar Charaktere, die unnachgiebig stumpf sind).

Diese Funktion 

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

Wird es in 2.968.750 Ticks 100.000-mal machen, nimmt der Regex 25.000.000 Ticks (und das mit dem Regex, der kompiliert wurde).

Es ist besser, für einen gegebenen Wert von besser (d. H. Schneller), jedoch mehr Code zu verwalten. "Besser" ist häufig ein Kompromiss zwischen konkurrierenden Anforderungen.

Hoffe das hilft :)

Update
Es ist schon lange her, seit ich mir das angeschaut habe, und mir wurde gerade klar, dass die Timings nicht aktualisiert wurden, seit der Code geändert wurde (es hat sich nur ein wenig geändert).

Bei einer Zeichenfolge mit "Abbbbbbbbb", die 100 Mal (dh 1.000 Bytes) wiederholt wird, dauert ein Durchlauf von 100.000 Konvertierungen der handcodierten Funktion 4.517.177 Ticks, und die nachfolgende Regex benötigt 59.435.719, so dass die handcodierte Funktion 7,6% der Zeit ausführt Regex.

Update 2 Werden Akronyme berücksichtigt? Es wird jetzt! ... Die Logik der if-Anweisung ist ziemlich dunkel, wie Sie sehen können, wenn Sie es auf diese erweitern.

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

... hilft überhaupt nicht!

Hier ist die ursprüngliche Methode simple, die sich nicht um Akronyme kümmert

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

Ihre Lösung hat ein Problem insofern, als vor dem ersten Buchstaben T ein Leerzeichen steht 

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

Um dies zu umgehen, suchen Sie nach dem vorangestellten Kleinbuchstaben und fügen Sie dann das Leerzeichen in die Mitte ein:

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

Edit 1:

Wenn Sie @"(\p{Ll})(\p{Lu})" verwenden, werden auch Zeichen mit Akzent aufgenommen.

Edit 2:

Wenn Ihre Zeichenfolgen Akronyme enthalten können, möchten Sie Folgendes verwenden:

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

So wird "DriveIsSCSICompatible" zu "Laufwerk ist SCSI-kompatibel"

130
Martin Brown

Performance nicht getestet, aber hier in einer Zeile mit linq:

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

Ich weiß, dass dies eine alte ist, aber diese Erweiterung verwende ich, wenn ich dies tun muss:

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

Dadurch können Sie MyCasedString.ToSentence() verwenden.

12
Rob Hardy

Willkommen bei Unicode

Alle diese Lösungen sind im Wesentlichen für moderne Texte falsch. Sie müssen etwas verwenden, das den Fall versteht. Da Bob nach anderen Sprachen gefragt hat, gebe ich ein paar für Perl.

Ich biete vier Lösungen an, die vom schlechtesten bis zum besten reichen. Nur der Beste hat immer Recht. Die anderen haben Probleme. Hier ist ein Testlauf, um zu zeigen, was funktioniert und was nicht und wo. Ich habe Unterstriche verwendet, damit Sie sehen können, wo die Leerzeichen platziert wurden, und ich habe etwas falsches als falsch markiert. 

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

Übrigens hat fast jeder hier den ersten Weg gewählt, den mit "schlechtesten". Einige haben den zweiten Weg gewählt, markiert mit "OK". Aber niemand anderes vor mir hat Ihnen gezeigt, wie Sie entweder den "besseren" oder den "besten" Ansatz verwenden.

Hier ist das Testprogramm mit seinen vier Methoden:

#!/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;
}

Wenn Sie in diesem Datensatz genau wie "Beste" Punkte erzielen können, wissen Sie, dass Sie dies richtig gemacht haben. Bis dahin bist du nicht Niemand sonst hat es besser gemacht als "Ok", und die meisten haben es "Worst". Ich freue mich darauf, jemanden zu sehen, der den richtigen Code schreibt.

Ich stelle fest, dass der Hervorhebungscode von StackOverflow wieder misstrauisch ist. Sie machen alle die gleichen alten Lahmen wie die meisten der hier erwähnten armen Ansätze (die meisten, aber nicht alle). Ist es nicht lange her, ASCII zur Ruhe zu setzen? Es macht keinen Sinn mehr und es ist einfach falsch, so zu tun, als wäre alles, was Sie haben. Das sorgt für schlechten Code.

8
tchrist

Ich habe mir vorgenommen, eine einfache Erweiterungsmethode zu erstellen, die auf dem Code von Binary Worrier basiert, der Akronyme richtig handhabt und wiederholbar ist. Hier ist mein Ergebnis.

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

Hier sind die Unit-Testfälle, die diese Funktion besteht. Ich habe die meisten Fälle von tchrist zu dieser Liste hinzugefügt. Die drei von denen, die es nicht passiert (zwei sind nur römische Ziffern), sind auskommentiert:

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

Binär Worrier, ich habe Ihren vorgeschlagenen Code verwendet, und er ist ziemlich gut, ich habe nur eine kleine Ergänzung:

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

Ich habe eine Bedingung !char.IsUpper(text[i - 1]) hinzugefügt. Dies behebte einen Fehler, der dazu führte, dass etwas wie 'AverageNOX' in 'Average N O X' umgewandelt wurde, was offensichtlich falsch ist, da 'Average NOX' lauten sollte.

Leider hat dies immer noch den Fehler, dass Sie, wenn Sie den Text "FromAStart" haben, "From AStart" erhalten würden.

Irgendwelche Gedanken zur Behebung dieses Problems?

4
Richard Priddy

Hier ist meins:

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

Stellen Sie sicher, dass Sie nicht Leerzeichen am Anfang der Zeichenfolge setzen, aber Sie sind sie zwischen aufeinanderfolgenden Kapitellen setzen. Einige Antworten beziehen sich nicht auf einen oder beide dieser Punkte. Es gibt andere Möglichkeiten als reguläre Ausdrücke. Wenn Sie dies jedoch vorziehen, versuchen Sie Folgendes:

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

Der \B ist ein negierter \b, also eine Nicht-Wort-Grenze. Es bedeutet, dass das Muster in XYzabc mit "Y" übereinstimmt, jedoch nicht in Yzabc oder X Yzabc. Als kleinen Bonus können Sie dies auf eine Zeichenfolge mit Leerzeichen anwenden, die sie nicht verdoppelt.

3
Justin Morgan

Was Sie haben, funktioniert perfekt. Denken Sie daran, value dem Rückgabewert dieser Funktion neu zuzuweisen.

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

Inspiriert von @MartinBrown, Zwei Zeilen von Simple Regex, die Ihren Namen auflösen, einschließlich Acyronyms an einer beliebigen Stelle im String.

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

So können Sie es in SQL machen 

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
KCITGuy

Dieser Regex setzt vor jedem Großbuchstaben ein Leerzeichen:

using System.Text.RegularExpressions;

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

Achten Sie auf das Leerzeichen vor "$ 1 $ 2".

Das ist das Ergebnis:

"This Is A String Without Spaces"
2

In Ruby über Regexp:

"FooBarBaz".gsub(/(?!^)(?=[A-Z])/, ' ') # => "Foo Bar Baz"
1
Artem
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
cyril
replaceAll("(?<=[^^\\p{Uppercase}])(?=[\\p{Uppercase}])"," ");
1
Randyaa

Ich habe Kevin Strikers ausgezeichnete Lösung genommen und auf VB umgestellt. Da ich in .NET 3.5 gesperrt bin, musste ich auch IsNullOrWhiteSpace schreiben. Dies hat alle seine Prüfungen bestanden.

<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
Brad Irby

Scheint eine gute Gelegenheit für Aggregate. Dies ist lesbar, nicht unbedingt besonders schnell.

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

Eine einfache Möglichkeit, Leerzeichen nach Kleinbuchstaben, Großbuchstaben oder Ziffern einzufügen.

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

Neben Martin Browns Answer hatte ich auch ein Problem mit Zahlen. Zum Beispiel: "Location2" oder "Jan22" sollte "Location 2" bzw. "Jan 22" sein.

Hier ist mein regulärer Ausdruck dafür, indem ich Martin Browns Antwort verwende:

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

Hier sind ein paar großartige Orte, um herauszufinden, was jeder Teil auch bedeutet:

Java-basierter Regular Expression Analyzer (funktioniert aber für die meisten .net-regulären Ausdrücke)

Action Script Based Analyzer

Der obige reguläre Ausdruck funktioniert auf der Aktionsskript-Site nur, wenn Sie alle \p{Ll} durch [a-z], den \p{Lu} durch [A-Z] und \p{Nd} durch [0-9] ersetzen.

0
Daryl

Alle vorherigen Antworten waren zu kompliziert.

Ich hatte eine Zeichenfolge, die eine Mischung aus Großbuchstaben und _ hatte, also string.Replace (), um das _, "" zu erstellen, und verwendete Folgendes, um ein Leerzeichen an den Großbuchstaben einzufügen.

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
st3_121

Für alle, die nach einer C++ - Funktion suchen, die dieselbe Frage beantwortet, können Sie Folgendes verwenden. Dies basiert auf der Antwort von @Binary Worrier. Bei dieser Methode werden Akronyme nur automatisch beibehalten. 

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;

Die Testzeichenfolgen, die ich für diese Funktion verwendet habe, und die Ergebnisse sind:

  • "Hallo Welt" -> "Hallo Welt"
  • "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
lbrendanl

Hier ist eine gründlichere Lösung, die keine Leerzeichen vor Wörtern setzt:

Hinweis: Ich habe mehrere Regexs verwendet (nicht knapp, aber es werden auch Akronyme und Wörter mit einem Buchstaben behandelt).

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

Im

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

Aus :

"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.)
0
CrazyTim

Eine Implementierung mit fold, auch bekannt als 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());

Zusätzlich zur Anforderung werden durch diese Implementierung führende, innere, nachgestellte Leerzeichen und Akronyme richtig gespeichert, z. B. 

" SpacedWord " => " Spaced Word ",  

"Inner Space" => "Inner Space",  

"SomeACRONYM" => "Some ACRONYM".
0
Artru

Dieses enthält Akronyme und Akronyme plurals und ist etwas schneller als die akzeptierte Antwort:

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

Besteht diese Tests:

string test1 = "RegularOTs";
string test2 = "ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ";
string test3 = "ThisStringHasNoSpacesButItDoesHaveCapitals";
0
Serj Sagan

Hier ist meine Lösung, basierend auf dem Vorschlag von Binary Worriers und dem Bauen in Richard Priddys 'Kommentaren, wobei jedoch auch berücksichtigt wird, dass Leerzeichen in der bereitgestellten Zeichenfolge vorhanden sein können, sodass neben den vorhandenen Leerzeichen keine Leerzeichen hinzugefügt werden.

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
Yetiish

Eine C # - Lösung für eine Eingabezeichenfolge, die nur aus ASCII -Zeichen besteht. Regex enthält negative lookbehind, um einen Großbuchstaben (Großbuchstaben) zu ignorieren, der am Anfang der Zeichenfolge steht. Verwendet Regex.Replace () , um die gewünschte Zeichenfolge zurückzugeben.

Siehe auch 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);
    }
}

Erwartete Ausgabe:

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

Update: Hier ist eine Variante, die auch Akronyme (Sequenzen von Großbuchstaben) behandelt.

Siehe auch regex101.com demo und ideone.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);
    }
}

Erwartete Ausgabe:

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

Inspiriert von der Antwort von Binary Worrier machte ich einen Schwung.

Hier ist das Ergebnis:

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

Test mit Stoppuhr mit 10000000 Iterationen und verschiedenen Saitenlängen und -kombinationen.

Im Durchschnitt 50% (vielleicht etwas mehr) schneller als die Antwort von Binary Worrier.

0
João Sequeira

Die Frage ist ein bisschen alt, aber heutzutage gibt es eine Bibliothek von Nice auf Nuget, die genau das tut, genauso wie viele andere Konvertierungen in für Menschen lesbaren Text.

Check out Humanizer on GitHub oder Nuget.

Beispiel

"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"
0
Jonas Pegerfalk