разобрать строку поиска - PullRequest
3 голосов
/ 23 марта 2010

У меня есть строки поиска, аналогичные приведенным ниже:

energy food "olympics 2010" Terrorism OR "government" OR cups NOT transport

, и мне нужно проанализировать их с помощью PHP5, чтобы определить, принадлежит ли контент к одному из следующих кластеров:

  • Массив AllWords
  • Массив AnyWords
  • Массив NotWords

Это установленные мной правила:

  1. Если он имеетИЛИ до или после слова или слов в кавычках, если он принадлежит AnyWord.
  2. Если в нем содержится НЕ перед словом или словами в кавычках, он принадлежит NotWords
  3. Если перед 0 или более пробеламиСлово или цитируемая фраза принадлежат AllWords.

Таким образом, конечный результат должен быть примерно таким:

AllWords: (energy, food, "olympics 2010")
AnyWords: (terrorism, "government", cups)
NotWords: (Transport)

Что было бы хорошим способом сделать это?

Ответы [ 2 ]

4 голосов
/ 23 марта 2010

Если вы хотите сделать это с Regex, имейте в виду, что ваш анализ будет прерываться при глупом вводе пользователем (пользователь, а не ввод =)).

Я бы попробовал следующие регулярные выражения.

NotWords:

(?<=NOT\s)\b((?!NOT|OR)\w+|"[^"]+")\b

AllWords:

(?<!OR\s)\b((?!NOT|OR)\w+|"[^"]+")\b(?!\s+OR)

AnyWords: Ну ... остальные.=) Их не так легко определить, так как я не знаю, как поместить выражение «ИЛИ позади него или ИЛИ перед ним» в регулярное выражение.Возможно, вы можете объединить результаты трех регулярных выражений

(?<=OR\s)\b((?!NOT|OR)\w+|"[^"]+")\b(?!\s+OR)
(?<=OR\s)\b((?!NOT|OR)\w+|"[^"]+")\b(?=\s+OR)
(?<!OR\s)\b((?!NOT|OR)\w+|"[^"]+")\b(?=\s+OR)

Задачи: для них требуется ровно один пробел между словами-модификаторами и выражениями.PHP поддерживает только lookbehinds для выражений с фиксированной длиной, так что я не вижу способа обойти это, извините.Вы можете просто использовать \b(\w+|"[^"]+")\b, чтобы разделить входные данные и проанализировать полученный массив вручную.

3 голосов
/ 23 марта 2010

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

public function setUp () {
  $this->searchParser = new App_Search_Parser();
}

public function testSingleWordParsesToAllWords () {
  $this->searchParser->parse('Transport');
  $this->assertEquals(
     $this->searchParser->getAllWords(), 
     array('Transport')
  );
  $this->assertEquals($this->searchParser->getNotWords(), array());
  $this->assertEquals($this->searchParser->getAnyWords());
}

public function testParseOfCombinedSearchString () {
   $query = 'energy food "olympics 2010" Terrorism ' . 
            'OR "government" OR cups NOT transport';
   $this->searchParser->parse($query);

  $this->assertEquals(
     $this->searchParser->getAllWords(), 
     array('energy', 'food', 'olympics 2010')
  );
  $this->assertEquals(
     $this->searchParser->getNotWords(), 
     array('Transport')
  );
  $this->assertEquals(
     $this->searchParser->getAnyWords(),
     array( 'terrorism', 'government', 'cups')
  );
}

Другие хорошие тесты будут включать:

  • testParseTwoWords
  • testParseTwoWordsWithOr
  • testParseSimpleWithNot
  • testParseInvalid
    • Здесь вы должны решить, как выглядит неверный ввод и как вы его интерпретируете, например:
    • «НЕ транспортировать»:Ищите что-нибудь, что не содержит транспорта, или сообщите пользователю, что он должен включить хотя бы один поисковый термин?
    • 'ИЛИ энергия': можно ли начинать с комбинатора?
    • «еда или НЕ энергия»: означает ли это «поиск еды или чего-либо, что не содержит энергии», или это означает «поиск еды, а не энергии», или это ничего не значит?(т.е. выбросить исключение, вернуть false или еще много чего)
  • testParseEmpty

Затем напишите тесты один за другим и напишите простое решение, которое проходиттест.Затем выполните рефакторинг, исправьте его и снова запустите, чтобы убедиться, что вы все еще проходили тест.После того, как тест пройден и код реорганизован, напишите следующий тест и повторите процедуру.Добавляйте больше тестов, когда вы находите особые случаи, и реорганизуйте код, чтобы он прошел все тесты.Если вы прервете тестирование, создадите резервную копию и перепишите код (не тест!) Так, чтобы он прошел.

Что касается того, как вы можете решить эту проблему, посмотрите preg_match , strtok или просто положите цикл по цепочке, добавляя токены по мере продвижения.

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