c#去除多余空格的最快方法

Posted

技术标签:

【中文标题】c#去除多余空格的最快方法【英文标题】:c# Fastest way to remove extra white spaces 【发布时间】:2011-09-20 12:12:35 【问题描述】:

将多余的空格替换为一个空格的最快方法是什么? 例如

来自

foo      bar 

foo bar

【问题讨论】:

最快的写法,最少的代码行,可理解/可维护的 LOC,CPU 时间,其他? How to replace multiple white spaces with one white space的可能重复 【参考方案1】:

您可以使用正则表达式:

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

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

要提高性能,请传递RegexOptions.Compiled

【讨论】:

@Navid:用空格替换\snew Regex(@" +")(两个空格字符) 考虑到他最初对性能的要求,这是公认的答案,这有点有趣。 我的测试结果(下面的代码)是 SLaks=1407ms 的 Regex,Blindy=154ms 的 StringBuilder,Array=130ms,NoIf=91ms。【参考方案2】:

试试这个:

System.Text.RegularExpressions.Regex.Replace(input, @"\s+", " ");

【讨论】:

不要每次都重新解析正则表达式。【参考方案3】:

您可以使用 indexOf 首先获取空白序列的开始位置,然后使用替换方法将空白更改为“”。从那里,您可以使用您抓取的索引并在该位置放置一个空白字符。

【讨论】:

这将涉及大量浪费的String 实例。 没错,不幸的是,我不熟悉任何快捷方式。 现在研究一下,这对我目前正在编写的程序应该很有用。【参考方案4】:

最快的方法?遍历字符串并在StringBuilder 中逐个字符地构建第二个副本,每组空格只复制一个空格。

更容易键入Replace 变体将产生大量额外字符串(或浪费时间构建正则表达式 DFA)。

使用比较结果编辑:

使用http://ideone.com/NV6EzU,n=50(必须在 ideone 上减少它,因为他们不得不杀死我的进程需要很长时间),我得到:

正则表达式:7771 毫秒。

字符串生成器:894 毫秒。

确实如预期的那样,Regex 对于这么简单的事情效率极低。

【讨论】:

Compiled 正则表达式的执行速度与您自己编写的任何内容一样快 @SLaks,另一个全面的概括被推翻! @L_7337,这就是 3 年(现在几乎 4 个)旧帖子的情况,链接有时会停止工作。你只需要相信我的话。 @Blindy:那么您能否将您直接使用的代码添加到您的答案中?提前谢谢! 你不需要源码,我已经把算法描述得够详细了。链接的程序仅用于速度比较。【参考方案5】:
string text = "foo       bar";
text = Regex.Replace(text, @"\s+", " ");
// text = "foo bar"

此解决方案适用于空格、制表符和换行符。如果只需要空格,请将 '\s' 替换为 ' '。

【讨论】:

不要每次都重新解析正则表达式。【参考方案6】:
string yourWord = "beep boop    baap beep   boop    baap             beep";

yourWord = yourWord .Replace("  ", " |").Replace("| ", "").Replace("|", "");

【讨论】:

如果您没有“|”在你的Word中。【参考方案7】:

我使用以下方法 - 它们不仅处理所有空白字符,还修剪前导尾随空格,删除多余的 空格,并且所有空格都替换为空格 char(所以我们有统一的空格分隔符)。而且这些方法快速

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;

【讨论】:

值得注意的是,单个换行符 '\n' 将被替换为空格 ' '【参考方案8】:

这很有趣,但在我的 PC 上,以下方法与 Sergey Povalyaev 的 StringBulder 方法一样快——(1000 次重复约 282 毫秒,10k src 字符串)。但不确定内存使用情况。

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

显然它适用于任何字符 - 不仅仅是空格。

虽然这不是 OP 所要求的 - 但如果您真正需要的是仅用一个实例替换字符串中的特定连续字符,您可以使用这种相对有效的方法:

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

【讨论】:

这将无法正常工作,例如: RemoveDuplicateChars("aa--sdf a", new char[] 'a' ) 将返回: "--sdf " 而它应该返回“a--sdf a”。 好的,RemoveDuplicateChars 不是这种方法的最佳名称,但是如果您查看 OP 的问题,您会发现目标是在源字符串中替换任意数量的可被视为带有单个空格的空白字符。所以我对“任何字符”的评论意味着这一点。我将编辑我的答案以使其更明显。【参考方案9】:

我需要其中一个来处理较大的字符串,并想出了下面的例程。

任何连续的空格(包括制表符、换行符)都将替换为normalizeTo 中的任何内容。 前导/尾随空格被删除。

它比使用我的 5k->5mil 字符字符串的 RegEx 快 8 倍左右。

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

