3

我需要删除字符串中的所有额外空格。我使用正则表达式匹配字符串和匹配的字符串,我用其他一些替换。为了更好地理解,请参阅以下示例:

3个输入字符串:

Hello, how are you?
Hello , how are  you?
Hello     ,     how    are   you    ?

这是应由一个模式正则表达式匹配的 3 个字符串。它看起来像这样:

Hello\s*,\s+how\s+are\s+you\s*?

它工作正常,但存在性能问题。如果我有很多模式(~20k)并尝试执行每个模式,它运行速度非常慢(3-5 分钟)。

也许有更好的方法来做到这一点?例如使用一些 3d 方库?

UPD:伙计们,这个问题不是关于如何做到这一点的。这是关于如何以最佳性能做到这一点。:)


让我更详细地解释一下。主要目标是标记文本。(用特殊符号替换一些标记)

例如,我有一个令牌“不错的尝试”。然后我输入文本“这是不错的尝试”。结果:“这是@tokenizedtext@”其中@tokenizedtext@ 一些特殊符号。在这种情况下没关系。

接下来我有字符串“迈克说这是一次不错的尝试”。结果应该是“迈克说这是一个@tokenizedtext@”。我认为主要思想很清楚。

所以我可以有很多代币。当我处理它时,我将我的令牌从“nice try”转换为模式“nice\s+try”。并尝试用此模式输入文本替换。它工作正常。但是,如果在标记中有更多的空格并且还有标点符号,那么我的正则表达式就会变得更大并且工作得非常慢。

你有解决这个问题的一些建议(技术或逻辑)吗?

4

6 回答 6

3

我可以提出一些解决方案。

首先,避免使用静态Regex方法。创建它的一个实例(并存储它,不要为每个替换调用构造函数!),如果可能,使用RegexOptions.Compiled. 它应该会提高你的表现。

其次,您可以尝试检查您的模式。我会做一些分析,但我目前在以下之间犹豫不决:

@"(?<=\s)\s+"

替换为空字符串或:

@"\s+"

用空格代替。您可以同时尝试此代码:

var s = "Hello , how are  you?";
var pattern = @"\s+";
var regex = new Regex(pattern, RegexOptions.Compiled);
var replaced = regex.Replace(s, " ");

编辑:在做了一些测量之后,第二种模式似乎更快。我正在编辑我的样本以适应它。

编辑2:我写了一个unsafe方法。它比这里介绍的其他方法(包括 Regex 的方法)快得多,但是,正如这个词本身所说,它是不安全的。我认为我编写的代码没有任何问题,但我可能是错的——所以请一次又一次地检查它,以防方法中出现错误。

static unsafe string TrimInternal(string input)
{
    var length = input.Length;
    var array = stackalloc char[length];
    fixed (char* fix = input)
    {
        var ptr = fix;
        var counter = 0;
        var lastWasSpace = false;
        while (*ptr != '\x0')
        {
            //Current char is a space?
            var isSpace = *ptr == ' ';
            //If it's a space but the last one wasn't
            //Or if it's not a space
            if (isSpace && !lastWasSpace || !isSpace)
                //Write into the result array
                array[counter++] = *ptr;
            //The last character (before the next loop) was a space
            lastWasSpace = isSpace;
            //Increase the pointer
            ptr++;
        }
        return new string(array, 0, counter);
    }
}

用法(使用 /unsafe 编译):

var s = TrimInternal("Hello    , how       are     you?");

在发布版本中进行的分析,优化,1000000 次迭代:

我上面的正则表达式解决方案:00:00:03.2130121

不安全的解决方案:00:00:00.2063467

于 2013-01-14T11:52:34.030 回答
1

