Como substituir vários espaços em branco por um espaço em branco

108

Digamos que eu tenha uma string como:

"Hello     how are   you           doing?"

Eu gostaria de uma função que transforma vários espaços em um espaço.

Então, eu obteria:

"Hello how are you doing?"

Eu sei que poderia usar regex ou chamar

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

Mas eu teria que chamá-lo várias vezes para garantir que todos os espaços em branco sequenciais sejam substituídos por apenas um.

Já existe um método integrado para isso?

Matt
fonte
Você poderia esclarecer: você está lidando apenas com espaços, ou "todos" os espaços em branco?
Jon Skeet de
E você quer que qualquer espaço em branco não espacial seja convertido em espaços?
Jon Skeet de
Eu apenas quis dizer que todos os espaços em branco na série devem ser no máximo 1
Matt
1
Possível duplicata de stackoverflow.com/questions/206717/…
Michael Freidgeim
2 coisas a considerar: 1. char.IsWhiteSpace inclui retorno de carro, alimentação de linha etc. 2. 'espaço em branco' é provavelmente testado com mais precisão com Char.GetUnicodeCategory (ch) = Globalization.UnicodeCategory.SpaceSeparator
smirkingman

Respostas:

196
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");
Tim Hoolihan
fonte
40
imo, evitar regex se você se sentir confortável com elas é otimização prematura
Tim Hoolihan
8
Se seu aplicativo não tiver tempo crítico, ele pode suportar 1 microssegundo de sobrecarga de processamento.
Daniel de
16
Observe que '\ s' não apenas substitui espaços em branco, mas também novos caracteres de linha.
Bart Kiers
12
boa pegadinha, se você quer apenas espaços, mude o padrão para "[] +"
Tim Hoolihan
9
Você não deveria usar '{2,}' em vez de '+' para evitar a substituição de espaços em branco?
angularsen
52

Esta pergunta não é tão simples quanto outros pôsteres fizeram parecer (e como eu originalmente acreditava que fosse) - porque a pergunta não é tão precisa quanto deveria ser.

Há uma diferença entre "espaço" e "espaço em branco". Se você quer dizer apenas espaços, deve usar um regex de " {2,}". Se você quer dizer qualquer espaço em branco, isso é diferente. Todos os espaços em branco devem ser convertidos em espaços? O que deve acontecer com o espaço no início e no fim?

Para o benchmark abaixo, presumi que você só se preocupa com espaços e não quer fazer nada com espaços individuais, mesmo no início e no final.

Observe que a correção quase sempre é mais importante do que o desempenho. O fato de que a solução Dividir / Juntar remove qualquer espaço em branco à esquerda / à direita (mesmo apenas espaços simples) é incorreto no que diz respeito aos seus requisitos especificados (que podem estar incompletos, é claro).

O benchmark usa 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);
    }
}

Alguns testes:

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

Aqui, o primeiro número é o número de iterações, o segundo é o tempo gasto e o terceiro é uma pontuação em escala com 1.0 sendo o melhor.

Isso mostra que em pelo menos alguns casos (incluindo este) uma expressão regular pode superar a solução Dividir / Juntar, às vezes por uma margem muito significativa.

No entanto, se você mudar para um "todos os espaços" exigência, em seguida, do Grupo / Junte- se parecem ganhar. Como tantas vezes acontece, o diabo está nos detalhes ...

Jon Skeet
fonte
1
Ótima análise. Portanto, parece que ambos estávamos corretos em vários graus. O código em minha resposta foi retirado de uma função maior que tem a capacidade de normalizar todos os espaços em branco e / ou caracteres de controle de dentro de uma string e do início e do fim.
Scott Dorman
1
Com apenas os caracteres de espaço em branco que você especificou, na maioria dos meus testes a regex e Split / Join foram quase iguais - S / J teve um benefício minúsculo, ao custo de correção e complexidade. Por essas razões, normalmente prefiro o regex. Não me interpretem mal - estou longe de ser um fanboy de regex, mas não gosto de escrever códigos mais complexos por uma questão de desempenho sem realmente testar o desempenho primeiro.
Jon Skeet de
NormalizeWithSplitAndJoin criará muito mais lixo, é difícil dizer se um problema real será atingido por mais tempo de GC do que o banchmark.
Ian Ringrose
@IanRingrose Que tipo de lixo pode ser criado?
Dronz de
18

