web-dev-qa-db-fra.com

Ajouter des espaces avant les lettres majuscules

Étant donné la chaîne "ThisStringHasNoSpacesButItDoesHaveCapitals", quel est le meilleur moyen d'ajouter des espaces avant les majuscules. Donc, la chaîne de fin serait "Cette chaîne n'a pas d'espaces mais elle a des majuscules"

Voici ma tentative avec un RegEx

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

Les expressions rationnelles fonctionneront bien (j'ai même voté pour la réponse de Martin Brown), mais elles coûtent cher (et personnellement, je trouve n'importe quel motif plus long que quelques caractères prohibitifs).

Cette fonction 

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

Fera-le 100 000 fois en 2 968 750 tiques, la regex prendra 25 000 000 tiques (et c'est avec la regex compilée).

C’est mieux, pour une valeur donnée de mieux (c’est-à-dire plus vite) mais c’est plus de code à maintenir. "Mieux" est souvent un compromis entre des exigences concurrentes.

J'espère que cela t'aides :)

Mettre à jour
Cela fait un bon bout de temps que je n’y ai pas regardé, et je viens de me rendre compte que les timings n’ont pas été mis à jour depuis que le code a changé (il a seulement changé un peu).

Sur une chaîne contenant 'Abbbbbbbbb' répétée 100 fois (soit 1 000 octets), une série de 100 000 conversions prend la fonction codée à la main 4 517 177 ticks, et l'expression régulière ci-dessous prend 59 435 719, ce qui rend la fonction codée à la main exécutée dans 7,6% du temps Regex.

Mise à jour 2 Prendra-t-il en compte les acronymes? Il va maintenant! La logique de la déclaration si est assez obscure, comme vous pouvez le voir élargir à cela ...

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

... n'aide pas du tout!

Voici la méthode originale simple qui ne s'inquiète pas des acronymes

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

Votre solution a un problème en ce sens qu’elle place un espace avant la première lettre T afin que vous obteniez 

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

Pour contourner ce problème, cherchez également la lettre minuscule qui le précède, puis insérez l'espace au milieu:

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

Modifier 1:

Si vous utilisez @"(\p{Ll})(\p{Lu})", il récupérera également les caractères accentués.

Edit 2:

Si vos chaînes peuvent contenir des acronymes, vous pouvez utiliser ceci:

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

Donc, "DriveIsSCSICompatible" devient "Le lecteur est compatible SCSI"

130
Martin Brown

N'a pas testé les performances, mais en une ligne avec linq:

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

Je sais que c'est un ancien, mais c'est une extension que j'utilise quand j'ai besoin de faire ça:

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

Cela vous permettra d'utiliser MyCasedString.ToSentence()

12
Rob Hardy

Bienvenue sur Unicode

Toutes ces solutions sont essentiellement fausses pour le texte moderne. Vous devez utiliser quelque chose qui comprend le cas. Puisque Bob a demandé d'autres langues, je vais en donner un pour Perl.

Je propose quatre solutions, allant du pire au meilleur. Seul le meilleur a toujours raison. Les autres ont des problèmes. Voici un test pour vous montrer ce qui fonctionne et ce qui ne fonctionne pas et où. J’ai utilisé des traits de soulignement pour que vous puissiez voir où les espaces ont été placés, et j’ai marqué comme faux tout ce qui est faux. 

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

BTW, presque tout le monde ici a sélectionné la première façon, celle marquée "Worst". Quelques-uns ont sélectionné la deuxième voie, marquée "OK". Mais personne avant moi ne vous a montré comment utiliser l'approche "Meilleur" ou "Meilleur".

Voici le programme de test avec ses quatre méthodes:

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

Lorsque vous pourrez obtenir le même résultat que "Meilleur" sur cet ensemble de données, vous saurez que vous l’avez fait correctement. Jusque-là, vous ne le faites pas. Personne d'autre ici n'a fait mieux que "Ok", et la plupart l'ont fait "Pire". Je suis impatient de voir quelqu'un publier le code ℂ♯ correct.

Je remarque que le code de surbrillance de StackOverflow est misérablement misérable à nouveau. Ils font tous les mêmes vieux boiteux que (la plupart mais pas tous) le reste des approches pauvres mentionnées ici. N'est-il pas temps de mettre ASCII au repos? Cela n’a plus de sens, et prétendre que tout ce que vous avez est tout simplement faux. Cela crée un mauvais code.

8
tchrist

Je me suis attaché à créer une méthode d'extension simple basée sur le code de Binary Worrier, qui gérera correctement les acronymes et qui est répétable (ne permet pas de changer les mots déjà espacés). Voici mon résultat.

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

Voici les cas de tests unitaires auxquels cette fonction a réussi. J'ai ajouté la plupart des cas suggérés par tchrist à cette liste. Les trois de ceux qu'il ne passe pas (deux ne sont que des chiffres romains) sont commentés:

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

Binary Worrier, j'ai utilisé votre code suggéré, et il est plutôt bon, je n'ai qu'un ajout mineur à cela:

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

J'ai ajouté une condition !char.IsUpper(text[i - 1]). Cela corrigeait un bug qui pouvait transformer quelque chose comme 'AverageNOX' en 'Average N O X', ce qui est évidemment faux, car il devrait se lire 'Average NOX'.

Malheureusement, il y a toujours le bogue voulant que si vous avez le texte «FromAStart», vous obtiendrez «From AStart».

Des idées sur la résolution de ce problème?

4
Richard Priddy

Voilà le mien:

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

