Un moyen efficace pour supprimer TOUS les espaces blancs de String?

359

J'appelle une API REST et je reçois une réponse XML. Il renvoie une liste de noms d'espace de travail et j'écris une IsExistingWorkspace()méthode rapide . Étant donné que tous les espaces de travail sont constitués de caractères contigus sans espace blanc, je suppose que le moyen le plus simple de savoir si un espace de travail particulier est dans la liste est de supprimer tous les espaces (y compris les nouvelles lignes) et de le faire (XML est la chaîne reçue du Web demande):

XML.Contains("<name>" + workspaceName + "</name>");

Je sais que c'est sensible à la casse, et je compte sur cela. J'ai juste besoin d'un moyen de supprimer efficacement tous les espaces d'une chaîne. Je sais que RegEx et LINQ peuvent le faire, mais je suis ouvert à d'autres idées. Je suis surtout préoccupé par la vitesse.

Corey Ogburn
la source
6
L'analyse XML avec regex est presque aussi mauvaise que l' analyse HTML avec regex .
dtb
3
@henk holterman; Voir ma réponse ci-dessous, regexp ne semble pas être le plus rapide dans tous les cas.
Henk J Meulekamp
Regex ne semble pas du tout le plus rapide. J'ai résumé les résultats de différentes façons de supprimer les espaces blancs d'une chaîne. Le résumé est dans une réponse ci-dessous - stackoverflow.com/a/37347881/582061
Stian Standahl

Réponses:

617

C'est le moyen le plus rapide que je connaisse, même si vous avez dit que vous ne vouliez pas utiliser d'expressions régulières:

Regex.Replace(XML, @"\s+", "")
slandau
la source
1
Je pourrais utiliser une expression régulière, je ne suis pas sûr que ce soit le moyen le plus rapide.
Corey Ogburn
1
J'en suis presque sûr. À tout le moins dans les coulisses, vous devez vérifier chaque personnage, et cela ne fait qu'une recherche linéaire.
slandau
19
N'est-ce pas Regex.Replace(XML, @"\s+", "")?
Jan-Peter Vos
61
Si vous prévoyez de le faire plusieurs fois, créez et stockez une instance Regex. Cela permettra d'économiser les frais généraux de sa construction à chaque fois, ce qui est plus cher que vous ne le pensez. private static readonly Regex sWhitespace = new Regex(@"\s+"); public static string ReplaceWhitespace(string input, string replacement) { return sWhitespace.Replace(input, replacement); }
hypehuman
10
Pour ceux qui ne connaissent pas RegEx et recherchent une explication sur ce que signifie cette expression, \ssignifie "correspondre à n'importe quel jeton d'espace blanc" et +signifie "correspondre à un ou plusieurs jetons de procédure". Aussi RegExr est un site agréable à pratiquer l' écriture d' expressions avec RegEx, si vous voulez expérimenter.
2017
181

J'ai un moyen alternatif sans regexp, et il semble fonctionner assez bien. C'est une suite à la réponse de Brandon Moretz:

 public static string RemoveWhitespace(this string input)
 {
    return new string(input.ToCharArray()
        .Where(c => !Char.IsWhiteSpace(c))
        .ToArray());
 }

Je l'ai testé dans un test unitaire simple:

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace1(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = input.RemoveWhitespace();
    }
    Assert.AreEqual(expected, s);
}

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace2(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = Regex.Replace(input, @"\s+", "");
    }
    Assert.AreEqual(expected, s);
}

Pour 1 000 000 de tentatives, la première option (sans regexp) s'exécute en moins d'une seconde (700 ms sur ma machine) et la seconde prend 3,5 secondes.

