113 votes

Comment remplacer plusieurs espaces blancs par un seul espace blanc ?

Disons que j'ai une chaîne de caractères telle que :

"Hello     how are   you           doing?"

Je voudrais une fonction qui transforme plusieurs espaces en un seul espace.

J'obtiendrais donc :

"Hello how are you doing?"

Je sais que je pourrais utiliser des expressions rationnelles ou appeler

string s = "Hello     how are   you           doing?".replace("  "," ");

Mais je devrais l'appeler plusieurs fois pour m'assurer que tous les espaces blancs séquentiels sont remplacés par un seul.

Existe-t-il déjà une méthode intégrée pour cela ?

0 votes

Pourriez-vous préciser si vous ne traitez que les espaces ou si vous traitez "tous" les espaces blancs ?

0 votes

Souhaitez-vous que les espaces blancs qui ne sont pas des espaces soient convertis en espaces ?

0 votes

Je voulais simplement dire que tous les espaces blancs dans les séries doivent être au maximum de 1.

211voto

Tim Hoolihan Points 6982
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");

2 votes

L'utilisation d'une expression régulière entraîne une surcharge de travail qui n'est pas nécessaire.

42 votes

Imo, éviter les regex si l'on est à l'aise avec elles est une optimisation prématurée.

8 votes

Si votre application n'est pas critique en termes de temps, elle peut se permettre une microseconde de surcharge de traitement.

54voto

Jon Skeet Points 692016

Cette question n'est pas aussi simple que d'autres affiches l'ont fait croire (et que je le pensais à l'origine) - parce que la question n'est pas aussi précise qu'elle devrait l'être.

Il y a une différence entre "espace" et "espace blanc". Si vous sólo signifient des espaces, vous devez alors utiliser une expression rationnelle du type " {2,}" . Si vous voulez dire tous les espaces blancs, c'est une autre affaire. Devrait tous les espaces blancs sont-ils convertis en espaces ? Qu'advient-il des espaces au début et à la fin ?

Pour le benchmark ci-dessous, j'ai supposé que vous ne vous intéressiez qu'aux espaces et que vous ne vouliez rien faire pour les espaces simples, même au début et à la fin.

Notez que l'exactitude est presque toujours plus importante que la performance. Le fait que la solution Split/Join supprime tout espace blanc en début ou en fin de texte (même s'il ne s'agit que d'espaces simples) est incorrect au regard des exigences que vous avez spécifiées (qui peuvent être incomplètes, bien entendu).

L'indice de référence utilise MiniBench .

using System;
using System.Text.RegularExpressions;
using MiniBench;

internal class Program
{
    public static void Main(string[] args)
    {

        int size = int.Parse(args[0]);
        int gapBetweenExtraSpaces = int.Parse(args[1]);

        char[] chars = new char[size];
        for (int i=0; i < size/2; i += 2)
        {
            // Make sure there actually *is* something to do
            chars[i*2] = (i % gapBetweenExtraSpaces == 1) ? ' ' : 'x';
            chars[i*2 + 1] = ' ';
        }
        // Just to make sure we don't have a \0 at the end
        // for odd sizes
        chars[chars.Length-1] = 'y';

        string bigString = new string(chars);
        // Assume that one form works :)
        string normalized = NormalizeWithSplitAndJoin(bigString);

        var suite = new TestSuite<string, string>("Normalize")
            .Plus(NormalizeWithSplitAndJoin)
            .Plus(NormalizeWithRegex)
            .RunTests(bigString, normalized);

        suite.Display(ResultColumns.All, suite.FindBest());
    }

    private static readonly Regex MultipleSpaces = 
        new Regex(@" {2,}", RegexOptions.Compiled);

    static string NormalizeWithRegex(string input)
    {
        return MultipleSpaces.Replace(input, " ");
    }

    // Guessing as the post doesn't specify what to use
    private static readonly char[] Whitespace =
        new char[] { ' ' };

    static string NormalizeWithSplitAndJoin(string input)
    {
        string[] split = input.Split
            (Whitespace, StringSplitOptions.RemoveEmptyEntries);
        return string.Join(" ", split);
    }
}

