Cách thay thế nhiều khoảng trắng bằng một khoảng trắng


108

Giả sử tôi có một chuỗi chẳng hạn như:

"Hello     how are   you           doing?"

Tôi muốn một chức năng biến nhiều khoảng trắng thành một không gian.

Vì vậy, tôi sẽ nhận được:

"Hello how are you doing?"

Tôi biết tôi có thể sử dụng regex hoặc gọi

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

Nhưng tôi sẽ phải gọi nó nhiều lần để đảm bảo rằng tất cả các khoảng trắng tuần tự chỉ được thay thế bằng một.

Đã có một phương pháp tích hợp cho việc này?


Bạn có thể làm rõ: bạn chỉ xử lý khoảng trắng hay "tất cả" khoảng trắng?
Jon Skeet

Và bạn có muốn bất kỳ khoảng trắng không phải dấu cách nào được chuyển đổi thành dấu cách không?
Jon Skeet

Ý tôi chỉ là tất cả khoảng trắng trong loạt phim tối đa là 1
Matt


2 điều cần xem xét: 1. char.IsWhiteSpace bao gồm vận chuyển một chiều linefeed vv 2. 'trắng' có lẽ là thử nghiệm chính xác hơn với Char.GetUnicodeCategory (ch) = Globalization.UnicodeCategory.SpaceSeparator
smirkingman

Câu trả lời:


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

40
imo, tránh regex nếu thoải mái của bạn với họ là tối ưu hóa sớm
Tim Hoolihan

8
Nếu ứng dụng của bạn không quan trọng về thời gian, nó có thể chi trả 1 micro giây cho tổng chi phí xử lý.
Daniel

16
Lưu ý rằng '\ s' không chỉ thay thế khoảng trắng mà còn thay thế các ký tự dòng mới.
Bart Kiers

12
nắm bắt tốt, nếu bạn chỉ muốn không gian chuyển đổi mô hình để "[] +"
Tim Hoolihan

9
Bạn không nên sử dụng '{2,}' thay vì '+' để tránh thay thế các khoảng trắng đơn lẻ?
angularsen

52

Câu hỏi này không đơn giản như các áp phích khác đã làm ra nó (và như tôi tin tưởng ban đầu) - bởi vì câu hỏi không hoàn toàn chính xác như nó cần.

Có sự khác biệt giữa "khoảng trắng" và "khoảng trắng". Nếu bạn chỉ có nghĩa là dấu cách, thì bạn nên sử dụng regex của " {2,}". Nếu bạn có nghĩa là bất kỳ khoảng trắng nào , đó là một vấn đề khác. Có nên chuyển đổi tất cả khoảng trắng thành khoảng trắng không? Điều gì sẽ xảy ra với không gian ở đầu và cuối?

Đối với điểm chuẩn bên dưới, tôi đã giả định rằng bạn chỉ quan tâm đến khoảng trắng và bạn không muốn làm bất cứ điều gì với các khoảng trắng đơn lẻ, ngay cả ở đầu và cuối.

Lưu ý rằng tính đúng đắn hầu như luôn quan trọng hơn hiệu suất. Thực tế là giải pháp Tách / Nối loại bỏ bất kỳ khoảng trắng nào ở đầu / cuối (thậm chí chỉ là khoảng trắng đơn) là không chính xác theo yêu cầu đã chỉ định của bạn (tất nhiên có thể không đầy đủ).

Điểm chuẩn sử dụng 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);
    }
}

Một vài lần chạy thử nghiệm:

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

Ở đây con số đầu tiên là số lần lặp lại, con số thứ hai là thời gian thực hiện và con số thứ ba là điểm theo tỷ lệ với 1,0 là tốt nhất.

Điều đó cho thấy rằng trong ít nhất một số trường hợp (bao gồm cả trường hợp này), một biểu thức chính quy có thể hoạt động tốt hơn giải pháp Tách / Nối, đôi khi bằng một biên độ rất đáng kể.