Henk J Meulekamp
la source
40
.ToCharArray()n'est pas nécessaire; vous pouvez utiliser .Where()directement sur une chaîne.
ProgramFOX
10
Juste pour noter ici. Regex est plus lent ... sur les petites cordes! Si vous dites que vous aviez une version numérisée d'un volume sur la législation fiscale américaine (~ million de mots?), Avec une poignée d'itérations, Regex est de loin le roi! Ce n'est pas ce qui est plus rapide, mais ce qui devrait être utilisé dans quelles circonstances. Vous n'avez prouvé que la moitié de l'équation ici. -1 jusqu'à ce que vous prouviez la seconde moitié du test afin que la réponse donne plus de détails sur ce qui doit être utilisé.
Piotr Kula
17
@ppumkin Il a demandé une suppression en un seul passage des espaces blancs. Pas plusieurs itérations d'autres traitements. Je ne vais pas faire de cette suppression d'espaces blancs en un seul passage un article détaillé sur l'analyse comparative du traitement de texte.
Henk J Meulekamp
1
Vous avez dit qu'il était préférable de ne pas l'utiliser pour regex cette fois mais n'avez pas dit pourquoi.
Piotr Kula
2
@ProgramFOX, dans une autre question (ne peut pas le trouver facilement), j'ai remarqué qu'au moins dans certaines requêtes, l'utilisation ToCharArrayest plus rapide que l'utilisation .Where()directe sur la chaîne. Cela a quelque chose à voir avec la surcharge dans IEnumerable<>chaque étape d'itération, et ToCharArrayétant très efficace (copie de bloc) et le compilateur optimise l'itération sur les tableaux. Pourquoi cette différence existe, personne n'a pu m'expliquer, mais mesurez avant de vous retirer ToCharArray().
Abel
87

Essayez la méthode de remplacement de la chaîne en C #.

XML.Replace(" ", string.Empty);
Mike_K
la source
28
Ne supprime pas les onglets ou les nouvelles lignes. Si je fais plusieurs suppressions maintenant, je fais plusieurs passes sur la chaîne.
Corey Ogburn
11
Votez pour ne pas avoir supprimé tous les espaces, comme le font les réponses de slandau et Henk.
Matt Sach
@MattSach pourquoi ne supprime-t-il PAS TOUS les espaces blancs?
Zapnologica
4
@Zapnologica Il ne fait que remplacer les caractères d'espace. L'OP a également demandé le remplacement des sauts de ligne (qui sont des caractères "blancs", même s'ils ne sont pas des caractères spatiaux).
Matt Sach
75

Ma solution consiste à utiliser Split and Join et c'est étonnamment rapide, en fait la plus rapide des meilleures réponses ici.

str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));

Timings pour 10 000 boucles sur une chaîne simple avec des espaces incluant de nouvelles lignes et tabulations

  • split / join = 60 millisecondes
  • linq chararray = 94 millisecondes
  • regex = 437 millisecondes