【讨论】:

如果输入=“”(单个空格),此解决方案将在返回行上引发异常。我改成something like this,现在可以正常使用了。【参考方案10】:
string q = " Hello     how are   you           doing?";
string a = String.Join(" ", q.Split(new string[]  " " , StringSplitOptions.RemoveEmptyEntries));

【讨论】:

【参考方案11】:

不需要复杂的代码!这是一个简单的代码,可以删除所有重复项:

public static String RemoveCharOccurence(String s, char[] remove)

    String s1 = s;
    foreach(char c in remove)
    
        s1 = RemoveCharOccurence(s1, c);
    

    return s1;


public static String RemoveCharOccurence(String s, char remove)

    StringBuilder sb = new StringBuilder(s.Length);

    Boolean removeNextIfMatch = false;
    foreach(char c in s)
    
        if(c == remove)
        
            if(removeNextIfMatch)
                continue;
            else
                removeNextIfMatch = true;
        
        else
            removeNextIfMatch = false;

        sb.Append(c);
    

    return sb.ToString();

【讨论】:

这不能回答 OP 的问题。他想用一个“空格”替换多个空格字符(空格、制表符、换行符)——可能只是空格。您的代码不会按预期处理空格后跟制表符(以及另一个空格和/或换行符) - 它不会用单个空格替换该序列。 此外,如果您需要删除多个字符,这效率不高 - 它需要 N 次传递。所以这绝对不是最快的方法。如果您只需要一个字符重复数据删除,那么您的第二种方法是可以的【参考方案12】:
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();
    

【讨论】:

【参考方案13】:

我尝试使用 StringBuilder 来:

    删除多余的空白子字符串 接受循环遍历原始字符串的字符,正如 Blindy 建议的那样

这是我发现的性能和可读性的最佳平衡(使用 100,000 次迭代计时运行)。有时这比不那么易读的版本测试得更快,最多慢 5%。在我的小测试字符串上,正则表达式需要 4.24 倍的时间。

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

【讨论】:

为了更好的内存使用,可以给StringBuilder初始容量。【参考方案14】:

它并不快,但如果简单有帮助,这很有效:

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

【讨论】:

【参考方案15】:

有点晚了,但我已经做了一些基准测试,以获得去除多余空格的最快方法。如果有更快的答案,我很乐意添加它们。

结果:

    NormalizeWhiteSpaceForLoop:156 毫秒 (by Me - From my answer on removing all whitespace) NormalizeWhiteSpace:267 毫秒 (by Alex K.) 正则表达式编译:1950 毫秒 (by SLaks) 正则表达式:2261 毫秒 (by SLaks)

代码:

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

测试:

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

【讨论】:

我刚刚添加了一个没有分支的版本(没有switch,没有if),它只做空间,而且似乎比NormalizeWhiteSpaceForLoop 更快。 酷。添加您的代码作为答案,我将运行基准测试。 如果这些方法不删除字符串中的最后一个空格,它们的意义何在? 这些方法将替换重复的空格(多个一个空格一个接一个的空格)。因此,如果字符串末尾有一个空格,则不会被删除。【参考方案16】:

这个问题有几个要求不清楚,值得深思。

    您想要一个前导或尾随空格字符吗? 用单个字符替换所有空格时,是否希望该字符保持一致? (即,其中许多解决方案会将 \t\t 替换为 \t 并将 ' ' 替换为 ' '。

这是一个非常高效的版本,它用一个空格替换所有空格,并在 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();
  

【讨论】:

【参考方案17】:

很简单,使用.Replace()方法即可:

string words = "Hello     world!";
words = words.Replace("\\s+", " ");

输出 >>> “Hello world!”

【讨论】:

【参考方案18】:

我刚刚做了这个,但还没有测试过。但我觉得这很优雅,并且避免了正则表达式:

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

【讨论】:

【参考方案19】:

我在这里遗漏了什么吗?我想出了这个:

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

我知道我在这里创建了第二个字符串来返回它以及创建 splitParts 数组。只是觉得这很简单。也许我没有考虑到一些潜在的情况。

【讨论】:

“我知道我在这里创建了第二个字符串”——您实际上是在为每个单词创建一个字符串,并且一个数组将它们放在一起。这是大量的内存分配,垃圾收集将不得不冻结你的线程来清理。【参考方案20】:

我知道这真的很老了,但压缩空格的最简单方法(用单个“空格”字符替换任何重复出现的空格字符)如下:

    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;
    

【讨论】:

当然,您可以在“for”循环中使用相同的逻辑,以稍微提高性能(因为 C# 不需要实例化“foreach”循环所需的枚举器)跨度> 【参考方案21】:
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;

【讨论】:

【参考方案22】:

这段代码运行良好。我没有衡量性能。

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

【讨论】:

【参考方案23】:

对于那些只想复制并继续的人:

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

【讨论】:

【参考方案24】:

我对 C# 不是很熟悉,因此我的代码不是优雅/最高效的代码。我来这里是为了找到一个适合我用例的答案,但我找不到(或者我想不出来)。

对于我的用例,我需要在以下条件下规范化所有空格(WS:spacetabcr lf):

WS 可以任意组合 用最重要的 WS 替换一系列 WS tab 在某些情况下需要保留(例如,一个制表符分隔的文件,在这种情况下,还需要保留重复的制表符)。但在大多数情况下,它们必须转换为空格。

所以这是一个示例输入和一个预期输出(免责声明:我的代码仅针对此示例进行测试)



        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


转换成

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

这是我的代码

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

在此处查看此代码:https://dotnetfiddle.net/eupjIU

【讨论】:

【参考方案25】:

我不知道这是否是最快的方法,但我使用它,这对我有用:

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

【讨论】:

【参考方案26】:

我尝试过使用数组但没有if

结果

PS C:\dev\Spaces> dotnet run -c release
// .NETCoreApp,Version=v3.0
Seed=7, n=20, s.Length=2828670
Regex by SLaks            1407ms, len=996757
StringBuilder by Blindy    154ms, len=996757
Array                      130ms, len=996757
NoIf                        91ms, len=996757
All match!

方法

private static string WithNoIf(string s)

    var dst = new char[s.Length];
    uint end = 0;
    char prev = char.MinValue;
    for (int k = 0; k < s.Length; ++k)
    
        var c = s[k];
        dst[end] = c;

        // We'll move forward if the current character is not ' ' or if prev char is not ' '
        // To avoid 'if' let's get diffs for c and prev and then use bitwise operatios to get 
        // 0 if n is 0 or 1 if n is non-zero
        uint x = (uint)(' ' - c) + (uint)(' ' - prev); // non zero if any non-zero

        end += ((x | (~x + 1)) >> 31) & 1; // https://***.com/questions/3912112/check-if-a-number-is-non-zero-using-bitwise-operators-in-c by ruslik
        prev = c;
    
    return new string(dst, 0, (int)end);

private static string WithArray(string s)

    var dst = new char[s.Length];
    int end = 0;
    char prev = char.MinValue;
    for (int k = 0; k < s.Length; ++k)
    
        char c = s[k];
        if (c != ' ' || prev != ' ') dst[end++] = c;
        prev = c;
    
    return new string(dst, 0, end);

测试代码

public static void Main()

    const int n = 20;
    const int seed = 7;
    string s = GetTestString(seed);

    var fs = new (string Name, Func<string, string> Func)[]
        ("Regex by SLaks", WithRegex),
        ("StringBuilder by Blindy", WithSb),
        ("Array", WithArray),
        ("NoIf", WithNoIf),
    ;

    Console.WriteLine($"Seed=seed, n=n, s.Length=s.Length");
    var d = new Dictionary<string, string>(); // method, result
    var sw = new Stopwatch();
    foreach (var f in fs)
    
        sw.Restart();
        var r = "";
        for( int i = 0; i < n; i++) r = f.Func(s);
        sw.Stop();
        d[f.Name] = r;
        Console.WriteLine($"f.Name,-25 sw.ElapsedMilliseconds,4ms, len=r.Length");
    
    Console.WriteLine(d.Values.All( v => v == d.Values.First()) ? "All match!" : "Not all match! BAD");


private static string GetTestString(int seed)

    // by blindy from https://***.com/questions/6442421/c-sharp-fastest-way-to-remove-extra-white-spaces
    var rng = new Random(seed);
    // random 1mb+ string (it's slow enough...)
    StringBuilder ssb = new StringBuilder(1 * 1024 * 1024);
    for (int i = 0; i < 1 * 1024 * 1024; ++i)
        if (rng.Next(5) == 0)
            ssb.Append(new string(' ', rng.Next(20)));
        else
            ssb.Append((char)(rng.Next(128 - 32) + 32));
    string s = ssb.ToString();
    return s;

【讨论】:

【参考方案27】:

如果你调整 famos 算法——在这种情况下是比较“相似”的字符串——不区分大小写并且不关心多个空格并且也可以忍受 NULL。 不要相信基准——这一项被投入到一项数据比较密集的任务中,大约。 1/4GB 数据和加速在整个动作中约为 100%(评论部分与此算法 5/10 分钟)。这里的一些these 的差异较小,约为 30%。会告诉构建最佳算法需要反汇编并检查编译器在发布或调试构建中将做什么。这里也简单了一半,完整的as answer to similar (C question),但区分大小写。

public static bool Differs(string srcA, string srcB)

    //return string.Join(" ", (a?.ToString()??String.Empty).ToUpperInvariant().Split(new char[0], StringSplitOptions.RemoveEmptyEntries).ToList().Select(x => x.Trim()))
    //    != string.Join(" ", (b?.ToString()??String.Empty).ToUpperInvariant().Split(new char[0], StringSplitOptions.RemoveEmptyEntries).ToList().Select(x => x.Trim()));

    if (srcA == null)  if (srcB == null) return false; else srcA = String.Empty;  // A == null + B == null same or change A to empty string
    if (srcB == null)  if (srcA == null) return false; else srcB = String.Empty; 
    int dstIdxA = srcA.Length, dstIdxB = srcB.Length; // are there any remaining (front) chars in a string ?
    int planSpaceA = 0, planSpaceB = 0; // state automaton 1 after non-WS, 2 after WS
    bool validA, validB; // are there any remaining (front) chars in a array ?
    char chA = '\0', chB = '\0';

spaceLoopA:
        if (validA = (dstIdxA > 0)) 
            chA = srcA[--dstIdxA];
            switch (chA) 
                case '!': case '"': case '#': case '$': case '%': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case '-':
                case '.': case '/': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':':
                case ';': case '<': case '=': case '>': case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
                case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':
                case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '[': case '\\': case ']': case '^': case '_': case '`': // a-z will be | 32 to Upper
                case '': case '|': case '': case '~':
                    break; // ASCII except lowercase
                case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i':
                case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
                case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
                    chA = (Char)(chA & ~0x20);
                    break;
                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 (planSpaceA == 1) planSpaceA = 2; // cycle here to address multiple WS before non-WS part
                    goto spaceLoopA;
                default:
                    chA = Char.ToUpper(chA);
                    break;
        
spaceLoopB:
        if (validB = (dstIdxB > 0))  // 2nd string / same logic
            chB = srcB[--dstIdxB];
            switch (chB) 
                case '!': case '"': case '#': case '$': case '%': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case '-':
                case '.': case '/': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':':
                case ';': case '<': case '=': case '>': case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
                case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':
                case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '[': case '\\': case ']': case '^': case '_': case '`': // a-z will be | 32 to Upper
                    break;
                case '': case '|': case '': case '~':
                    break; // ASCII except lowercase
                case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i':
                case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
                case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
                    chB = (Char)(chB & ~0x20);
                    break;
                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 (planSpaceB == 1) planSpaceB = 2;
                goto spaceLoopB;
                default:
                    chB = Char.ToUpper(chB);
                    break;
        
        if (planSpaceA != planSpaceB) return true; // both should/not have space now (0 init / 1 last non-WS / 2 last was WS)
        if (validA)  // some (non-WS) in A still
            if (validB) 
            if (chA != chB) return true; // both have another char to compare, are they different ?
             else return true; // not in B not - they are different
         else  // A done, current last pair equal => continue 2 never ending loop till B end (by WS only to be same)
            if (!validB) return false; // done and end-up here without leaving by difference => both are same except some WSs arround
            else return true; // A done, but non-WS remains in B - different
          // A done, B had no non-WS or non + WS last follow - never ending loop continue
        planSpaceA = 1; planSpaceB = 1;
        goto spaceLoopA; // performs better
    

【讨论】:

【参考方案28】:

我的版本(根据 Stian 的回答改进)。应该很快。

public static string TrimAllExtraWhiteSpaces(this string input)

    if (string.IsNullOrEmpty(input))
    
        return input;
    

    var current = 0;
    char[] output = new char[input.Length];
    var charArray = input.ToCharArray();

    for (var i = 0; i < charArray.Length; i++)
    
        if (!char.IsWhiteSpace(charArray[i]))
        
            if (current > 0 && i > 0 && char.IsWhiteSpace(charArray[i - 1]))
            
                output[current++] = ' ';
            
            output[current++] = charArray[i];
        
    

    return new string(output, 0, current);

【讨论】:

【参考方案29】:

我能想到的最简单的方法:

Text = Text.Replace("\<Space>\<Space>", "\<Space>").Replace("\<Space>\<Space>", "\<Space>");
// Replace 2 \<Space>s with 1 space, twice

【讨论】:

以上是关于c#去除多余空格的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

编程去掉字符串中的多余空格

source insight 保存时删除多余空格,去除多余空格 space tab键

华为OD机试真题Java实现去除多余空格真题+解题思路+代码(2022&2023)

Python去除多余空格

华为OD机试真题 Java 实现去除多余空格2022.11 Q4 新题

华为OD机试真题 JS 实现去除多余空格2023 Q1 | 200分