0

在没有任何字符要转义的情况下,将

if (s.Contains("\"")) 
    s = s.Replace("\"", "\"\""); 

真的跑得比

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

Contains 方法必须像 Replace 方法一样搜索字符串,如果 Replace 方法没有找到任何要转义的内容,那么我认为它不应该比 Contains 方法花费的时间更长细绳。但是,如果有一个字符要转义,那么您必须在字符串中搜索两次。

4

7 回答 7

4

一些基本的性能测试,每进行 10 000 000 次(大约 30 个字符的字符串,中间有一个引号),给出:

Contains("\"")          1327 ms.
Contains('"')           2949 ms.
Replace("\"", "\"\"")   2528 m.s

Contains通话大约需要通话时间的一半Replace。我认为寻找一个字符会比寻找一个字符串更快,但令人惊讶的是事实并非如此。

因此,比较首先检查并始终更换以达到收支平衡的情况,其中 x 是Contains调用时间,y 是您需要进行更换的频率:

x + 2xy = 2x

给出:

y = 0.5

这意味着这两种方法的收支平衡在 50% 左右。如果您需要在超过一半的时间内进行替换,则首先检查存在性没有任何好处。

于 2012-08-30T19:09:19.377 回答
1

Andre Calil 的微基准修改:

        List<string> StringList = new List<string>();

        for (int i = 0; i < 10000; i++)
        {
            StringList.Add(DateTime.Now.Ticks + " abc ");
        }

        string temp;

        KeyValuePair<string, string>[] StringsToReplace = new KeyValuePair<string, string>[6];

        // Use array to avoid dictionary access cost
        StringsToReplace[0] = new KeyValuePair<string,string>("1", ".1.");
        StringsToReplace[1] = new KeyValuePair<string,string>("a", "z");
        StringsToReplace[2] = new KeyValuePair<string,string>("b", "x");
        StringsToReplace[3] = new KeyValuePair<string,string>("c", "v");
        StringsToReplace[4] = new KeyValuePair<string,string>("d", "u");
        StringsToReplace[5] = new KeyValuePair<string,string>("e", "t");

        int TotalIterations = 100;
        Stopwatch stopWatch1 = new Stopwatch();
        Stopwatch stopWatch2 = new Stopwatch();

        GC.Collect(); // remove influence of garbage objects

        for (int j = 0; j <= TotalIterations; j++)
        {
            stopWatch1.Start(); // StopWatch Start/Stop does its own accumation

            for (int i = 0; i < StringList.Count; i++)
            {
                for (int k = 0; k < StringsToReplace.Length; k++)
                {
                    temp = StringList[i].Replace(StringsToReplace[k].Value, StringsToReplace[k].Key);
                }
            }

            stopWatch1.Stop();

            stopWatch2.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                for (int k = 0; k < StringsToReplace.Length; k++)
                {
                    if (StringList[i].Contains(StringsToReplace[k].Value))
                        temp = StringList[i].Replace(StringsToReplace[k].Value, StringsToReplace[k].Key);
                }
            }

            stopWatch2.Stop();

            if (j == 0) // discard first run, warm only
            {
                stopWatch1.Reset();
                stopWatch2.Reset();
            }
        }

        // Elapsed.TotalMilliseconds return in double, more accurate than ElapsedMilliseconds
        Console.WriteLine("Replace           : {0:N3} ms", stopWatch1.Elapsed.TotalMilliseconds / TotalIterations);
        Console.WriteLine("Contains > Replace: {0:N3} ms", stopWatch2.Elapsed.TotalMilliseconds / TotalIterations);

结果支持直接调用 Replace,因为 50% 的时间需要真正的替换:

Replace           : 7.453 ms
Contains > Replace: 8.381 ms
于 2012-08-31T00:08:01.150 回答
0

如果有人好奇(你不是,相信我),找出需要替换 OP 方法以提高效率的字符串的必要百分比的等式(大致)是这样的:

是的,没错。

在哪里:

C = Time it takes to create a new string
R = average Ratio of Delimiter position to string length
n = average string length
p = average position of the delimiter (quote in this case) in the string
t = time it takes to search one character of a string

因此,如果您在其中包含引号的字符串的百分比大于此 - 无论如何,请使用第一种方法。

这并没有考虑到我认为可以忽略不计的变量,例如返回时间、字符的实际替换(尽管您确实可以将其包含在 中C),或者我不知道的任何类型的编译器优化。

