Як замінити кілька пробілів одним пробілом


108

Скажімо, у мене є такий рядок, як:

"Hello     how are   you           doing?"

Я хотів би функцію, яка перетворює кілька просторів в один простір.

Так я отримав би:

"Hello how are you doing?"

Я знаю, що міг би скористатися регулярним виразом або подзвонити

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

Але мені доведеться викликати це кілька разів, щоб переконатися, що всі послідовні пробіли замінені лише одним.

Чи є вже вбудований метод для цього?


Чи можете ви уточнити: чи маєте ви справу лише з пробілами, або "всі" пробілами?
Джон Скіт

І ви хочете, щоб будь-який простір пробілів перетворився на пробіли?
Джон Скіт

Я просто мав на увазі, що весь пробіл у серії повинен бути максимум 1
Метт

1
Можливий дублікат stackoverflow.com/questions/206717 / ...
Michael Freidgeim

2 речі , щоб розглянути наступні питання: 1. char.IsWhiteSpace включає в себе повернення каретки, переведення рядка і т.д. 2. «пробіл», ймовірно , більш точно протестовані з Char.GetUnicodeCategory (CH) = Globalization.UnicodeCategory.SpaceSeparator
smirkingman

Відповіді:


196
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");

40
imo, уникаючи регулярного вираження, якщо вам комфортно з ними - передчасна оптимізація
Тім Хуліхан,

8
Якщо ваша програма не є критичною для часу, вона може дозволити собі 1 мікросекунд обробних витрат.
Даніель

16
Зауважте, що "\ s" замінює не лише пробіли, а й нові символи рядка.
Барт Кіерс

12
хороший улов, якщо ви просто хочете, щоб пробіли переключили шаблон на "[] +"
Тім Хуліхан

9
Чи не слід використовувати "{2,}" замість "+", щоб уникнути заміни одиничних пробілів?
angularsen

52

Це питання не таке просте, як це показали інші афіші (і як я спочатку вважав, що це так) - тому що питання не зовсім точне, як воно має бути.

Існує різниця між "простором" і "пробілом". Якщо ви маєте на увазі лише пробіли, то вам слід скористатись регулярним виразом " {2,}". Якщо ви маєте на увазі будь-який пробіл, це вже інша справа. Чи слід все пробіли перетворити на пробіли? Що має статися з космосом на початку та в кінці?

Щодо еталону нижче, я припускав, що ви дбаєте лише про пробіли, і ви не хочете нічого робити на окремих просторах, навіть на початку та в кінці.

Зауважте, що правильність майже завжди важливіша, ніж продуктивність. Той факт, що рішення Split / Join видаляє будь-яке провідне проміжне пробіл (навіть лише окремі пробіли), є невірним, що стосується вказаних вами вимог (які, звичайно, можуть бути неповними).

Тест використовує 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);
    }
}

Кілька проб:

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

Тут перше число - це кількість повторень, друге - час, а третє - масштабний бал, 1,0 - найкращий.

Це показує, що принаймні в деяких випадках (включаючи цей) регулярний вираз може перевершити рішення Split / Join, іноді за дуже значним відривом.

Однак якщо ви перейдете на вимогу "все пробіли", то розділитись / приєднатися, як видається, виграє. Як це часто буває, чорт у деталях ...


1
Чудовий аналіз. Отже, виявляється, що ми обидва були в тій чи іншій мірі правильними. Код у моїй відповіді взято з більшої функції, яка має можливість нормалізувати всі пробіли та / або символи управління зсередини рядка та з початку та в кінці.
Скотт Дорман

1
Щойно вказані вами символи пробілу, у більшості моїх тестів регулярний вираз та розділення / приєднання були приблизно однаковими - S / J мав крихітну, крихітну перевагу, ціною правильності та складності. З цих причин я зазвичай вважаю за краще регулярний вираз. Не зрозумійте мене неправильно - я далекий від фан-регексу, але мені не подобається писати складніші коди заради продуктивності, не по-справжньому спочатку перевіряючи продуктивність.
Джон Скіт

NormalizeWithSplitAndJoin створить набагато більше сміття, важко сказати, чи справжня проблема отримає більше часу в GC, а потім у відмінні знаки.
Ян Рінроуз

