В информатике алгоритм сопоставления подстановочных знаков (также известный как подстановка ) полезен при сравнении текстовых строк, которые могут содержать синтаксис подстановочных знаков . [1] Обычно эти алгоритмы используются в интерфейсах командной строки , например, в оболочке Bourne [2] или командной строке Microsoft Windows [3] или текстовом редакторе или файловом менеджере, а также в интерфейсах некоторых поисковых систем [4] и баз данных. [5] Сопоставление подстановочных знаков является подмножеством проблемы сопоставления регулярных выражений и сопоставления строк в целом. [6]
Подстановочный шаблон проверяет подстановочный шаблон p на входной строке s . Он выполняет якорное сопоставление, возвращает true только тогда, когда p соответствует всему s .
Шаблон может быть основан на любом общем синтаксисе (см. подстановку ), но программисты Windows склонны обсуждать только упрощенный синтаксис, поддерживаемый собственной средой выполнения C: [7] [8]
?
соответствует ровно одному вхождению любого символа. *
соответствует произвольному количеству (включая ноль) вхождений любого символа.В данной статье в основном обсуждается формулировка проблемы для Windows, если не указано иное.
Задачу сопоставления с помощью индексов, начинающихся с нуля, можно рекурсивно определить следующим образом:
где m ij — результат сопоставления шаблона p с текстом t, усеченным на i и j символах соответственно. Это формулировка, используемая алгоритмом Рихтера и алгоритмом Snippets , найденным в коллекции Кантатора. [9] [10] Это описание похоже на расстояние Левенштейна .
К непосредственно связанным проблемам в информатике относятся:
?
определенного. [11] [12]Ранние алгоритмы для сопоставления подстановочных знаков часто полагались на рекурсию , но эта техника подвергалась критике по соображениям производительности [10] и надежности [8] . Нерекурсивные алгоритмы для сопоставления подстановочных знаков получили признание в свете этих соображений.
Как среди рекурсивных, так и нерекурсивных алгоритмов стратегии выполнения операции сопоставления с образцом сильно различаются, о чем свидетельствует множество примеров алгоритмов, приведенных ниже. Методы разработки тестовых случаев и оптимизации производительности были наглядно применены к определенным алгоритмам, особенно к тем, которые были разработаны критиками рекурсивных алгоритмов.
Рекурсия обычно происходит при сопоставлении, *
когда есть еще суффиксы для сопоставления. Это форма обратного отслеживания , также выполняемая некоторыми сопоставителями регулярных выражений.
[...]
многобайтовые наборы символов):Общая форма этих алгоритмов одинакова. При рекурсии алгоритм разбивает входные данные на подстроки и считает совпадение произошедшим, когда ОДНА из подстрок возвращает положительное совпадение. Для dowild("*X", "abcX")
он жадно вызывает dowild("X", "abcX")
, dowild("X", "bcX")
и . Обычно они отличаются менее важными вещами dowild("X", "cX")
, dowild("X", "X")
такими как поддержка функций, и более важными факторами, такими как незначительные, но очень эффективные оптимизации. Вот некоторые из них:
*
и убедиться, что ОДНА из подстрок возвращает положительное совпадение, время выполнения становится экспоненциальным для отклонения совпадения с большим количеством *
в тексте. Ларс Матисен изменяет возврат на три класса: совпадение, отсутствие совпадения и ABORT (совпадение вообще невозможно для рекурсии со звездочкой). Значение ABORT возвращается, когда текст потребляется слишком рано или когда другое совпадение со звездочкой не удалось, гарантируя линейную производительность по отношению к количеству звездочек. (Общая сложность дополнительно квадратична количеству символов, оставшихся для сопоставления.) [14] Wildmatch ABORT в Git/Rsync также охватывает недопустимые входные данные. [21] Новый INN uwildmat делает то же самое. [22]FNM_PATHNAME
.Алгоритм Мартина Рихтера является исключением из этого шаблона, хотя общая операция эквивалентна. На * он рекурсивно увеличивает любой из индексов, следуя формулировке задачи динамического программирования. Метод "ABORT" также применим к нему. [9] На типичных шаблонах (как протестировал Кантаторе) он медленнее, чем реализации жадного вызова. [10]
Рекурсивные алгоритмы в целом проще для рассуждений, и с модификацией ABORT они работают приемлемо с точки зрения сложности в худшем случае. На строках без * они тратят линейное время на размер строки для сопоставления, поскольку существует фиксированное отношение один к одному.
Критики рекурсивных алгоритмов разработали следующее:
MATCH("da*da*da*", "daaadabadmanda")
) [24]Следующее не является:
MATCH("*?", "xx")
)Итеративные функции выше реализуют возврат, сохраняя старый набор указателей шаблона/текста и возвращаясь к нему в случае неудачного сопоставления. По словам Курта, поскольку требуется только одно успешное сопоставление, необходимо сохранить только один такой набор. [17]
Кроме того, проблема сопоставления с подстановочными знаками может быть преобразована в сопоставление с регулярными выражениями с использованием наивного подхода замены текста . Хотя нерекурсивные сопоставления с регулярными выражениями, такие как конструкция Томпсона, на практике используются реже из-за отсутствия поддержки обратных ссылок, сопоставление с подстановочными знаками в целом не обладает столь же богатым набором функций. (На самом деле, многие из приведенных выше алгоритмов поддерживают только ?
и *
.) Реализация Расса Кокса NFA Томпсона может быть тривиально модифицирована для этого. [26] Алгоритм nrgrep на основе BDM Густаво Наварро обеспечивает более оптимизированную реализацию с упором на эффективные суффиксы. [27] См. также регулярные выражения § Реализации .