Améliorez cela en l'enveloppant dans une méthode pour lui donner un sens, et faites-en également une méthode d'extension pendant que nous y sommes ...

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
kernowcode
la source
3
J'aime vraiment cette solution, j'en utilise une similaire depuis les jours pré-LINQ. En fait, je suis impressionné par les performances de LINQ et quelque peu surpris par l'expression régulière. Peut-être que le code n'était pas aussi optimal qu'il aurait pu l'être pour regex (vous devrez mettre en cache l'objet regex par exemple). Mais le nœud du problème est que la "qualité" des données importera beaucoup. Peut-être qu'avec de longues chaînes, l'expression régulière surpassera les autres options. Ce sera une référence amusante à réaliser ... :-)
Loudenvier
1
Comment par défaut (chaîne []) == une liste de tous les espaces blancs? Je le vois fonctionner, mais je ne comprends pas comment?
Jake Drew
5
@kernowcode Vous voulez dire l'ambiguïté entre les 2 surcharges avec string[]et char[]? il vous suffit de préciser celui que vous voulez par exemple: string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));. C'est en fait ce que fait votre appel defaultdans ce cas car il revient nullégalement: il aide le compilateur à décider quelle surcharge choisir. D'où mon commentaire car la déclaration dans votre commentaire "Split a besoin d'un tableau valide et null ne fera pas ..." est fausse. Pas grave, je pensais juste qu'il fallait le mentionner puisque Jake Drew avait demandé comment cela fonctionnait. +1 pour votre réponse
Frank J
6
Idée string.Concat("H \ne llo Wor ld".Split())
sympa
3
La solution michaelkrisper est très lisible. J'ai fait un test et «split / join» (162 millisecondes) a mieux performé que «split / concat» (180 millisecondes) pour 10 000 itérations de la même chaîne.
kernowcode
45

S'appuyant sur la réponse de Henks j'ai créé quelques méthodes de test avec sa réponse et quelques méthodes ajoutées, plus optimisées. J'ai trouvé que les résultats diffèrent en fonction de la taille de la chaîne d'entrée. Par conséquent, j'ai testé avec deux jeux de résultats. Dans la méthode la plus rapide, la source liée a un moyen encore plus rapide. Mais, comme il est caractérisé comme dangereux, je l'ai laissé de côté.

Résultats de la chaîne d'entrée longue:

  1. InPlaceCharArray: 2021 ms ( réponse de Sunsetquest ) - ( Source originale )
  2. Séparation de chaîne puis jointure: 4277 ms (réponse de Kernowcode )
  3. Lecteur de cordes: 6082 ms
  4. LINQ utilisant le caractère natif. Espace blanc: 7357 ms
  5. LINQ: 7746 ms ( réponse de Henk )
  6. ForLoop: 32320 ms
  7. RegexCompilé: 37157 ms
  8. Regex: 42940 ms

Résultats de chaîne d'entrée courts:

  1. InPlaceCharArray: 108 ms ( réponse de Sunsetquest ) - ( Source originale )
  2. Séparation de chaîne puis jointure: 294 ms ( réponse de Kernowcode )
  3. Lecteur de cordes: 327 ms
  4. ForLoop: 343 ms
  5. LINQ utilisant le caractère natif. IsWhitespace: 624 ms
  6. LINQ: 645 ms ( réponse de Henk )
  7. RegexCompilé: 1671 ms
  8. Regex: 2599 ms

Code :

public class RemoveWhitespace
{
    public static string RemoveStringReader(string input)
    {
        var s = new StringBuilder(input.Length); // (input.Length);
        using (var reader = new StringReader(input))
        {
            int i = 0;
            char c;
            for (; i < input.Length; i++)
            {
                c = (char)reader.Read();
                if (!char.IsWhiteSpace(c))
                {
                    s.Append(c);
                }
            }
        }

        return s.ToString();
    }

    public static string RemoveLinqNativeCharIsWhitespace(string input)
    {
        return new string(input.ToCharArray()
            .Where(c => !char.IsWhiteSpace(c))
            .ToArray());
    }

    public static string RemoveLinq(string input)
    {
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }

    public static string RemoveRegex(string input)
    {
        return Regex.Replace(input, @"\s+", "");
    }

    private static Regex compiled = new Regex(@"\s+", RegexOptions.Compiled);
    public static string RemoveRegexCompiled(string input)
    {
        return compiled.Replace(input, "");
    }

    public static string RemoveForLoop(string input)
    {
        for (int i = input.Length - 1; i >= 0; i--)
        {
            if (char.IsWhiteSpace(input[i]))
            {
                input = input.Remove(i, 1);
            }
        }
        return input;
    }

    public static string StringSplitThenJoin(this string str)
    {
        return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
    }

    public static string RemoveInPlaceCharArray(string input)
    {
        var len = input.Length;
        var src = input.ToCharArray();
        int dstIdx = 0;
        for (int i = 0; i < len; i++)
        {
            var ch = src[i];
            switch (ch)
            {
                case '\u0020':
                case '\u00A0':
                case '\u1680':
                case '\u2000':
                case '\u2001':
                case '\u2002':
                case '\u2003':
                case '\u2004':
                case '\u2005':
                case '\u2006':
                case '\u2007':
                case '\u2008':
                case '\u2009':
                case '\u200A':
                case '\u202F':
                case '\u205F':
                case '\u3000':
                case '\u2028':
                case '\u2029':
                case '\u0009':
                case '\u000A':
                case '\u000B':
                case '\u000C':
                case '\u000D':
                case '\u0085':
                    continue;
                default:
                    src[dstIdx++] = ch;
                    break;
            }
        }
        return new string(src, 0, dstIdx);
    }
}

Tests :

[TestFixture]
public class Test
{
    // Short input
    //private const string input = "123 123 \t 1adc \n 222";
    //private const string expected = "1231231adc222";

    // Long input
    private const string input = "123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222";
    private const string expected = "1231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc222";

    private const int iterations = 1000000;

    [Test]
    public void RemoveInPlaceCharArray()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveInPlaceCharArray(input);
        }

        stopwatch.Stop();
        Console.WriteLine("InPlaceCharArray: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveStringReader()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveStringReader(input);
        }

        stopwatch.Stop();
        Console.WriteLine("String reader: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveLinqNativeCharIsWhitespace()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveLinqNativeCharIsWhitespace(input);
        }

        stopwatch.Stop();
        Console.WriteLine("LINQ using native char.IsWhitespace: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveLinq()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveLinq(input);
        }

        stopwatch.Stop();
        Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveRegex()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveRegex(input);
        }

        stopwatch.Stop();
        Console.WriteLine("Regex: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveRegexCompiled()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveRegexCompiled(input);
        }

        stopwatch.Stop();
        Console.WriteLine("RegexCompiled: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveForLoop()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveForLoop(input);
        }

        stopwatch.Stop();
        Console.WriteLine("ForLoop: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [TestMethod]
    public void StringSplitThenJoin()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.StringSplitThenJoin(input);
        }

        stopwatch.Stop();
        Console.WriteLine("StringSplitThenJoin: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }
}