@IanRingrose Які сорти сміття можна створити?
Дронз

18

Звичайний експресоїн був би найпростішим способом. Якщо ви правильно пишете регулярний вираз, вам не потрібно буде кілька дзвінків.

Змініть це на це:

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

Моє одне питання @"\s{2,}"полягає в тому, що він не може замінити окремі вкладки та інші символи пробілу Unicode пробілом. Якщо ви збираєтеся замінити 2 вкладки пробілом, вам, ймовірно, слід замінити 1 вкладку пробілом. @"\s+"зробить це за вас.
Девід Спехт

17

Хоча існуючі відповіді чудові, я хотів би зазначити один підхід, який не працює:

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

Це може циклічно назавжди. Комусь хочеться здогадатися, чому? (Я натрапив на це лише тоді, коли його задавали як питання групи новин кілька років тому ... хтось насправді зіткнувся з цим як проблема.)


Я думаю, що я пам’ятаю, що це питання я задавали деякий час назад. IndexOf ігнорує певні символи, які не замінює. Тож подвійний простір завжди був там, просто ніколи не знімався.
Брендон

19
Це тому, що IndexOf ігнорує деякі символи Unicode, конкретним винуватцем у цьому випадку є деякий азіатський символ iirc. Хм, нульова ширина, не приєднується, згідно Google.
ахаукер

Я дізнався, що важкий шлях :( stackoverflow.com/questions/9260693/…
Антоніо Бакула

Я навчився важкого шляху. Особливо з двома нульовими шинами без ширини (\ u200C \ u200C). IndexOf повертає індекс цього "подвійного простору", але замінити його не замінює. Я думаю, що це тому, що для IndexOf потрібно вказати StringComparsion (Ordinal), щоб він був таким же, як і Замінити. Таким чином, жоден з цих двох не знайде "подвійних пробілів". Більше про StringComparsion docs.microsoft.com/en-us/dotnet/api/…
Мартін

4

Як уже зазначалося, це легко зробити звичайним виразом. Я просто додам, що ви можете додати до цього .trim (), щоб позбутися провідної / задніх пробілів.


4

Ось рішення, з яким я працюю. Без RegEx і 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();
}

тож ви можете:

string cleanedString = dirtyString.TrimWhiteSpace();

4

Швидкий додатковий засіб для усунення простору ... Це найшвидший і заснований на місці копії Феліпе Мачадо.

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

Тести ...

InPlaceCharArraySpaceOnly Феліпе Мачадо на CodeProject 2015 та модифікований Sunsetquest для видалення кількох просторів. Час: 3,75 Кліщів

InPlaceCharArray від Felipe Machado 2015 та трохи змінений Sunsetquest для видалення з кількох просторів. Час 6.50 Кліщі (також підтримує вкладки)

SplitAndJoinOnSpace від Джона Скіта . Час: 13,25 тиків

StringBuilder від fubo Час: 13.5 Тик (також підтримує вкладки)

Regex з компілятором Джон Скіт . Час: 17 тиків

StringBuilder від David S 2013 Час: 30,5 тиків

Реджекс з некомпіляцією за часом Брендона : 63,25 тиків

StringBuilder по user214147 Час: 77,125 Кліщів

Regex з не-компіляції Tim Hoolihan Час: 147.25 Кліщів

Код еталону ...

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

}

Бенчмарк Примітки: Режим випуску, доданий не-налагоджувач, процесор i7, середня кількість 4 запусків, протестовані лише короткі рядки