Um expressoin regular seria a maneira mais fácil. Se você escrever o regex da maneira correta, não precisará de várias chamadas.

Altere para este:

string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " "); 
Brandon
fonte
Meu único problema @"\s{2,}"é que ele não consegue substituir guias simples e outros caracteres de espaço Unicode por um espaço. Se você for substituir 2 guias por um espaço, provavelmente deverá substituir 1 guia por um espaço. @"\s+"vai fazer isso por você.
David Specht
17

Embora as respostas existentes sejam boas, gostaria de apontar uma abordagem que não funciona:

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

Isso pode ficar em loop para sempre. Alguém se importa em adivinhar por quê? (Eu só descobri isso quando foi perguntado como uma pergunta de newsgroup alguns anos atrás ... alguém realmente encontrou isso como um problema.)

Jon Skeet
fonte
Acho que me lembro dessa pergunta há algum tempo atrás no SO. IndexOf ignora certos caracteres que Replace não ignora. Portanto, o espaço duplo sempre esteve lá, apenas nunca removido.
Brandon de
19
É porque IndexOf ignora alguns caracteres Unicode, o culpado específico neste caso é algum caractere asiático iirc. Hmm, não aderente de largura zero de acordo com o Google.
ahawker de
Aprendi isso da maneira mais difícil :( stackoverflow.com/questions/9260693/…
Antonio Bakula
Eu aprendi da maneira mais difícil. Especialmente com dois não unificadores de largura zero (\ u200C \ u200C). IndexOf retorna o índice deste "espaço duplo", mas Replace não o substitui. Acho que é porque para IndexOf, você precisa especificar StringComparsion (Ordinal) para se comportar da mesma forma que Replace. Dessa forma, nenhum dos dois localizará "espaços duplos". Mais sobre StringComparsion docs.microsoft.com/en-us/dotnet/api/…
Martin Brabec
4

Como já apontado, isso é feito facilmente por uma expressão regular. Vou apenas acrescentar que você pode querer adicionar um .trim () para se livrar dos espaços em branco à esquerda / à direita.

MAK
fonte
4

Aqui está a solução com a qual trabalho. Sem RegEx e String.Split.

public static string TrimWhiteSpace(this string Value)
{
    StringBuilder sbOut = new StringBuilder();
    if (!string.IsNullOrEmpty(Value))
    {
        bool IsWhiteSpace = false;
        for (int i = 0; i < Value.Length; i++)
        {
            if (char.IsWhiteSpace(Value[i])) //Comparion with WhiteSpace
            {
                if (!IsWhiteSpace) //Comparison with previous Char
                {
                    sbOut.Append(Value[i]);
                    IsWhiteSpace = true;
                }
            }
            else
            {
                IsWhiteSpace = false;
                sbOut.Append(Value[i]);
            }
        }
    }
    return sbOut.ToString();
}

então você pode:

string cleanedString = dirtyString.TrimWhiteSpace();
fubo
fonte
4

Um removedor de espaço em branco extra rápido ... Este é o mais rápido e é baseado na cópia no local de Felipe Machado.

static string InPlaceCharArray(string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false;
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        if (src[i] == '\u0020')
        {
            if (lastWasWS == false)
            {
                src[dstIdx++] = ch;
                lastWasWS = true;
            }
        }
        else
        { 
            lastWasWS = false;
            src[dstIdx++] = ch;
        }
    }
    return new string(src, 0, dstIdx);
}

Os benchmarks ...

InPlaceCharArraySpaceOnly por Felipe Machado no CodeProject 2015 e modificado por Sunsetquest para remoção de vários espaços. Tempo: 3,75 carrapatos

InPlaceCharArray de Felipe Machado 2015 e ligeiramente modificado por Sunsetquest para remoção de vários espaços. Time 6,50 Ticks (também suporta guias)