Tuy nhiên, nếu bạn thay đổi yêu cầu "tất cả khoảng trắng", thì Tách / Tham gia vẻ sẽ giành chiến thắng. Như thường lệ, ma quỷ ở trong chi tiết ...


1
Phân tích tuyệt vời. Vì vậy, có vẻ như cả hai chúng tôi đều đúng ở các mức độ khác nhau. Mã trong câu trả lời của tôi được lấy từ một hàm lớn hơn có khả năng chuẩn hóa tất cả các khoảng trắng và / hoặc các ký tự điều khiển từ bên trong một chuỗi và từ đầu đến cuối.
Scott Dorman

1
Chỉ với các ký tự khoảng trắng mà bạn đã chỉ định, trong hầu hết các thử nghiệm của tôi, regex và Split / Join gần như ngang nhau - S / J có một lợi ích nhỏ, rất nhỏ, với cái giá phải trả là sự chính xác và phức tạp. Vì những lý do đó, tôi thường thích regex hơn. Đừng hiểu sai ý tôi - tôi không phải là một fanboy của regex, nhưng tôi không thích viết mã phức tạp hơn vì lợi ích của hiệu suất mà không thực sự kiểm tra hiệu suất trước.
Jon Skeet

NormalizeWithSplitAndJoin sẽ tạo ra nhiều rác hơn, thật khó để nói liệu một vấn đề thực sự sẽ bị ảnh hưởng nhiều hơn thời gian GC sau đó là banchmark.
Ian Ringrose

@IanRingrose Có thể tạo loại rác nào?
Dronz

18

Một expressoin thông thường sẽ là cách dễ nhất. Nếu bạn viết regex đúng cách, bạn sẽ không cần nhiều cuộc gọi.

Thay đổi nó thành thế này:

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

Một vấn đề của tôi @"\s{2,}"là nó không thể thay thế các tab đơn và các ký tự dấu cách Unicode khác bằng dấu cách. Nếu bạn định thay thế 2 tab bằng dấu cách, thì có lẽ bạn nên thay thế 1 tab bằng dấu cách. @"\s+"sẽ làm điều đó cho bạn.
David Specht

17

Mặc dù các câu trả lời hiện có đều ổn, nhưng tôi muốn chỉ ra một cách tiếp cận không hoạt động:

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

Điều này có thể lặp lại mãi mãi. Bất cứ ai quan tâm để đoán tại sao? (Tôi chỉ biết điều này khi nó được hỏi như một câu hỏi nhóm tin vài năm trước ... ai đó thực sự đã gặp phải vấn đề đó.)


Tôi nghĩ rằng tôi nhớ câu hỏi này đã được hỏi một thời gian trở lại SO. IndexOf bỏ qua một số ký tự mà Replace thì không. Vì vậy, không gian kép luôn ở đó, chỉ là không bao giờ bị loại bỏ.
Brandon

19
Đó là vì IndexOf bỏ qua một số ký tự Unicode, giá trị cụ thể trong trường hợp này là một số ký tự châu Á. Rất tiếc, dấu nối không có độ rộng bằng 0 theo Google.
ahawker

Tôi đã học được rằng cách cứng :( stackoverflow.com/questions/9260693/...
Antonio Bakula

Tôi đã học thật chăm chỉ. Đặc biệt với hai bộ nối không có độ rộng bằng không (\ u200C \ u200C). IndexOf trả về chỉ mục của "khoảng trắng kép" này, nhưng Replace không thay thế nó. Tôi nghĩ đó là vì đối với IndexOf, bạn cần chỉ định StringComparsion (Ordinal) để hoạt động giống như Replace. Bằng cách này, cả hai đều không định vị được "dấu cách kép". Tìm hiểu thêm về StringComparsion docs.microsoft.com/en-us/dotnet/api/…
Martin Brabec

4

Như đã chỉ ra, điều này có thể dễ dàng thực hiện bằng một biểu thức chính quy. Tôi chỉ nói thêm rằng bạn có thể muốn thêm .trim () vào đó để loại bỏ khoảng trắng ở đầu / cuối.


4

Đây là Giải pháp mà tôi làm việc. Không có RegEx và 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();
}