1
Приємно бачити мою статтю на яку посилається тут! (Я Феліпе Мачадо) Я збираюся оновити його за допомогою відповідного інструменту орієнтиру під назвою BenchmarkDotNet! Я спробую налаштувати прогони в усі періоди виконання (тепер, коли у нас
ДОБРЕ НЕ ЧАС

1
@Loudenvier - Приємна робота над цим. Ваш був найшвидшим майже на 400%! .Net Core - це як безкоштовне підвищення продуктивності на 150-200%. Вона наближається до продуктивності c ++, але набагато простіше кодувати. Дякуємо за коментар
Захід сонця

Це робиться лише пробілами, а не іншими символами пробілу. Може бути, ви хочете char.IsWhiteSpace (ch) замість src [i] == '\ u0020'. Я помічаю, що це відредагувало громаду. Вони його гавкали?
Злий голуб

3

Я ділюся тим, що використовую, бо, здається, я придумав щось інше. Я використовую це деякий час, і це досить швидко для мене. Я не впевнений, як це протиставляється іншим. Я використовую його в розмежуваному файловому програмі і запускаю великі таблиці даних по одному по одному через нього.

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

2

Використовуючи тестову програму, яку розмістив Джон Скіт, я спробував зрозуміти, чи зможу я отримати рукописний цикл для швидшого запуску.
Я можу кожного разу перемогти NormalizeWithSplitAndJoin, але тільки перемогти NormalizeWithRegex з введеннями 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();
}

Я не дивився на машинний код, який створює тремтіння, однак я вважаю, що проблема полягає в часі, який вимагає виклик StringBuilder.Append (), і щоб зробити це набагато краще, знадобиться використання небезпечного коду.

Тож Regex.Replace () дуже швидко і важко перемогти !!


2

VB.NET

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

C #

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

Насолоджуйтесь потужністю LINQ = D


Саме так! Для мене це теж найелегантніший підхід. Отже, для запису в C # це буде:string.Join(" ", myString.Split(' ').Where(s => s != " ").ToArray())
Efrain

1
Незначне вдосконалення, Splitщоб виловити всю пробіл та зняти Whereпункт:myString.Split(null as char[], StringSplitOptions.RemoveEmptyEntries)
Девід

1
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");

Це замінює всі символи, що не містять слова, на пробіл. Таким чином, він також замінить такі речі, як дужки та цитати тощо, що може бути не тим, що ви хочете.
Герман

0

Найменше рішення:

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


0

Ви можете спробувати це:

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

0

Змінні групи забезпечують impler підходить дозвіл заміни декількох пробільних символів з однієї єдиними одним:

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

Зверніть увагу, що другий приклад зберігає єдину, \nа прийнята відповідь замінить кінець рядка пробілом.

Якщо вам потрібно замінити будь-яку комбінацію символів пробілу першою, просто видаліть зворотну посилання \kз шаблону.


0

Використання регулярного вираження для заміни двох або більше пробілів на простір також є хорошим рішенням.

Ми використовуємо шаблон регулярного вираження як " \ s + ".

  • \ s відповідає пробілу, вкладці, новій лінії, поверненню каретки, стрічці форми або вертикальній вкладці.

  • '+' говорить про одне або декілька подій.

Приклад Regex

String blogName = "  Sourav .  Pal.   "

 String nameWithProperSpacing = blogName.replaceAll("\\s+", " ");   
System.out.println( nameWithProperSpacing );

-1

Для цього не існує вбудованого способу. Ви можете спробувати це:

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

Це видалить провідні та кінцеві пробіли, а також згортає будь-який внутрішній пробіл до одного символу пробілу. Якщо ви дійсно хочете згорнути пробіли, то рішення, що використовують регулярний вираз, краще; інакше це рішення краще. (Див. Аналіз, зроблений Джоном Скітом.)


7
Якщо регулярний вираз компілюється і кешируєтся, я не впевнений , що є більше накладних витрат , ніж розщеплення і приєднання, які можуть створювати навантаження проміжних рядків сміття. Ви зробили ретельні орієнтири обох підходів, перш ніж припускати, що ваш шлях швидший?
Джон Скіт

1
пробіли тут не задекларовані
Тім Хуліхан

3
Говорячи про накладні, чому на землі ви дзвоните, source.ToCharArray()а потім викидаєте результат?
Джон Скіт

2
І закликаючи ToCharArray()результат string.Join, тільки щоб створити нову рядок ... ух, адже це бути в посту, який скаржиться на накладні витрати просто чудовий. -1.
Джон Скіт

1
О, і припускаючи whitespaceце new char[] { ' ' }, це дасть неправильний результат , якщо вхідний рядок починається або закінчується з простором.
Джон Скіт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.