Edit : testé une belle doublure de Kernowcode.

Stian Standahl
la source
24

Juste une alternative car elle a l'air plutôt sympa :) - NOTE: La réponse de Henks est la plus rapide de celles-ci.

input.ToCharArray()
 .Where(c => !Char.IsWhiteSpace(c))
 .Select(c => c.ToString())
 .Aggregate((a, b) => a + b);

Test de 1 000 000 de boucles sur "This is a simple Test"

Cette méthode = 1,74 secondes
Regex = 2,58 secondes
new String(Henks) = 0,82

BlueChippy
la source
1
Pourquoi cela a-t-il été rejeté? Il est parfaitement acceptable, répond aux exigences, fonctionne plus rapidement que l'option RegEx et est très lisible?
BlueChippy
4
car il peut être écrit beaucoup plus court: nouvelle chaîne (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Bas Smit
7
Cela peut être vrai - mais la réponse est toujours valable, est lisible, plus rapide que l'expression régulière et produit le résultat souhaité. Beaucoup d'autres réponses sont APRÈS celle-ci ... donc un downvote n'a pas de sens.
BlueChippy
2
Existe-t-il une unité pour "0,82"? Ou s'agit-il d'une mesure relative (82%)? Pouvez-vous modifier votre réponse pour la rendre plus claire?
Peter Mortensen
20

J'ai trouvé un bon article à ce sujet sur CodeProject de Felipe Machado (avec l'aide de Richard Robertson )

Il a testé dix méthodes différentes. Celui-ci est la version dangereuse la plus rapide ...

public static unsafe string TrimAllWithStringInplace(string str) {
    fixed (char* pfixed = str) {
        char* dst = pfixed;
        for (char* p = pfixed; *p != 0; p++)

            switch (*p) {

                case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':

                case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':

                case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':

                case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':

                case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
                    continue;

                default:
                    *dst++ = *p;
                    break;
            }

        return new string(pfixed, 0, (int)(dst - pfixed));
    }
}

Et la version sûre la plus rapide ...

public static string TrimAllWithInplaceCharArray(string str) {

    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;

    for (int i = 0; i < len; i++) {
        var ch = src[i];

        switch (ch) {

            case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':

            case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':

            case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':

            case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':

            case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
                continue;

            default:
                src[dstIdx++] = ch;
                break;
        }
    }
    return new string(src, 0, dstIdx);
}

Il existe également de jolis repères indépendants sur Stack Overflow de Stian Standahl qui montrent également que la fonction de Felipe est environ 300% plus rapide que la fonction la plus rapide suivante.