Quelques essais :

c:\Users\Jon\Test>test 1000 50
============ Normalize ============
NormalizeWithSplitAndJoin  1159091 0:30.258 22.93
NormalizeWithRegex        26378882 0:30.025  1.00

c:\Users\Jon\Test>test 1000 5
============ Normalize ============
NormalizeWithSplitAndJoin  947540 0:30.013 1.07
NormalizeWithRegex        1003862 0:29.610 1.00

c:\Users\Jon\Test>test 1000 1001
============ Normalize ============
NormalizeWithSplitAndJoin  1156299 0:29.898 21.99
NormalizeWithRegex        23243802 0:27.335  1.00

Ici, le premier chiffre est le nombre d'itérations, le deuxième est le temps nécessaire et le troisième est un score échelonné, 1,0 étant le meilleur.

Cela montre qu'au moins dans certains cas (y compris celui-ci), une expression régulière peut surpassent la solution du fractionnement et de la jonction, parfois avec une marge très importante.

Cependant, si vous passez à une exigence de "tous les espaces", alors Split/Join fait semblent gagner. Comme souvent, le diable se cache dans les détails...

1 votes

Excellente analyse. Il apparaît donc que nous avions tous deux raison à des degrés divers. Le code de ma réponse provient d'une fonction plus large qui a la capacité de normaliser tous les espaces blancs et/ou les caractères de contrôle à l'intérieur d'une chaîne de caractères, ainsi qu'au début et à la fin de celle-ci.

1 votes

Avec les caractères d'espacement que vous avez spécifiés, dans la plupart de mes tests, la regex et Split/Join étaient à peu près égaux - S/J avait un tout petit avantage, au détriment de la correction et de la complexité. Pour ces raisons, je préférerais normalement la regex. Ne vous méprenez pas, je suis loin d'être un fanboy des regex, mais je n'aime pas écrire du code plus complexe pour des raisons de performance sans avoir vraiment testé la performance au préalable.

0 votes

NormalizeWithSplitAndJoin créera beaucoup plus de déchets, il est difficile de dire si un vrai problème sera touché plus de temps GC que le banchmark.

19voto

Brandon Points 35624

La solution la plus simple serait d'utiliser un expresso régulier. Si vous écrivez l'expression régulière de la bonne manière, vous n'aurez pas besoin d'appels multiples.

Remplacez-la par ceci :

string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " ");

0 votes

Mon seul problème avec @"\s{2,}" est qu'il ne remplace pas les tabulations simples et les autres caractères d'espacement Unicode par un espace. Si vous devez remplacer 2 tabulations par un espace, vous devriez probablement remplacer 1 tabulation par un espace. @"\s+" le fera pour vous.

18voto

Jon Skeet Points 692016

Bien que les réponses existantes soient satisfaisantes, j'aimerais souligner une approche qui a fait ses preuves. ne travail :

public static string DontUseThisToCollapseSpaces(string text)
{
    while (text.IndexOf("  ") != -1)
    {
        text = text.Replace("  ", " ");
    }
    return text;
}

Cette opération peut se répéter à l'infini. Quelqu'un peut-il deviner pourquoi ? (Je n'ai découvert cette question que lorsqu'elle a été posée dans un groupe de discussion il y a quelques années... quelqu'un l'a en fait rencontrée comme un problème).

0 votes

Je crois me souvenir que cette question a été posée il y a quelques temps sur SO. IndexOf ignore certains caractères, contrairement à Replace. Ainsi, le double espace a toujours été présent, mais n'a jamais été supprimé.

19 votes

C'est parce que IndexOf ignore certains caractères Unicode, le coupable spécifique dans ce cas étant un caractère asiatique. Hmm, un non-jointeur à largeur nulle d'après Google.

0 votes

Je l'ai appris à mes dépens :( stackoverflow.com/questions/9260693/

4voto

MAK Points 12571

Comme nous l'avons déjà souligné, il est facile de le faire à l'aide d'une expression régulière. J'ajouterai simplement que vous voudrez peut-être ajouter un .trim() à cela pour vous débarrasser des espaces blancs de début et de fin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X