vì vậy bạn có thể:

string cleanedString = dirtyString.TrimWhiteSpace();

4

Công cụ xóa khoảng trắng nhanh hơn ... Đây là công cụ nhanh nhất và dựa trên bản sao tại chỗ của 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);
}

Điểm chuẩn ...

InPlaceCharArraySpaceOnly bởi Felipe Machado trên CodeProject 2015 và được sửa đổi bởi Sunsetquest để loại bỏ nhiều không gian. Thời gian: 3,75 Ve

InPlaceCharArray của Felipe Machado 2015 và được sửa đổi một chút bởi Sunsetquest để loại bỏ nhiều không gian. Thời gian 6,50 Bọ ve (cũng hỗ trợ các tab)

SplitAndJoinOnSpace của Jon Skeet . Thời gian: 13,25 Bọ ve

StringBuilder của fubo Thời gian: 13,5 Ticks (cũng hỗ trợ các tab)

Regex với biên dịch của Jon Skeet . Thời gian: 17 Ve

StringBuilder của David S 2013 Thời gian: 30,5 Ticks

Regex không biên dịch bởi Brandon Thời gian: 63,25 Ticks

StringBuilder bởi người dùng214147 Thời gian: 77.125 Chích

Regex với Tim Hoolihan không biên dịch Thời gian: 147,25 Ticks

Mã điểm chuẩn ...

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

}

Ghi chú điểm chuẩn: Chế độ phát hành, không có trình gỡ lỗi đính kèm, bộ xử lý i7, trung bình 4 lần chạy, chỉ kiểm tra các chuỗi ngắn


1
Rất vui khi thấy bài viết của tôi được tham khảo tại đây! (Tôi là Felipe Machado) Tôi sắp cập nhật nó bằng một công cụ điểm chuẩn thích hợp có tên là BenchmarkDotNet! Tôi sẽ cố gắng thiết lập chạy trong tất cả các thời gian chạy (bây giờ chúng tôi đã có DOT NET CORE và những thứ thích ...
Loudenvier

1
@Loudenvier - Làm tốt lắm. Của bạn là người nhanh nhất gần 400%! .Net Core giống như một phần mềm tăng hiệu suất 150-200% miễn phí. Nó đang tiến gần hơn đến hiệu suất c ++ nhưng dễ viết mã hơn nhiều. Cảm ơn vì nhận xét.
Sunsetquest

Điều này chỉ thực hiện dấu cách, không phải ký tự khoảng trắng khác. Có thể bạn muốn char.IsWhiteSpace (ch) thay vì src [i] == '\ u0020'. Tôi nhận thấy điều này đã được chỉnh sửa bởi cộng đồng. Họ đã làm nó lên?
Evil Pigeon

3

Tôi đang chia sẻ những gì tôi sử dụng, vì có vẻ như tôi đã nghĩ ra một điều gì đó khác biệt. Tôi đã sử dụng cái này một thời gian và nó đủ nhanh đối với tôi. Tôi không chắc nó xếp chồng lên nhau như thế nào so với những cái khác. Tôi sử dụng nó trong trình ghi tệp được phân tách và chạy các tệp dữ liệu lớn từng trường một qua nó.

    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

Sử dụng chương trình thử nghiệm mà Jon Skeet đã đăng, tôi đã thử xem liệu tôi có thể nhận được vòng lặp viết tay để chạy nhanh hơn không.
Tôi có thể đánh bại NormalizeWithSplitAndJoin mọi lúc, nhưng chỉ đánh bại NormalizeWithRegex với đầu vào là 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();
}

Tôi chưa xem mã máy mà jitter tạo ra, tuy nhiên tôi cho rằng vấn đề là thời gian thực hiện lệnh gọi đến StringBuilder.Append () và để làm tốt hơn nhiều sẽ cần sử dụng mã không an toàn.

