Какой самый быстрый, без учета регистра, способ увидеть, содержит ли строка другую строку в C #? - PullRequest
11 голосов
/ 14 октября 2011

РЕДАКТИРОВАТЬ 2:

Подтвердил, что мои проблемы с производительностью были вызваны статическим вызовом функции класса StringExtensions. После удаления метод IndexOf действительно является самым быстрым способом достижения этой цели.

Какой самый быстрый, без учета регистра способ увидеть, содержит ли строка другую строку в C #? Я вижу приемлемое решение для поста здесь Без учета регистра 'Contains (string)' , но я провел предварительный сравнительный анализ, и кажется, что использование этого метода приводит к более медленным вызовам на более крупные строки на несколько порядков (> 100 символов) всякий раз, когда тестовая строка не может быть найдена.

Вот методы, которые я знаю:

IndexOf:

public static bool Contains(this string source, string toCheck, StringComparison comp)
{
    if (string.IsNullOrEmpty(toCheck) || string.IsNullOrEmpty(source))
        return false;

    return source.IndexOf(toCheck, comp) >= 0;
} 

ToUpper:

source.ToUpper().Contains(toCheck.ToUpper());

Regex:

bool contains = Regex.Match("StRiNG to search", "string", RegexOptions.IgnoreCase).Success;

Итак, мой вопрос: какой действительно самый быстрый путь в среднем и почему так?

EDIT:

Вот мое простое тестовое приложение, которое я использовал, чтобы подчеркнуть разницу в производительности. Используя это, я вижу 16 мс для ToLower (), 18 мс для ToUpper и 140 мс для StringExtensions.Contains ():

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace ScratchConsole
{
    class Program
    {
    static void Main(string[] args)
    {
        string input = "";
        while (input != "exit")
        {
            RunTest();
            input = Console.ReadLine();
        }
    }

    static void RunTest()
    {
        List<string> s = new List<string>();
        string containsString = "1";
        bool found;
        DateTime now;
        for (int i = 0; i < 50000; i++)
        {
            s.Add("AAAAAAAAAAAAAAAA AAAAAAAAAAAA");
        }

        now = DateTime.Now;
        foreach (string st in s)
        {
            found = st.ToLower().Contains(containsString);
        }
        Console.WriteLine("ToLower(): " + (DateTime.Now - now).TotalMilliseconds);

        now = DateTime.Now;
        foreach (string st in s)
        {
            found = st.ToUpper().Contains(containsString);
        }
        Console.WriteLine("ToUpper(): " + (DateTime.Now - now).TotalMilliseconds);


        now = DateTime.Now;
        foreach (string st in s)
        {
            found = StringExtensions.Contains(st, containsString, StringComparison.OrdinalIgnoreCase);
        }
        Console.WriteLine("StringExtensions.Contains(): " + (DateTime.Now - now).TotalMilliseconds);

    }
}

public static class StringExtensions
{
    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
        return source.IndexOf(toCheck, comp) >= 0;
    }
}

}

Ответы [ 3 ]

15 голосов
/ 14 октября 2011

Так как на самом деле ToUpper приведет к созданию новой строки, StringComparison.OrdinalIgnoreCase будет быстрее, кроме того, регулярное выражение имеет много накладных расходов для простого сравнения, подобного этому.Тем не менее, String.IndexOf (String, StringComparison.OrdinalIgnoreCase) должен быть самым быстрым, поскольку он не включает создание новых строк.

Я бы предположил (я снова здесь), что RegEx имеет лучший худший случай из-за того, как он оценивает строку, IndexOf всегда будет выполнять линейный поиск, я предполагаю (и снова), что RegEx используетчто-то немного лучше.RegEx также должен иметь лучший вариант, который, вероятно, будет близок, хотя и не так хорош, как IndexOf (из-за дополнительной сложности в его языке).

15,000 length string, 10,000 loop

00:00:00.0156251 IndexOf-OrdinalIgnoreCase
00:00:00.1093757 RegEx-IgnoreCase 
00:00:00.9531311 IndexOf-ToUpper 
00:00:00.9531311 IndexOf-ToLower

Placement in the string also makes a huge difference:

At start:
00:00:00.6250040 Match
00:00:00.0156251 IndexOf
00:00:00.9687562 ToUpper
00:00:01.0000064 ToLower

At End:
00:00:00.5781287 Match
00:00:01.0468817 IndexOf
00:00:01.4062590 ToUpper
00:00:01.4218841 ToLower

Not Found:
00:00:00.5625036 Match
00:00:01.0000064 IndexOf
00:00:01.3750088 ToUpper
00:00:01.3906339 ToLower
1 голос
/ 14 октября 2011

Я обнаружил, что скомпилированный RegEx на сегодняшний день является самым быстрым решением и, очевидно, гораздо более универсальным. Компиляция помогает сравнить его с сравнением строк меньшего размера, и, как вы сказали, сравнение с более крупными строками отсутствует.

http://www.dijksterhuis.org/regular-expressions-advanced/ содержит некоторые подсказки для получения максимальной скорости из сравнений RegEx; Вы можете найти это полезным.

0 голосов
/ 14 октября 2011

Это был интересный вопрос для меня, поэтому я создал небольшой тест, используя разные методы

string content = "";
            for (var i = 0; i < 10000; i++)
                content = String.Format("{0} asldkfjalskdfjlaskdfjalskdfj laksdf lkwiuirh 9238 r9849r8 49834", content);

            string test = String.Format("{0} find_me {0}", content);

            string search = test;

            var tickStart = DateTime.Now.Ticks;
            //6ms
            //var b = search.ToUpper().Contains("find_me".ToUpper());

            //2ms
            //Match m = Regex.Match(search, "find_me", RegexOptions.IgnoreCase);


            //a little bit over 1ms
            var c = false;
            if (search.Length == search.ToUpper().Replace("find_me".ToUpper(), "x").Length)
                c = true;
            var tickEnd = DateTime.Now.Ticks;
            Debug.Write(String.Format("{0} {1}", tickStart, tickEnd));

Итак, я сделал, создал строку и искал в ней

первый методsearch.ToUpper().Contains("find_me".ToUpper()) 5 мс

второй метод Match m = Regex.Match(search, "find_me", RegexOptions.IgnoreCase) 2 мс

третий метод

if (search.Length == search.ToUpper().Replace("find_me".ToUpper(), "x").Length)
                c = true;

потребовалось чуть более 1 мс

...