Assurez-vous que ne sont pas en plaçant des espaces au début de la chaîne, mais vous les mettez entre des majuscules consécutives. Certaines des réponses ici ne traitent pas de l'un ou l'autre de ces points. Il existe d'autres moyens que regex, mais si vous préférez l'utiliser, essayez ceci:

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

Le \B est un \b négatif, il représente donc une limite autre que Word. Cela signifie que le modèle correspond à "Y" dans XYzabc, mais pas dans Yzabc ou X Yzabc. En prime, vous pouvez l’utiliser sur une chaîne contenant des espaces et elle ne les doublera pas.

3
Justin Morgan

Ce que vous avez fonctionne parfaitement. Rappelez-vous simplement de réaffecter value à la valeur de retour de cette fonction.

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

Inspiré de @MartinBrown, Deux lignes de regex simple, qui résoudront votre nom, y compris les noms d'acyronymes, n'importe où dans la chaîne.

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

Voici comment vous pouvez le faire en 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
KCITGuy

Cette regex place un caractère d'espace devant chaque lettre majuscule:

using System.Text.RegularExpressions;

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

Faites attention à l’espace en face si "$ 1 $ 2", c’est ce qui sera fait.

C'est le résultat:

"This Is A String Without Spaces"
2
Matthias Thomann

En Ruby, via 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

J'ai pris Kevin Strikers excellente solution et converti en VB. Depuis que je suis bloqué dans .NET 3.5, je devais aussi écrire IsNullOrWhiteSpace. Cela passe tous ses tests.

<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

Cela semble être une bonne opportunité pour Aggregate. Ceci est conçu pour être lisible, pas nécessairement particulièrement rapide.

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

Un moyen simple d'ajouter des espaces après les lettres minuscules, majuscules ou les chiffres.

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

En plus de la réponse de Martin Brown, j'avais également un problème avec les chiffres. Par exemple: "Emplacement2" ou "Jan22" devrait être "Emplacement 2" et "22 janvier" respectivement.

Voici mon expression régulière pour le faire, en utilisant la réponse de Martin Brown:

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

Voici quelques sites intéressants pour comprendre la signification de chaque partie:

Analyseur d'expressions régulières basé sur Java (mais fonctionne pour la plupart des regex .net)

Action Script Analyzer

La regex ci-dessus ne fonctionnera pas sur le site de script d'action à moins que vous ne remplaciez le \p{Ll} par le [a-z], le \p{Lu} par le [A-Z] et le \p{Nd} par le [0-9].

0
Daryl

Toutes les réponses précédentes semblaient trop compliquées.

J'avais string qui avait un mélange de majuscules et de _ donc utilisé, string.Replace () pour créer le _, "" et a utilisé les éléments suivants pour ajouter un espace en majuscule.

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

Pour ceux qui recherchent une fonction C++ répondant à la même question, vous pouvez utiliser ce qui suit. Ceci est calqué sur la réponse donnée par @Binary Worrier. Cette méthode ne fait que conserver les acronymes automatiquement. 

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;

Les chaînes de tests que j'ai utilisées pour cette fonction et les résultats sont les suivants:

  • "helloWorld" -> "hello World"
  • "HelloWorld" -> "Bonjour le monde"
  • "HelloABCWorld" -> "Bonjour ABC World"
  • "HelloWorldABC" -> "Bonjour le monde ABC"
  • "ABCHelloWorld" -> "ABC Bonjour le monde"
  • "ABC HELLO WORLD" -> "ABC HELLO WORLD"
  • "ABCHELLOWORLD" -> "ABCHELLOWORLD"
  • "A" -> "A"
0
lbrendanl

Voici une solution plus complète qui ne place pas d'espaces devant les mots:

Remarque: J'ai utilisé plusieurs expressions rationnelles (pas concis, mais il gérera également les acronymes et les mots composés d'une seule lettre)

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

Dans

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

En dehors :

"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

Une implémentation avec fold, également appelée 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());

En plus de la demande, cette implémentation enregistre correctement les espaces et acronymes de début, intérieurs, de fin, par exemple, 

" SpacedWord " => " Spaced Word ",  

"Inner Space" => "Inner Space",  

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

Celui-ci comprend les acronymes et les pluriels d’acronyme et est un peu plus rapide que la réponse acceptée:

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

Passe ces tests:

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

Voici ma solution, basée sur la suggestion de Binary Worriers et sur l’intégration des commentaires de Richard Priddys, mais en tenant également compte du fait qu’un espace blanc peut exister dans la chaîne fournie afin qu’il n’ajoute pas d’espace blanc à côté d’un espace blanc existant.

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

Solution C # pour une chaîne d'entrée composée uniquement de caractères ASCII. Regex incorpore negative lookbehind pour ignorer une lettre majuscule (majuscule) qui apparaît au début de la chaîne. Utilise Regex.Replace () pour retourner la chaîne désirée.

Voir aussi démo de 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);
    }
}

Production attendue:

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

Update: Voici une variante qui gérera également acronymes (séquences de lettres majuscules).

Voir aussi demo regex101.com et demo 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);
    }
}

Production attendue:

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

Inspiré par la réponse binaire plus inquiétante, j’ai pris un élan à cet égard.

Voici le résultat:

/// <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 effectué avec un chronomètre exécutant 10000000 itérations et différentes longueurs de chaîne et combinaisons.

En moyenne 50% (peut-être un peu plus) plus vite que la réponse Binary Worrier.

0
João Sequeira

La question est un peu ancienne, mais de nos jours, il existe une belle bibliothèque sur Nuget qui fait exactement cela, ainsi que de nombreuses autres conversions en texte lisible par l'homme.

Découvrez Humanizer on GitHub ou Nuget.

Exemple

"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