web-dev-qa-db-fra.com

c # Le moyen le plus rapide de supprimer des espaces blancs supplémentaires

Quel est le moyen le plus rapide de remplacer des espaces blancs supplémentaires par un espace blanc?
par exemple.

de  

foo      bar 

à

foo bar
37
Navid Rahmani

Le moyen le plus rapide? Parcourez la chaîne et créez une seconde copie dans une variable StringBuilder, caractère par caractère, en ne copiant qu'un seul espace pour chaque groupe d'espaces.

Les variantes Replace plus faciles à créer créeront une charge supplémentaire de chaînes (ou une perte de temps lors de la création du DFA regex).

Modifier avec les résultats de la comparaison:

En utilisant http://ideone.com/h6pw3 , avec n = 50 (je devais le réduire sur ideone car cela prenait si longtemps qu’il fallait tuer mon processus), je reçois:

Regex: 7771ms. 

Constructeur de chaînes: 894ms.

Ce qui est en effet comme prévu, Regex est horriblement inefficace pour quelque chose d'aussi simple.

44
Blindy

Vous pouvez utiliser une regex:

static readonly Regex trimmer = new Regex(@"\s\s+");

s = trimmer.Replace(s, " ");

Pour des performances accrues, transmettez RegexOptions.Compiled.

38
SLaks

Un peu en retard, mais j’ai fait quelques analyses comparatives pour trouver le moyen le plus rapide de supprimer les espaces blancs supplémentaires. S'il y a des réponses plus rapides, j'aimerais les ajouter.

Résultats:

  1. NormalizeWhiteSpaceForLoop: 156 ms ( par moi - De ma réponse sur la suppression de tous les espaces blancs )
  2. NormalizeWhiteSpace: 267 ms ( par Alex K. )
  3. RegexCompiled: 1950 ms ( par SLaks )
  4. Regex: 2261 ms ( par SLaks )

Code:

public class RemoveExtraWhitespaces
{
    public static string WithRegex(string text)
    {
        return Regex.Replace(text, @"\s+", " ");
    }

    public static string WithRegexCompiled(Regex compiledRegex, string text)
    {
        return compiledRegex.Replace(text, " ");
    }

    public static string NormalizeWhiteSpace(string input)
    {
        if (string.IsNullOrEmpty(input))
            return string.Empty;

        int current = 0;
        char[] output = new char[input.Length];
        bool skipped = false;

        foreach (char c in input.ToCharArray())
        {
            if (char.IsWhiteSpace(c))
            {
                if (!skipped)
                {
                    if (current > 0)
                        output[current++] = ' ';

                    skipped = true;
                }
            }
            else
            {
                skipped = false;
                output[current++] = c;
            }
        }

        return new string(output, 0, current);
    }

    public static string NormalizeWhiteSpaceForLoop(string input)
    {
        int len = input.Length,
            index = 0,
            i = 0;
        var src = input.ToCharArray();
        bool skip = false;
        char ch;
        for (; i < len; i++)
        {
            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':
                    if (skip) continue;
                    src[index++] = ch;
                    skip = true;
                    continue;
                default:
                    skip = false;
                    src[index++] = ch;
                continue;
            }
        }

        return new string(src, 0, index);
    }
}

Tests:

[TestFixture]
public class RemoveExtraWhitespacesTest
{
    private const string _text = "foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo ";
    private const string _expected = "foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo ";

    private const int _iterations = 10000;

    [Test]
    public void Regex()
    {
        var result = TimeAction("Regex", () => RemoveExtraWhitespaces.WithRegex(_text));
        Assert.AreEqual(_expected, result);
    }

    [Test]
    public void RegexCompiled()
    {
        var compiledRegex = new Regex(@"\s+", RegexOptions.Compiled);
        var result = TimeAction("RegexCompiled", () => RemoveExtraWhitespaces.WithRegexCompiled(compiledRegex, _text));
        Assert.AreEqual(_expected, result);
    }

