Какой метод поиска строки является наиболее эффективным (по времени чтения)? (C#)

Я обнаружил, что моя программа просматривает множество длинных строк (более 20 000), пытаясь найти конкретную уникальную фразу.

Каков наиболее эффективный способ сделать это в C#?

Ниже представлен текущий код, который работает следующим образом:

  1. Поиск начинается с startPos, потому что целевая область несколько удалена от начала.
  2. Он просматривает строку и на каждом шаге проверяет, начинается ли подстрока с этой точки с startMatchString, что является индикатором того, что начало целевой строки было найдено. (Длина целевой строки варьируется).
  3. Отсюда он создает новую подстроку (отсекая 11 символов, обозначающих начало целевой строки) и ищет endMatchString

Я уже знаю, что это ужасно сложный и, возможно, очень неэффективный алгоритм. Как лучше достичь того же результата?

string result = string.Empty;    
for (int i = startPos; i <= response.Length - 1; i++)
{
   if (response.Substring(i).StartsWith(startMatchString))
   {
       string result = response.Substring(i).Substring(11);
       for (int j = 0; j <= result.Length - 1; j++)
       {
           if (result.Substring(j).StartsWith(endMatchString))
           {
               return result.Remove(j)
           }
       }
   }
}
return result;

Ответов (8)

Решение

Вы можете использовать String.IndexOf, но убедитесь, что используете StringComparison.Ordinal, иначе это может быть на порядок медленнее.

private string Search2(int startPos, string startMatchString, string endMatchString, string response) {
    int startMarch = response.IndexOf(startMatchString, startPos, StringComparison.Ordinal);
    if (startMarch != -1) {
        startMarch += startMatchString.Length;
        int endMatch = response.IndexOf(endMatchString, startMarch, StringComparison.Ordinal);
        if (endMatch != -1) { return response.Substring(startMarch, endMatch - startMarch); }
    }
    return string.Empty;
}

Поиск 1000 раз в строке примерно в 40% файла размером 183 КБ занял около 270 миллисекунд. Без StringComparison.Ordinal это заняло около 2000 миллисекунд.
Один поиск с помощью вашего метода занял более 60 секунд, поскольку он создает новую строку (O (n)) на каждой итерации, что делает ваш метод O (n ^ 2).

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

Для очень длинных строк нельзя превзойти алгоритм поиска Бойера-Мура. Это сложнее, чем я мог бы попытаться объяснить здесь, но на сайте CodeProject есть довольно хорошая статья по этому поводу.

Это зависит от того, что вы пытаетесь найти в строке. Если вы ищете конкретную последовательность, IndexOf/Contains это быстро, но если вы ищете шаблоны с подстановочными знаками, Regex он оптимизирован для этого вида поиска.

Есть целая куча алгоритмов,

  • Бойер и Мур
  • Воскресенье
  • Кнут-Моррис-Пратт
  • Рабин-Карп

Я бы рекомендовал использовать упрощенный метод Бойера-Мура, который называется Бойер-Мур-Хорспул.

C-код есть в википедии. Для кода Java посмотрите

http://www.fmi.uni-sofia.bg/fmi/logic/vboutchkova/sources/BoyerMoore_java.html

Хорошая статья об этом доступна по адресу http://www.ibm.com/developerworks/java/library/j-text-searching.html.

Если вы хотите использовать встроенные средства, используйте регулярные выражения.

Вы можете использовать регулярное выражение; он оптимизирован для такого рода поиска и манипуляций.

Вы также можете попробовать IndexOf ...

string result = string.Empty;

if (startPos >= response.Length) 
    return result;

int startingIndex = response.IndexOf(startMatchString, startPos);
int rightOfStartIndex = startingIndex + startMatchString.Length;

if (startingIndex > -1 && rightOfStartIndex < response.Length)
{
    int endingIndex = response.IndexOf(endMatchString, rightOfStartIndex);
    if (endingIndex > -1)
        result = response.Substring(rightOfStartIndex, endingIndex - rightOfStartIndex);
}

return result;

Как было сказано ранее, регулярное выражение - ваш друг. Вы можете посмотреть RegularExpressions.Group. Таким образом вы можете назвать часть согласованного набора результатов.

Вот пример

Вот пример использования IndexOf ( осторожно : написано с головы до ног, не тестировал):

int skip = 11;
int start = response.IndexOf(startMatchString, startPos);
if (start >= 0)
{
   int end = response.IndexOf(startMatchString, start + skip);
   if (end >= 0)
       return response.Substring(start + skip, end - start - skip);
   else
       return response.Substring(start + skip);
}
return string.Empty;