3

文字列内の余分なスペースをすべて削除する必要があります。正規表現を使用して文字列を一致させ、一致した文字列を他の文字列に置き換えます。理解を深めるために、以下の例を参照してください。

3 つの入力文字列:

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

これは、1 つのパターン正規表現で一致する 3 つの文字列です。次のようになります。

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

正常に動作しますが、パフォーマンスに問題があります。多くのパターン (~20k) があり、各パターンを実行しようとすると、実行速度が非常に遅くなります (3 ~ 5 分)。

多分これを行うためのより良い方法がありますか?たとえば、いくつかのサードパーティのライブラリを使用しますか?

UPD: 皆さん、この質問はこれを行う方法に関するものではありません。最高のパフォーマンスでこれを行う方法についてです。:)


もっと詳しく説明しましょう。主な目標は、テキストをトークン化することです。(いくつかのトークンを特別な記号に置き換えます)

たとえば、「ナイストライ」というトークンがあります。次に、「これはいい試みです」というテキストを入力します。結果: 「これは @tokenizedtext@ です」ここで @tokenizedtext@ いくつかの特殊記号。この場合は問題ありません。

次に、「Mike said it was a nice try」という文字列があります。結果は「Mike said it was a @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番目のパターンの方が速いようです。サンプルを編集して適応させています。

EDIT 2 :メソッドを書きましたunsafe。正規表現を含む、ここで紹介する他のものよりもはるかに高速ですが、言葉自体が言うように、安全ではありません. 私が書いたコードに問題はないと思いますが、間違っているかもしれません -- メソッドにバグがある場合に備えて、何度も確認してください。

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 に答える