SplitAndJoinOnSpace de Jon Skeet . Tempo: 13,25 Carrapatos

StringBuilder por fubo Tempo: 13,5 Ticks (também oferece suporte a guias)

Regex com compilação de Jon Skeet . Tempo: 17 carrapatos

StringBuilder por David S 2013 Hora: 30,5 carrapatos

Regex com não compilação por Brandon Time: 63,25 Ticks

StringBuilder pelo usuário214147 Tempo: 77,125 carrapatos

Regex com Tim Hoolihan não compilado Tempo: 147,25 Carrapatos

O código de referência ...

using System;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
using System.Text;

static class Program
{
    public static void Main(string[] args)
    {
    long seed = ConfigProgramForBenchmarking();

    Stopwatch sw = new Stopwatch();

    string warmup = "This is   a Warm  up function for best   benchmark results." + seed;
    string input1 = "Hello World,    how are   you           doing?" + seed;
    string input2 = "It\twas\t \tso    nice  to\t\t see you \tin 1950.  \t" + seed;
    string correctOutput1 = "Hello World, how are you doing?" + seed;
    string correctOutput2 = "It\twas\tso nice to\tsee you in 1950. " + seed;
    string output1,output2;

    //warm-up timer function
    sw.Restart();
    sw.Stop();

    sw.Restart();
    sw.Stop();
    long baseVal = sw.ElapsedTicks;

    // InPlace Replace by Felipe Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
    output1 = InPlaceCharArraySpaceOnly (warmup);
    sw.Restart();
    output1 = InPlaceCharArraySpaceOnly (input1);
    output2 = InPlaceCharArraySpaceOnly (input2);
    sw.Stop();
    Console.WriteLine("InPlaceCharArraySpaceOnly : " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    // InPlace Replace by Felipe R. Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
    output1 = InPlaceCharArray(warmup);
    sw.Restart();
    output1 = InPlaceCharArray(input1);
    output2 = InPlaceCharArray(input2);
    sw.Stop();
    Console.WriteLine("InPlaceCharArray: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex with non-compile Tim Hoolihan (https://stackoverflow.com/a/1279874/2352507)
    string cleanedString = 
    output1 = Regex.Replace(warmup, @"\s+", " ");
    sw.Restart();
    output1 = Regex.Replace(input1, @"\s+", " ");
    output2 = Regex.Replace(input2, @"\s+", " ");
    sw.Stop();
    Console.WriteLine("Regex by Tim Hoolihan: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex with compile by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
    output1 = MultipleSpaces.Replace(warmup, " ");
    sw.Restart();
    output1 = MultipleSpaces.Replace(input1, " ");
    output2 = MultipleSpaces.Replace(input2, " ");
    sw.Stop();
    Console.WriteLine("Regex with compile by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
    output1 = SplitAndJoinOnSpace(warmup);
    sw.Restart();
    output1 = SplitAndJoinOnSpace(input1);
    output2 = SplitAndJoinOnSpace(input2);
    sw.Stop();
    Console.WriteLine("Split And Join by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex by Brandon (https://stackoverflow.com/a/1279878/2352507
    output1 = Regex.Replace(warmup, @"\s{2,}", " ");
    sw.Restart();
    output1 = Regex.Replace(input1, @"\s{2,}", " ");
    output2 = Regex.Replace(input2, @"\s{2,}", " ");
    sw.Stop();
    Console.WriteLine("Regex by Brandon: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
    output1 = user214147(warmup);
    sw.Restart();
    output1 = user214147(input1);
    output2 = user214147(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder by user214147: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
    output1 = fubo(warmup);
    sw.Restart();
    output1 = fubo(input1);
    output2 = fubo(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder by fubo: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));


    //StringBuilder by David S 2013 (https://stackoverflow.com/a/16035044/2352507)
    output1 = SingleSpacedTrim(warmup);
    sw.Restart();
    output1 = SingleSpacedTrim(input1);
    output2 = SingleSpacedTrim(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder(SingleSpacedTrim) by David S: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
}

// InPlace Replace by Felipe Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArray(string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false;
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        if (src[i] == '\u0020')
        {
            if (lastWasWS == false)
            {
                src[dstIdx++] = ch;
                lastWasWS = true;
            }
        }
        else
        { 
            lastWasWS = false;
            src[dstIdx++] = ch;
        }
    }
    return new string(src, 0, dstIdx);
}

// InPlace Replace by Felipe R. Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArraySpaceOnly (string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false; //Added line
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        switch (ch)
        {
            case '\u0020': //SPACE
            case '\u00A0': //NO-BREAK SPACE
            case '\u1680': //OGHAM SPACE MARK
            case '\u2000': // EN QUAD
            case '\u2001': //EM QUAD
            case '\u2002': //EN SPACE
            case '\u2003': //EM SPACE
            case '\u2004': //THREE-PER-EM SPACE
            case '\u2005': //FOUR-PER-EM SPACE
            case '\u2006': //SIX-PER-EM SPACE
            case '\u2007': //FIGURE SPACE
            case '\u2008': //PUNCTUATION SPACE
            case '\u2009': //THIN SPACE
            case '\u200A': //HAIR SPACE
            case '\u202F': //NARROW NO-BREAK SPACE
            case '\u205F': //MEDIUM MATHEMATICAL SPACE
            case '\u3000': //IDEOGRAPHIC SPACE
            case '\u2028': //LINE SEPARATOR
            case '\u2029': //PARAGRAPH SEPARATOR
            case '\u0009': //[ASCII Tab]
            case '\u000A': //[ASCII Line Feed]
            case '\u000B': //[ASCII Vertical Tab]
            case '\u000C': //[ASCII Form Feed]
            case '\u000D': //[ASCII Carriage Return]
            case '\u0085': //NEXT LINE
                if (lastWasWS == false) //Added line
                {
                    src[dstIdx++] = ch; //Added line
                    lastWasWS = true; //Added line
                }
            continue;
            default:
                lastWasWS = false; //Added line 
                src[dstIdx++] = ch;
                break;
        }
    }
    return new string(src, 0, dstIdx);
}

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

//Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
static string SplitAndJoinOnSpace(string input)
{
    string[] split = input.Split(new char[] { ' '}, StringSplitOptions.RemoveEmptyEntries);
    return string.Join(" ", split);
}

//StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
public static string user214147(string S)
{
    string s = S.Trim();
    bool iswhite = false;
    int iwhite;
    int sLength = s.Length;
    StringBuilder sb = new StringBuilder(sLength);
    foreach (char c in s.ToCharArray())
    {
        if (Char.IsWhiteSpace(c))
        {
            if (iswhite)
            {
                //Continuing whitespace ignore it.
                continue;
            }
            else
            {
                //New WhiteSpace

                //Replace whitespace with a single space.
                sb.Append(" ");
                //Set iswhite to True and any following whitespace will be ignored
                iswhite = true;
            }
        }
        else
        {
            sb.Append(c.ToString());
            //reset iswhitespace to false
            iswhite = false;
        }
    }
    return sb.ToString();
}

//StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
public static string fubo(this string Value)
{
    StringBuilder sbOut = new StringBuilder();
    if (!string.IsNullOrEmpty(Value))
    {
        bool IsWhiteSpace = false;
        for (int i = 0; i < Value.Length; i++)
        {
            if (char.IsWhiteSpace(Value[i])) //Comparison with WhiteSpace
            {
                if (!IsWhiteSpace) //Comparison with previous Char
                {
                    sbOut.Append(Value[i]);
                    IsWhiteSpace = true;
                }
            }
            else
            {
                IsWhiteSpace = false;
                sbOut.Append(Value[i]);
            }
        }
    }
    return sbOut.ToString();
}

//David S. 2013 (https://stackoverflow.com/a/16035044/2352507)
public static String SingleSpacedTrim(String inString)
{
    StringBuilder sb = new StringBuilder();
    Boolean inBlanks = false;
    foreach (Char c in inString)
    {
        switch (c)
        {
            case '\r':
            case '\n':
            case '\t':
            case ' ':
                if (!inBlanks)
                {
                    inBlanks = true;
                    sb.Append(' ');
                }
                continue;
            default:
                inBlanks = false;
                sb.Append(c);
                break;
        }
    }
    return sb.ToString().Trim();
}

/// <summary>
/// We want to run this item with max priory to lower the odds of
/// the OS from doing program context switches in the middle of our code. 
/// source:https://stackoverflow.com/a/16157458 
/// </summary>
/// <returns>random seed</returns>
private static long ConfigProgramForBenchmarking()
{
    //prevent the JIT Compiler from optimizing Fkt calls away
    long seed = Environment.TickCount;
    //use the second Core/Processor for the test
    Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
    //prevent "Normal" Processes from interrupting Threads
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
    //prevent "Normal" Threads from interrupting this thread
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
    return seed;
}

}

Notas de benchmark: modo de lançamento, sem depurador conectado, processador i7, média de 4 execuções, apenas sequências curtas testadas

Sunsetquest
fonte
1
Bom ver meu artigo referenciado aqui! (Sou Felipe Machado) Estou prestes a atualizá-lo usando uma ferramenta de benchmark adequada chamada BenchmarkDotNet! Vou tentar configurar execuções em todos os tempos de execução (agora que temos DOT NET CORE e similares ...
Loudenvier
1
@Loudenvier - Bom trabalho nisso. O seu foi o mais rápido em quase 400%! .Net Core é como um aumento de desempenho de 150-200% gratuito. Está se aproximando do desempenho do c ++, mas muito mais fácil de codificar. Obrigado pelo comentário.
Sunsetquest
Isso faz apenas espaços, não outros caracteres de espaço em branco. Talvez você queira char.IsWhiteSpace (ch) em vez de src [i] == '\ u0020'. Percebi que isso foi editado pela comunidade. Eles arrancaram tudo?
Evil Pigeon
3

Estou compartilhando o que uso, porque parece que descobri algo diferente. Estou usando isso há um tempo e é rápido o suficiente para mim. Não tenho certeza de como ele se compara aos outros. Eu o uso em um gravador de arquivo delimitado e executo grandes tabelas de dados, um campo de cada vez.

    public static string NormalizeWhiteSpace(string S)
    {
        string s = S.Trim();
        bool iswhite = false;
        int iwhite;
        int sLength = s.Length;
        StringBuilder sb = new StringBuilder(sLength);
        foreach(char c in s.ToCharArray())
        {
            if(Char.IsWhiteSpace(c))
            {
                if (iswhite)
                {
                    //Continuing whitespace ignore it.
                    continue;
                }
                else
                {
                    //New WhiteSpace

                    //Replace whitespace with a single space.
                    sb.Append(" ");
                    //Set iswhite to True and any following whitespace will be ignored
                    iswhite = true;
                }  
            }
            else
            {
                sb.Append(c.ToString());
                //reset iswhitespace to false
                iswhite = false;
            }
        }
        return sb.ToString();
    }
user214147
fonte
2

Usando o programa de teste que Jon Skeet postou, tentei ver se conseguia fazer um loop escrito à mão rodar mais rápido.
Posso vencer o NormalizeWithSplitAndJoin todas as vezes, mas apenas vencer o NormalizeWithRegex com entradas de 1000, 5.

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

    char lastChar = '*';  // anything other then space 
    for (int i = 0; i < input.Length; i++)
    {
        char thisChar = input[i];
        if (!(lastChar == ' ' && thisChar == ' '))
            output.Append(thisChar);

        lastChar = thisChar;
    }

    return output.ToString();
}

Eu não olhei para o código de máquina que o jitter produz, no entanto, espero que o problema seja o tempo gasto pela chamada para StringBuilder.Append () e para fazer muito melhor seria necessário o uso de código não seguro.

Então Regex.Replace () é muito rápido e difícil de bater !!

Ian Ringrose
fonte
2

VB.NET

Linha.Split(" ").ToList().Where(Function(x) x <> " ").ToArray

C #

Linha.Split(" ").ToList().Where(x => x != " ").ToArray();

Aproveite o poder do LINQ = D

Patryk Moura
fonte
Exatamente! Para mim, essa também é a abordagem mais elegante. Então, para registro, em C # isso seria:string.Join(" ", myString.Split(' ').Where(s => s != " ").ToArray())
Efrain
1
Melhoria menor no Splitpara capturar todos os espaços em branco e remover a Wherecláusula:myString.Split(null as char[], StringSplitOptions.RemoveEmptyEntries)
David
1
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");
Michael D.
fonte
Isso substitui todos os caracteres não-word por espaço. Portanto, também substituiria coisas como colchetes e aspas, etc., que podem não ser o que você deseja.
Herman
0

A menor solução:

var regExp = / \ s + / g, newString = oldString.replace (regExp, '');


fonte
0

Você pode tentar isto:

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

Os grupos de substituição fornecem uma abordagem mais simples para resolver a substituição de vários caracteres de espaço em branco pelo mesmo :

    public static void WhiteSpaceReduce()
    {
        string t1 = "a b   c d";
        string t2 = "a b\n\nc\nd";

        Regex whiteReduce = new Regex(@"(?<firstWS>\s)(?<repeatedWS>\k<firstWS>+)");
        Console.WriteLine("{0}", t1);
        //Console.WriteLine("{0}", whiteReduce.Replace(t1, x => x.Value.Substring(0, 1))); 
        Console.WriteLine("{0}", whiteReduce.Replace(t1, @"${firstWS}"));
        Console.WriteLine("\nNext example ---------");
        Console.WriteLine("{0}", t2);
        Console.WriteLine("{0}", whiteReduce.Replace(t2, @"${firstWS}"));
        Console.WriteLine();
    }

Observe que o segundo exemplo permanece único, \nenquanto a resposta aceita substituiria o fim da linha por um espaço.

Se você precisar substituir qualquer combinação de caracteres de espaço em branco pelo primeiro, apenas remova a referência anterior \kdo padrão.

Dan
fonte
0

Usar a expressão regular, para substituir 2 ou mais espaços em branco por um único espaço, também é uma boa solução.

Estamos usando o padrão regex como “ \ s + ”.

  • \ s corresponde a um espaço, guia, nova linha, retorno de carro, avanço de formulário ou guia vertical.

  • '+' diz uma ou mais ocorrências.

Exemplo Regex

String blogName = "  Sourav .  Pal.   "

 String nameWithProperSpacing = blogName.replaceAll("\\s+", " ");   
System.out.println( nameWithProperSpacing );
amigo sourav
fonte
-1

Não há nenhuma maneira embutida de fazer isso. Você pode tentar isto:

private static readonly char[] whitespace = new char[] { ' ', '\n', '\t', '\r', '\f', '\v' };
public static string Normalize(string source)
{
   return String.Join(" ", source.Split(whitespace, StringSplitOptions.RemoveEmptyEntries));
}

Isso removerá espaços em branco à esquerda e à direita, bem como reduzirá qualquer espaço em branco interno a um único caractere de espaço em branco. Se você realmente deseja apenas reduzir os espaços, as soluções que usam uma expressão regular são melhores; caso contrário, esta solução é melhor. (Veja a análise feita por Jon Skeet.)

Scott Dorman
fonte
7
Se a expressão regular for compilada e armazenada em cache, não tenho certeza se isso tem mais sobrecarga do que divisão e junção, o que poderia criar muitas sequências de lixo intermediárias. Você fez benchmarks cuidadosos de ambas as abordagens antes de presumir que seu caminho é mais rápido?
Jon Skeet de
1
espaço em branco não é declarado aqui
Tim Hoolihan
3
Falando em overhead, por que diabos você está ligando source.ToCharArray()e jogando fora o resultado?
Jon Skeet de
2
E invocando ToCharArray()o resultado do string.Join, apenas para criar um novo string ... uau, para isso estar em um post reclamando de overhead é simplesmente notável. -1.
Jon Skeet de
1
Ah, e supondo que whitespaceseja new char[] { ' ' }, isso dará o resultado errado se a string de entrada começar ou terminar com um espaço.
Jon Skeet de