Sunsetquest
la source
J'ai essayé de traduire cela en C ++ mais je suis un peu coincé. Des idées pourquoi mon port pourrait être défaillant? stackoverflow.com/questions/42135922/…
Jon Cage
2
Je ne peux pas résister. Regardez dans la section commentaires de l'article auquel vous faites référence. Vous me trouverez en tant que "logiciel de casier". Il a travaillé dessus pendant un certain temps. J'avais complètement oublié cela lorsque ce problème est revenu. Merci pour de bons souvenirs. :)
Richard Robertson
1
Et si vous souhaitez supprimer uniquement les WS supplémentaires? Qu'en est-il de ce mod stackoverflow.com/questions/17770202/… ?
Tom
La chaîne la plus rapide est un peu plus lente ;-) Les chaînes améliorent les performances des conteneurs ici (dans l'application 4:15 à 3:55 => 8,5% de moins, mais lorsqu'elles sont laissées, la chaîne 3:30 => 21,4% de moins et Profiller affiche environ 50% de dépenses en cette méthode). Ainsi, dans la vraie chaîne en direct, la conversion en tableau (lente) utilisée ici devrait être environ 40% plus rapide.
Tom
15

Si vous avez besoin de performances exceptionnelles, vous devez éviter LINQ et les expressions régulières dans ce cas. J'ai fait un benchmarking des performances, et il semble que si vous voulez supprimer les espaces blancs du début et de la fin de la chaîne, string.Trim () est votre fonction ultime.

Si vous devez supprimer tous les espaces blancs d'une chaîne, la méthode suivante fonctionne le plus rapidement de tout ce qui a été publié ici:

    public static string RemoveWhitespace(this string input)
    {
        int j = 0, inputlen = input.Length;
        char[] newarr = new char[inputlen];

        for (int i = 0; i < inputlen; ++i)
        {
            char tmp = input[i];

            if (!char.IsWhiteSpace(tmp))
            {
                newarr[j] = tmp;
                ++j;
            }
        }
        return new String(newarr, 0, j);
    }
JHM
la source
Je serais curieux de connaître les détails de vos repères - non pas que je sois sceptique, mais je suis curieux de connaître les frais généraux liés à Linq. À quel point était-ce mauvais?
Mark Meuer
Je n'ai pas relancé tous les tests, mais je m'en souviens bien: tout ce qui impliquait Linq était beaucoup plus lent que tout sans lui. Toute l'utilisation intelligente des fonctions de chaîne / char et des constructeurs n'a fait aucune différence en pourcentage si Linq était utilisé.
JHM
11

Regex est exagéré; utilisez simplement l'extension sur la chaîne (merci Henk). C'est trivial et aurait dû faire partie du cadre. Quoi qu'il en soit, voici ma mise en œuvre:

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}
Maksood
la source
c'est fondamentalement une réponse inutile (l'expression régulière est exagérée, mais est une solution plus rapide que celle donnée - et elle est déjà acceptée?)
W1ll1amvl
Comment pouvez-vous utiliser les méthodes d'extension Linq sur une chaîne? Je ne peux pas savoir à quel usage il me manque d'autres queSystem.Linq
GGirard
Ok on dirait que ce n'est pas disponible en PCL, IEnumerable <char> est conditionnel dans l' implémentation de Microsoft String ... Et j'utilise Profile259 qui ne le supporte pas :)
GGirard
4

Voici une alternative linéaire simple à la solution RegEx. Je ne sais pas lequel est le plus rapide; il faudrait le comparer.

static string RemoveWhitespace(string input)
{
    StringBuilder output = new StringBuilder(input.Length);

    for (int index = 0; index < input.Length; index++)
    {
        if (!Char.IsWhiteSpace(input, index))
        {
            output.Append(input[index]);
        }
    }
    return output.ToString();
}
Brandon Moretz
la source
3

J'avais besoin de remplacer les espaces blancs dans une chaîne par des espaces, mais pas des espaces en double. par exemple, j'avais besoin de convertir quelque chose comme ceci:

"a b   c\r\n d\t\t\t e"

à

"a b c d e"

J'ai utilisé la méthode suivante

private static string RemoveWhiteSpace(string value)
{
    if (value == null) { return null; }
    var sb = new StringBuilder();

    var lastCharWs = false;
    foreach (var c in value)
    {
        if (char.IsWhiteSpace(c))
        {
            if (lastCharWs) { continue; }
            sb.Append(' ');
            lastCharWs = true;
        }
        else
        {
            sb.Append(c);
            lastCharWs = false;
        }
    }
    return sb.ToString();
}
user1325543
la source
2

Je suppose que votre réponse XML ressemble à ceci:

var xml = @"<names>
                <name>
                    foo
                </name>
                <name>
                    bar
                </name>
            </names>";

La meilleure façon de traiter XML est d'utiliser un analyseur XML, tel que LINQ to XML :

var doc = XDocument.Parse(xml);

var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");
dtb
la source
Une fois que j'ai vérifié qu'une balise <name> particulière a la valeur appropriée, j'ai terminé. L'analyse du document n'aurait-elle pas un surcoût?
Corey Ogburn
4
Bien sûr, il y a des frais généraux. Mais cela a l'avantage d'être correct. Une solution basée par exemple sur l'expression régulière est beaucoup plus difficile à obtenir correctement. Si vous déterminez qu'une solution LINQ to XML est trop lente, vous pouvez toujours la remplacer par quelque chose de plus rapide. Mais vous devez éviter de rechercher l'implémentation la plus efficace avant de savoir que la bonne est trop lente.
dtb
Cela va être exécuté sur les serveurs principaux de mon employeur. Le poids léger est ce que je recherche. Je ne veux pas quelque chose qui "fonctionne" mais qui soit optimal.
Corey Ogburn
4
LINQ to XML est l'un des moyens les plus légers de travailler correctement avec XML dans .NET
dtb
1

Voici encore une autre variante:

public static string RemoveAllWhitespace(string aString)
{
  return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}

Comme avec la plupart des autres solutions, je n'ai pas effectué de tests de référence exhaustifs, mais cela fonctionne assez bien pour mes besoins.

Fred
la source
1

On peut utiliser:

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }
Tarik BENARAB
la source
C'est presque exactement la même chose que la réponse de Henk ci-dessus. La seule différence est que vous vérifiez null.
Corey Ogburn
Oui, vérifier que null est important
Tarik BENARAB
1
Peut-être que cela aurait dû être juste un commentaire sur sa réponse. Je suis cependant ravi que vous en parliez. Je ne savais pas que les méthodes d'extension pouvaient être appelées sur des objets nuls.
Corey Ogburn
0

J'ai trouvé que différents résultats étaient vrais. J'essaie de remplacer tous les espaces par un seul espace et l'expression régulière était extrêmement lente.

return( Regex::Replace( text, L"\s+", L" " ) );

Ce qui a fonctionné le mieux pour moi (en C ++ cli) était:

String^ ReduceWhitespace( String^ text )
{
  String^ newText;
  bool    inWhitespace = false;
  Int32   posStart = 0;
  Int32   pos      = 0;
  for( pos = 0; pos < text->Length; ++pos )
  {
    wchar_t cc = text[pos];
    if( Char::IsWhiteSpace( cc ) )
    {
      if( !inWhitespace )
      {
        if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
        inWhitespace = true;
        newText += L' ';
      }
      posStart = pos + 1;
    }
    else
    {
      if( inWhitespace )
      {
        inWhitespace = false;
        posStart = pos;
      }
    }
  }

  if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );

  return( newText );
}

J'ai d'abord essayé la routine ci-dessus en remplaçant chaque caractère séparément, mais j'ai dû passer à la création de sous-chaînes pour les sections non spatiales. Lors de l'application à une chaîne de 1 200 000 caractères:

  • la routine ci-dessus le fait en 25 secondes
  • la routine ci-dessus + remplacement de caractère séparé en 95 secondes
  • l'expression régulière a avorté après 15 minutes.
hvanbrug
la source