附带说明:我只花了半个小时解决世界上最没用的方程式,这就是我为什么从数学转向 CS 的一个完美例子。

于 2012-08-30T18:56:07.643 回答
0

新版本:

        string test = "A quick brown fox jumps over a lazy dog.";

        int count = 1000 * 1000;

        Stopwatch watch = new Stopwatch();

        for (int i = 0; i < 4; i++)
        {
            string result = String.Empty;

            watch.Restart();

            for (int c = 0; c < count; c++)
            {
                switch (i)
                {
                    case 0: // warmup
                        break;

                    case 1:
                        if (test.Contains("\""))
                        {
                            result = test.Replace("\"", "\"\"");
                        }
                        break;

                    case 2:
                        result = test.Replace("\"", "\"\"");
                        break;

                    case 3:
                        if (test.IndexOf('\"') >= 0)
                        {
                            result = test.Replace("\"", "\"\"");
                        }
                        break;
                }
            }

            watch.Stop();

            Console.WriteLine("Test {0,16} {1,7:N3} ms {2}", 
                new string[]{"Base","Contains-Replace","Replace","IndexOf-Replace"}[i],
                watch.Elapsed.TotalMilliseconds,
                result);

结果:

Test             Base   3.026 ms
Test Contains-Replace 284.780 ms
Test          Replace 214.503 ms
Test  IndexOf-Replace  64.447 ms

所以 Contains(string) 本身很慢。原因是由于 NLS(自然语言处理 API。但 IndexOf(char) 快得多。

于 2012-08-30T22:23:29.773 回答
0

这取决于需要修复的字符串与所有字符串的比率。如果必须修复很少的字符串,这肯定是一个胜利。尽管如果没有找到任何匹配项,Replace 肯定不会分配新字符串,但胜利将非常小。这是一个常见的优化。

但是,无法预测收支平衡点。措施。

于 2012-08-30T18:45:41.117 回答
0

与所有与性能相关的问题一样,答案是measure

这取决于智能Replace程度(它是否“提前”分配任何内存?如果是,那么性能损失有多大?),需要替换的字符串与您处理的字符串总数的比率,长度的字符串,第一个索引"位于(Contains如果它可以更快返回,则true更快)等。

于 2012-08-30T18:48:04.537 回答
0

String.Replace

此方法执行序数(区分大小写和不区分区域性)搜索以查找 oldValue。

String.Contains

此方法执行序数(区分大小写和不区分区域性)比较。搜索从该字符串的第一个字符位置开始,一直到最后一个字符位置。

所以,我会说没有实际的区别。不过,我将在这里进行微基准测试。


微基准测试(伙计,我喜欢这个)

编码:

        List<string> StringList = new List<string>();

        for (int i = 0; i < 10000; i++)
        {
            StringList.Add(DateTime.Now.Ticks + " abc ");
        }

        string temp;

        Dictionary<string, string> StringsToReplace = new Dictionary<string, string>();
        StringsToReplace.Add("1", ".1.");
        StringsToReplace.Add("a", "z");
        StringsToReplace.Add("b", "x");
        StringsToReplace.Add("c", "v");
        StringsToReplace.Add("d", "u");
        StringsToReplace.Add("e", "t");

        long ReplaceElapsedTime = 0;
        long ContainsReplaceElapsedTime = 0;

        int TotalIterations = 10000;

        for (int j = 0; j < TotalIterations; j++)
        {
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                foreach (KeyValuePair<string, string> CurrentPair in StringsToReplace)
                {
                    temp = StringList[i].Replace(CurrentPair.Value, CurrentPair.Key);
                }
            }

            stopWatch.Stop();

            ReplaceElapsedTime += stopWatch.ElapsedMilliseconds;

            stopWatch.Reset();
            stopWatch.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                foreach (KeyValuePair<string, string> CurrentPair in StringsToReplace)
                {
                    if (StringList[i].Contains(CurrentPair.Value))
                        temp = StringList[i].Replace(CurrentPair.Value, CurrentPair.Key);
                }
            }

            stopWatch.Stop();

            ContainsReplaceElapsedTime += stopWatch.ElapsedMilliseconds;
        }

        Console.WriteLine(string.Format("Replace: {0} ms", ReplaceElapsedTime/TotalIterations));
        Console.WriteLine(string.Format("Contains > Replace: {0} ms", ContainsReplaceElapsedTime/TotalIterations));

        Console.ReadLine();

结果:

Replace: 14 ms 
Contains > Replace: 14 ms

=)

于 2012-08-30T18:48:45.190 回答