Regex: я хочу это и это и это ... в любом порядке - PullRequest
42 голосов
/ 20 августа 2010

Я даже не уверен, возможно ли это или нет, но вот что я хотел бы.

String: "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870"

У меня есть текстовое поле, в котором я набираю параметры поиска, и они разделены пробелом,Из-за этого я хочу вернуть совпадение, если строка1 находится в строке, а затем строка2 находится в строке, ИЛИ строка2 находится в строке, а затем строка1 находится в строке.Мне все равно, в каком порядке находятся строки, но они ВСЕ (будет что-то больше 2) должны быть в строке.

Так, например, в предоставленной строке я хотел бы:

"FEB Low"

или

"Low FEB"

... для возврата в качестве совпадения.

Я ДЕЙСТВИТЕЛЬНО новичок в регулярных выражениях, здесь можно прочитать только некоторые учебники по здесь но это было некоторое время назад, и мне нужно сделать это сегодня.В понедельник я начинаю новый проект, который намного важнее и не может быть отвлечен этим вопросом.Есть ли способ сделать это с помощью регулярных выражений, или мне нужно перебирать каждую часть поискового фильтра и переставлять порядок?Любая помощь очень ценится.Спасибо.

ОБНОВЛЕНИЕ: причина, по которой я не хочу перебирать цикл и ищу наилучшую производительность, заключается в том, что, к сожалению, используемый мной dataTable вызывает эту функцию при каждом нажатии клавиши, и яне хочу, чтобы это застопорилось.

ОБНОВЛЕНИЕ: Спасибо всем за вашу помощь, это было высоко оценено.

ОБНОВЛЕНИЕ КОДА:

В конечном счете, это то, что япошло с.

string sSearch = nvc["sSearch"].ToString().Replace(" ", ")(?=.*");
if (sSearch != null && sSearch != "")
{
  Regex r = new Regex("^(?=.*" + sSearch + ").*$", RegexOptions.IgnoreCase);
  _AdminList = _AdminList.Where<IPB>(
                                       delegate(IPB ipb)
                                       {
                                          //Concatenated all elements of IPB into a string
                                          bool returnValue = r.IsMatch(strTest); //strTest is the concatenated string
                                          return returnValue;
                                    }).ToList<IPB>();
                                       }
}

В классе IPB есть X элементов, и ни в одной таблице на всем сайте, над которой я работаю, столбцы не расположены в одинаковом порядке.Поэтому мне нужно было искать любые заказы, и я не хотел писать много кода для этого.Здесь были и другие хорошие идеи, но я знаю, что моему боссу действительно нравится Regex (проповедует их), и поэтому я подумал, что было бы лучше, если бы я пошел с этим сейчас.Если по какой-либо причине производительность сайта снизится (интранет-сайт), я попробую другой путь.Спасибо всем.

Ответы [ 7 ]

97 голосов
/ 20 августа 2010

Вы можете использовать (?=…) позитивный прогноз ; он утверждает, что данный шаблон может быть сопоставлен. Вы привязываетесь к началу строки, и один за другим, в любом порядке, ищите совпадения для каждого из ваших паттернов.

Это будет выглядеть примерно так:

^(?=.*one)(?=.*two)(?=.*three).*$

Это будет соответствовать строке, содержащей "one", "two", "three", в любом порядке (, как видно на rubular.com ).

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

Это не самое эффективное решение проблемы. Лучшее решение - разобрать слова в вводе и поместить их в эффективное представление набора и т. Д.

Похожие вопросы


Более практичный пример: проверка пароля

Допустим, мы хотим, чтобы наш пароль:

  • Содержит от 8 до 15 символов
  • Должен содержать заглавную букву
  • Должен содержать строчную букву
  • Должен содержать цифру
  • Должен содержать один из специальных символов

Тогда мы можем написать регулярное выражение, подобное этому:

^(?=.{8,15}$)(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*]).*$
 \__________/\_________/\_________/\_________/\______________/
    length      upper      lower      digit        symbol
4 голосов
/ 20 августа 2010

Почему бы просто не выполнить простую проверку текста, поскольку порядок не имеет значения?

string test = "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870";
test = test.ToUpper();
bool match = ((test.IndexOf("FEB") >= 0) && (test.IndexOf("LOW") >= 0));

Вам нужно использовать регулярные выражения?

3 голосов
/ 20 августа 2010

Я думаю, что на сегодня наиболее целесообразным будет string.Split(' ') условия поиска, а затем перебрать результаты, подтверждающие, что sourceString.Contains(searchTerm)

var source = @"NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870".ToLowerInvariant();
var search = "FEB Low";

var terms = search.Split(' ');

bool all_match = !terms.Any(term => !(source.Contains(term.ToLowerInvariant())));

Обратите внимание, что мы используем Any() для настройки короткого замыкания, поэтому, если первый член не соответствует, мы пропускаем проверку второго, третьего и т. Д.


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

Вы указали в некоторых комментариях, что хотите избежать цикла, но RegEx - это не однопроходное решение. Нетрудно создать ужасно бесполезные поиски, которые зацикливаются и пошагово посимвольно, например, печально известный катастрофический возврат , где очень простое совпадение занимает тысячи шагов, чтобы вернуть false.

2 голосов
/ 06 ноября 2016

Ответ @polygenelubricants является одновременно и полным, и идеальным, но у меня был случай, когда я хотел сопоставить дату и что-то еще, например. 10-значное число, поэтому заголовок не совпадает, и я не могу сделать это только с заголовками, поэтому я использовал именованные группы:

(?:.*(?P<1>[0-9]{10}).*(?P<2>2[0-9]{3}-(?:0?[0-9]|1[0-2])-(?:[0-2]?[0-9]|3[0-1])).*)+

и таким образом, число всегда является группой 1, а дата - всегда группой 2. Конечно, у нее есть несколько недостатков, но это было очень полезно для меня, и я просто подумал, что должен поделиться этим! (посмотрите https://www.debuggex.com/r/YULCcpn8XtysHfmE)

2 голосов
/ 20 августа 2010
var text = @"NS306Low FEBRUARY 2FEB0078/9/201013B1-9-1Low31 AUGUST 19870";   
var matches = Regex.Matches(text, @"(FEB)|(Low)");
foreach (Match match in matches)
{
   Console.WriteLine(match.Value);
}

Output:
Low
FEB
FEB
Low

Должны ли вы начать.

0 голосов
/ 20 августа 2010

Использовать string.Split (). Он вернет массив подстрок, которые разделены указанной строкой / символом. Код будет выглядеть примерно так:

int maximumSize = 100;<br> string myString = "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870";<br> string[] individualString = myString.Split(' ', maximumSize);<br>

Для получения дополнительной информации http://msdn.microsoft.com/en-us/library/system.string.split.aspx

Edit: Если вы действительно хотите использовать регулярные выражения, этот шаблон будет работать. [^ ]* И вы просто будете использовать Regex.Matches (); Код будет примерно таким:

string myString = "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870";<br> string pattern = "[^ ]*"; Regex rgx = new Regex(pattern);<br> foreach(Match match in reg.Matches(s))<br> {<br> //do stuff with match.value<br> }

0 голосов
/ 20 августа 2010

Вам не нужно проверять каждую перестановку, просто разбейте результаты поиска на несколько частей «FEB» и «Low» и убедитесь, что каждая часть соответствует. Это будет намного проще, чем пытаться придумать регулярное выражение, которое полностью соответствует всему этому (что, я уверен, теоретически возможно, но, вероятно, не практично в действительности).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...