Vì vậy, Regex.Replace () rất nhanh và khó bị đánh bại !!


2

VB.NET

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

C #

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

Tận hưởng sức mạnh của LINQ = D


Chính xác! Đối với tôi, đây cũng là cách tiếp cận thanh lịch nhất. Vì vậy, đối với bản ghi, trong C # đó sẽ là:string.Join(" ", myString.Split(' ').Where(s => s != " ").ToArray())
Efrain

1
Nâng cấp nho nhỏ trên Splitđể bắt tất cả các khoảng trắng và loại bỏ các Wherekhoản:myString.Split(null as char[], StringSplitOptions.RemoveEmptyEntries)
David

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

Điều này thay thế tất cả các ký tự không phải từ bằng khoảng trắng. Vì vậy, nó cũng sẽ thay thế những thứ như dấu ngoặc và dấu ngoặc kép, v.v., những thứ có thể không phải là những gì bạn muốn.
Herman

0

Giải pháp nhỏ nhất:

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


0

Bạn có thể thử điều này:

    /// <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

Các nhóm thay thế cung cấp phương pháp tiếp cận bộ xử lý giải quyết việc thay thế nhiều ký tự khoảng trắng bằng cùng một ký tự :

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

Xin lưu ý rằng ví dụ thứ hai giữ đơn \n trong khi câu trả lời được chấp nhận sẽ thay thế cuối dòng bằng dấu cách.

Nếu bạn cần thay thế bất kỳ tổ hợp ký tự khoảng trắng nào bằng tổ hợp đầu tiên, chỉ cần xóa tham chiếu ngược \kkhỏi mẫu.


0

Sử dụng biểu thức chính quy, để thay thế 2 hoặc nhiều khoảng trắng bằng một khoảng trắng, cũng là một giải pháp tốt.

Chúng tôi đang sử dụng mẫu regex là “ \ s + ”.

  • \ s khớp với khoảng trắng, tab, dòng mới, dấu xuống dòng, nguồn cấp dữ liệu biểu mẫu hoặc tab dọc.

  • '+' cho biết một hoặc nhiều lần xuất hiện.

Ví dụ về Regex

String blogName = "  Sourav .  Pal.   "

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

-1

Không có cách nào được tích hợp sẵn để làm điều này. Bạn có thể thử điều này:

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

Thao tác này sẽ loại bỏ khoảng trắng đầu và cuối cũng như thu gọn bất kỳ khoảng trắng nội bộ nào thành một ký tự khoảng trắng duy nhất. Nếu bạn thực sự chỉ muốn thu gọn khoảng trắng, thì các giải pháp sử dụng biểu thức chính quy sẽ tốt hơn; nếu không thì giải pháp này tốt hơn. (Xem phân tích được thực hiện bởi Jon Skeet.)


7
Nếu biểu thức chính quy được biên dịch và lưu vào bộ nhớ cache, tôi không chắc điều đó có nhiều chi phí hơn việc tách và nối, điều này có thể tạo ra số chuỗi rác trung gian. Bạn đã thực hiện các điểm chuẩn cẩn thận của cả hai cách tiếp cận trước khi cho rằng cách của bạn nhanh hơn chưa?
Jon Skeet

1
khoảng trắng được không khai báo ở đây
Tim Hoolihan

3
Nói về chi phí, tại sao bạn đang gọi source.ToCharArray()và sau đó vứt bỏ kết quả?
Jon Skeet

2
việc gọi ToCharArray()kết quả của string.Join, chỉ để tạo một chuỗi mới ... wow, để có được trong một bài viết phàn nàn về chi phí thật đáng chú ý. -1.
Jon Skeet

1
Ồ, và giả sử whitespacenew char[] { ' ' }, điều này sẽ cho kết quả sai nếu chuỗi đầu vào bắt đầu hoặc kết thúc bằng dấu cách.
Jon Skeet
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.