这可能对你有用。它应该很快。请注意,它还会删除字符串末尾的空格;那可能不是你想要的……

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(">{0}<", RemoveExtraSpaces("Hello, how are you?"));
            Console.WriteLine(">{0}<", RemoveExtraSpaces("Hello , how are  you?"));
            Console.WriteLine(">{0}<", RemoveExtraSpaces("Hello     ,     how    are   you    ?"));
        }

        public static string RemoveExtraSpaces(string text)
        {
            var buffer = new char[text.Length];
            bool isSpaced = false;
            int n = 0;

            foreach (char c in text)
            {
                if (c == ' ')
                {
                    isSpaced = true;
                }
                else
                {
                    if (isSpaced)
                    {
                        if ((c != ',') && (c != '?'))
                        {
                            buffer[n++] = ' ';
                        }

                        isSpaced = false;
                    }

                    buffer[n++] = c;
                }
            }

            return new string(buffer, 0, n);
        }
    }
}
于 2013-01-14T12:40:11.417 回答
1

我自己的东西:

查找字符串中 WhiteSpacechar 的所有位置;

private static IEnumerable<int> GetWhiteSpacePos(string input)
{

    int iPos = -1;
    while ((iPos = input.IndexOf(" ", iPos + 1, StringComparison.Ordinal)) > -1)
    {
        yield return iPos;
    }
}

GetWhiteSpacePos返回顺序中的所有空格

        string original_string = "Hello     ,     how    are   you    ?";

        var poss = GetWhiteSpacePos(original_string).ToList();
        int startPos;
        int endPos;
        StringBuilder builder = new StringBuilder(original_string);
        for (int i = poss.Count -1; i > 1; i--)
        {
            endPos = poss[i];
            while ((poss[i] == poss[i - 1] + 1) && i  > 1)
            {
                i--;
            }
            startPos = poss[i];
            if (endPos - startPos > 1)
            {
                builder.Remove(startPos, endPos - startPos);
            }

        }

        string new_string = builder.ToString();
于 2013-01-14T13:03:29.167 回答
0

如果只是空间问题;

试试这个来源: http: //www.codeproject.com/Articles/10890/Fastest-C-Case-Insenstive-String-Replace

    private static string ReplaceEx(string original,
                string pattern, string replacement)
    {
        int count, position0, position1;
        count = position0 = position1 = 0;
        string upperString = original.ToUpper();
        string upperPattern = pattern.ToUpper();
        int inc = (original.Length / pattern.Length) *
                  (replacement.Length - pattern.Length);
        char[] chars = new char[original.Length + Math.Max(0, inc)];
        while ((position1 = upperString.IndexOf(upperPattern,
                                          position0)) != -1)
        {
            for (int i = position0; i < position1; ++i)
                chars[count++] = original[i];
            for (int i = 0; i < replacement.Length; ++i)
                chars[count++] = replacement[i];
            position0 = position1 + pattern.Length;
        }
        if (position0 == 0) return original;
        for (int i = position0; i < original.Length; ++i)
            chars[count++] = original[i];
        return new string(chars, 0, count);
    }

用法:

        string original_string = "Hello     ,     how    are   you    ?";
        while (original_string.Contains("  "))
        {
            original_string = ReplaceEx(original_string, "  ", " ");
        }

替换正则表达式方式:

string resultString = null;
try {
    resultString = Regex.Replace(subjectString, @"\s+", " ", RegexOption.Compiled);
} catch (ArgumentException ex) {
    // Syntax error in the regular expression
}
于 2013-01-14T11:46:18.210 回答
0

嗯,这些问题真的很困扰我们。使用此代码,我相信您会得到您所要求的结果。此命令删除任何字符串之间的任何额外空格。

cleanString= Regex.Replace(originalString, @"\s", " ");

希望塔尔对你有用。谢谢。

因为这是一个单一的指令。它将使用更少的 CPU 资源,从而减少 CPU 时间,最终提高您的性能。因此,在我看来,这种方法在性能方面效果最好。

于 2013-01-14T11:47:13.577 回答
0

您正在使用一个非常复杂的正则表达式..简化正则表达式,这肯定会提高性能

使用\s+并替换为单个空格

于 2013-01-14T11:50:16.827 回答