    [Test]
    public void NormalizeWhiteSpace()
    {
        var result = TimeAction("NormalizeWhiteSpace", () => RemoveExtraWhitespaces.NormalizeWhiteSpace(_text));
        Assert.AreEqual(_expected, result);
    }

    [Test]
    public void NormalizeWhiteSpaceForLoop()
    {
        var result = TimeAction("NormalizeWhiteSpaceForLoop", () => RemoveExtraWhitespaces.NormalizeWhiteSpaceForLoop(_text));
        Assert.AreEqual(_expected, result);
    }

    public string TimeAction(string name, Func<string> func)
    {
        var timer = Stopwatch.StartNew();
        string result = string.Empty; ;
        for (int i = 0; i < _iterations; i++)
        {
            result = func();
        }

        timer.Stop();
        Console.WriteLine(string.Format("{0}: {1} ms", name, timer.ElapsedMilliseconds));
        return result;
    }
}
21
Stian Standahl

J'utilise les méthodes ci-dessous - ils gèrent tous les caractères d'espacement et pas seulement les espaces, découpent les espaces lead et trailing, remove extra les espaces, et tous les espaces sont remplacés par des espaces. car (nous avons donc un séparateur d'espace uniforme). Et ces méthodes sont fast.

public static String CompactWhitespaces( String s )
{
    StringBuilder sb = new StringBuilder( s );

    CompactWhitespaces( sb );

    return sb.ToString();
}

public static void CompactWhitespaces( StringBuilder sb )
{
    if( sb.Length == 0 )
        return;

    // set [start] to first not-whitespace char or to sb.Length

    int start = 0;

    while( start < sb.Length )
    {
        if( Char.IsWhiteSpace( sb[ start ] ) )
            start++;
        else 
            break;
    }

    // if [sb] has only whitespaces, then return empty string

    if( start == sb.Length )
    {
        sb.Length = 0;
        return;
    }

    // set [end] to last not-whitespace char

    int end = sb.Length - 1;

    while( end >= 0 )
    {
        if( Char.IsWhiteSpace( sb[ end ] ) )
            end--;
        else 
            break;
    }

    // compact string

    int dest = 0;
    bool previousIsWhitespace = false;

    for( int i = start; i <= end; i++ )
    {
        if( Char.IsWhiteSpace( sb[ i ] ) )
        {
            if( !previousIsWhitespace )
            {
                previousIsWhitespace = true;
                sb[ dest ] = ' ';
                dest++;
            }
        }
        else
        {
            previousIsWhitespace = false;
            sb[ dest ] = sb[ i ];
            dest++;
        }
    }

    sb.Length = dest;
}
11
Sergey Povalyaev
string text = "foo       bar";
text = Regex.Replace(text, @"\s+", " ");
// text = "foo bar"

Cette solution fonctionne avec des espaces, des onglets et une nouvelle ligne. Si vous voulez juste des espaces, remplacez '\ s' par ''.

6
OpticalDelusion
string q = " Hello     how are   you           doing?";
string a = String.Join(" ", q.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
6
Detlef Kroll

J'avais besoin de l'un d'eux pour les grosses cordes et j'ai proposé la routine ci-dessous.

Tout espace blanc consécutif (y compris les onglets et les nouvelles lignes) est remplacé par tout ce qui est dans normalizeTo. Les espaces blancs de début/fin sont supprimés.

C'est environ 8 fois plus rapide qu'un RegEx avec mes chaînes de caractères de 5k-> 5mil.

internal static string NormalizeWhiteSpace(string input, char normalizeTo = ' ')
{
    if (string.IsNullOrEmpty(input))
        return string.Empty;

    int current = 0;
    char[] output = new char[input.Length];
    bool skipped = false;

    foreach (char c in input.ToCharArray())
    {
        if (char.IsWhiteSpace(c))
        {
            if (!skipped)
            {
                if (current > 0)
                    output[current++] = normalizeTo;

                skipped = true;
            }
        }
        else
        {
            skipped = false;
            output[current++] = c;
        }
    }

    return new string(output, 0, skipped ? current - 1 : current);
}
6
Alex K.
string yourWord = "beep boop    baap beep   boop    baap             beep";

yourWord = yourWord .Replace("  ", " |").Replace("| ", "").Replace("|", "");
5
Abu Dina

J'ai essayé d'utiliser StringBuilder pour:

  1. supprime les sous-chaînes d'espaces
  2. accepte les caractères de la boucle sur la chaîne d'origine, comme le suggère Blindy

Voici le meilleur équilibre entre performances et lisibilité que j'ai trouvé (en utilisant 100 000 exécutions de minutage d'itération). Parfois, cela teste plus rapidement qu'une version moins lisible, au plus 5% plus lente. Sur ma petite chaîne de test, regex prend 4,24 fois plus de temps.

public static string RemoveExtraWhitespace(string str)
    {
        var sb = new StringBuilder();
        var prevIsWhitespace = false;
        foreach (var ch in str)
        {
            var isWhitespace = char.IsWhiteSpace(ch);
            if (prevIsWhitespace && isWhitespace)
            {
                continue;
            }
            sb.Append(ch);
            prevIsWhitespace = isWhitespace;
        }
        return sb.ToString();
    }
4
TTT

Ce n'est pas rapide, mais si la simplicité vous aide, cela fonctionne:

while (text.Contains("  ")) text=text.Replace("  ", " ");
2
user3029478

Ce morceau de code fonctionne bien. Je n'ai pas mesuré la performance.

string text = "   hello    -  world,  here   we go  !!!    a  bc    ";
string.Join(" ", text.Split().Where(x => x != ""));
// Output
// "hello - world, here we go !!! a bc"
1
Christian Torrez

Quelques exigences ne sont pas claires dans cette question qui méritent une réflexion. 

  1. Voulez-vous un seul caractère d’espace blanc en fin ou de fin?
  2. Lorsque vous remplacez tous les espaces blancs par un seul caractère, souhaitez-vous que ce caractère soit cohérent? (c’est-à-dire que beaucoup de ces solutions remplaceraient\t\t par\t et "avec".

C'est une version très efficace qui remplace tous les espaces blancs par un seul espace et supprime tout espace blanc avant et arrière avant la boucle for.

  public static string WhiteSpaceToSingleSpaces(string input)
  {
    if (input.Length < 2) 
        return input;

    StringBuilder sb = new StringBuilder();

    input = input.Trim();
    char lastChar = input[0];
    bool lastCharWhiteSpace = false;

    for (int i = 1; i < input.Length; i++)
    {
        bool whiteSpace = char.IsWhiteSpace(input[i]);

        //Skip duplicate whitespace characters
        if (whiteSpace && lastCharWhiteSpace)
            continue;

        //Replace all whitespace with a single space.
        if (whiteSpace)
            sb.Append(' ');
        else
            sb.Append(input[i]);

        //Keep track of the last character's whitespace status
        lastCharWhiteSpace = whiteSpace;
    }

    return sb.ToString();
  }
1
Jake Drew

Je ne sais pas si c'est le moyen le plus rapide mais j'utilise ceci et cela a fonctionné pour moi:

    /// <summary>
    /// Remove all extra spaces and tabs between words in the specified string!
    /// </summary>
    /// <param name="str">The specified string.</param>
    public static string RemoveExtraSpaces(string str)
    {
        str = str.Trim();
        StringBuilder sb = new StringBuilder();
        bool space = false;
        foreach (char c in str)
        {
            if (char.IsWhiteSpace(c) || c == (char)9) { space = true; }
            else { if (space) { sb.Append(' '); }; sb.Append(c); space = false; };
        }
        return sb.ToString();
    }
1
LL99

essaye ça:

System.Text.RegularExpressions.Regex.Replace(input, @"\s+", " ");
1
jaltiere
public string GetCorrectString(string IncorrectString)
    {
        string[] strarray = IncorrectString.Split(' ');
        var sb = new StringBuilder();
        foreach (var str in strarray)
        {
            if (str != string.Empty)
            {
                sb.Append(str).Append(' ');
            }
        }
        return sb.ToString().Trim();
    }
0
chandudab
public static string RemoveExtraSpaces(string input)
{
    input = input.Trim();
    string output = "";
    bool WasLastCharSpace = false;
    for (int i = 0; i < input.Length; i++)
    {
        if (input[i] == ' ' && WasLastCharSpace)
            continue;
        WasLastCharSpace = input[i] == ' ';
        output += input[i];
    }
    return output;
}
0
Saeed Taran

Pour ceux qui veulent juste copier-coller et continuer:

    private string RemoveExcessiveWhitespace(string value)
    {
        if (value == null) { return null; }

        var builder = new StringBuilder();
        var ignoreWhitespace = false;
        foreach (var c in value)
        {
            if (!ignoreWhitespace || c != ' ')
            {
                builder.Append(c);
            }
            ignoreWhitespace = c == ' ';
        }
        return builder.ToString();
    }
0
Mert Akcakaya

Je sais que cela est vraiment vieux, mais le moyen le plus simple de compacter les espaces (remplacer tout caractère d’espace récurrent par un seul caractère "espace") est le suivant:

    public static string CompactWhitespace(string astring)
    {
        if (!string.IsNullOrEmpty(astring))
        {
            bool found = false;
            StringBuilder buff = new StringBuilder();

            foreach (char chr in astring.Trim())
            {
                if (char.IsWhiteSpace(chr))
                {
                    if (found)
                    {
                        continue;
                    }

                    found = true;
                    buff.Append(' ');
                }
                else
                {
                    if (found)
                    {
                        found = false;
                    }

                    buff.Append(chr);
                }
            }

            return buff.ToString();
        }

        return string.Empty;
    }
0
Brien Halstead

Je ne suis pas très familier avec C #, mon code n’est donc pas élégant/efficace. Je suis venu ici pour trouver une réponse qui corresponde à mon cas d'utilisation, mais je ne pouvais pas en trouver une (ou je ne pouvais pas en trouver une).

Pour mon cas d'utilisation, je devais normaliser tous les espaces blancs (WS: {space, tab, cr lf}) dans les conditions suivantes:

  • WS peut venir dans n'importe quelle combinaison
  • Remplacer une séquence de WS par le plus significatif
  • tab doivent être conservés dans certains cas (un fichier séparé par onglet, par exemple. Dans ce cas, il faut également conserver les onglets répétés). Mais dans la plupart des cas, ils doivent être convertis en espaces.

Voici donc un exemple d'entrée et une sortie attendue (Avertissement: mon code est testé uniquement pour cet exemple)



        Every night    in my            dreams  I see you, I feel you
    That's how    I know you go on

Far across the  distance and            places between us   



You            have                 come                    to show you go on


être converti en

Every night in my dreams I see you, I feel you
That's how I know you go on
Far across the distance and places between us
You have come to show you go on

Voici mon code

using System;
using System.Text.RegularExpressions;

public class Program
{
    public static void Main(string text)
    {
        bool preserveTabs = false;

        //[Step 1]: Clean up white spaces around the text
        text = text.Trim();
        //Console.Write("\nTrim\n======\n" + text);

        //[Step 2]: Reduce repeated spaces to single space. 
        text = Regex.Replace(text, @" +", " ");
        // Console.Write("\nNo repeated spaces\n======\n" + text);

        //[Step 3]: Hande Tab spaces. Tabs needs to treated with care because 
        //in some files tabs have special meaning (for eg Tab seperated files)
        if(preserveTabs)
        {
            text = Regex.Replace(text, @" *\t *", "\t");
        }
        else
        {
            text = Regex.Replace(text, @"[ \t]+", " ");
        }
        //Console.Write("\nTabs preserved\n======\n" + text);

        //[Step 4]: Reduce repeated new lines (and other white spaces around them)
                  //into a single new line.
        text = Regex.Replace(text, @"([\t ]*(\n)+[\t ]*)+", "\n");
        Console.Write("\nClean New Lines\n======\n" + text);    
    }
}

Voir ce code en action ici: https://dotnetfiddle.net/eupjIU

0
BeNiza

Est-ce que j'ai râté quelque chose? Je suis venu avec ceci:

// Input: "HELLO     BEAUTIFUL       WORLD!"
private string NormalizeWhitespace(string inputStr)
{
    // First split the string on the spaces but exclude the spaces themselves
    // Using the input string the length of the array will be 3. If the spaces
    // were not filtered out they would be included in the array
    var splitParts = inputStr.Split(' ').Where(x => x != "").ToArray();

   // Now iterate over the parts in the array and add them to the return
   // string. If the current part is not the last part, add a space after.
   for (int i = 0; i < splitParts.Count(); i++)
   {
        retVal += splitParts[i];
        if (i != splitParts.Count() - 1)
        {
            retVal += " ";
        }
   }
    return retVal;
}
// Would return "HELLO BEAUTIFUL WORLD!"

Je sais que je crée une deuxième chaîne ici pour la renvoyer, ainsi que la création du tableau splitParts. Je me suis dit que c'était assez simple. Peut-être que je ne prends pas en compte certains des scénarios potentiels. 

0
Tom

vous pouvez utiliser indexOf pour commencer par saisir le début des séquences d'espaces, puis utiliser la méthode replace pour changer l'espace blanc en "". À partir de là, vous pouvez utiliser l'index que vous avez saisi et placer un caractère d'espacement à cet endroit.

0
MGZero

C’est drôle, mais sur mon PC, la méthode ci-dessous est aussi rapide que l’approche StringBulder de Sergey Povalyaev - (~ 282ms pour 1 000 répétitions, chaînes de 10 000 caractères src). Pas sûr de l'utilisation de la mémoire si.

string RemoveExtraWhiteSpace(string src, char[] wsChars){
   return string.Join(" ",src.Split(wsChars, StringSplitOptions.RemoveEmptyEntries));
}

Évidemment, cela fonctionne avec tous les caractères - pas seulement les espaces.

Bien que ce ne soit pas ce que l'OP a demandé, mais si vous avez vraiment besoin de remplacer des caractères consécutifs spécifiques dans une chaîne par une seule instance, vous pouvez utiliser cette méthode relativement efficace:

    string RemoveDuplicateChars(string src, char[] dupes){  
        var sd = (char[])dupes.Clone();  
        Array.Sort(sd);

        var res = new StringBuilder(src.Length);

        for(int i = 0; i<src.Length; i++){
            if( i==0 || src[i]!=src[i-1] || Array.BinarySearch(sd,src[i])<0){
                res.Append(src[i]); 
            }
        }
        return res.ToString();
    }
0
Zar Shardan

Je viens juste de préparer ça, je ne l'ai pas encore testé. Mais je sentais que c'était élégant, et évite les regex: 

    /// <summary>
    /// Removes extra white space.
    /// </summary>
    /// <param name="s">
    /// The string
    /// </param>
    /// <returns>
    /// The string, with only single white-space groupings. 
    /// </returns>
    public static string RemoveExtraWhiteSpace(this string s)
    {
        if (s.Length == 0)
        {
            return string.Empty;
        }

        var stringBuilder = new StringBuilder();
        var whiteSpaceCount = 0;
        foreach (var character in s)
        {
            if (char.IsWhiteSpace(character))
            {
                whiteSpaceCount++;
            }
            else
            {
                whiteSpaceCount = 0;
            }

            if (whiteSpaceCount > 1)
            {
                continue;
            }

            stringBuilder.Append(character);
        }

        return stringBuilder.ToString();
    }
0
Kris Coleman