RegEx для сопоставления триграмм

Я пытаюсь получить все группы из трех слов из строки, которая может состоять из нескольких предложений, не выходя за границы предложений. У меня это работает для слов, которые имеют только стандартные буквы алфавита:

preg_match_all("/(?=(\b(\w+)(?:\s+(\w+)\b|$)(?:\s+(\w+)\b|$)))/",$utext,$matches);
print_r($matches[1]);

Но он падает там, где есть апострофы или дефисы. Итак, с этим образцом текста:

The quick brown fox's feet jumped over the lazy dog. The rain falls head-first in the plain.

Я хочу этот список:

  • Быстрый коричневый
  • быстрая коричневая лиса
  • лапы коричневой лисы
  • лисьи лапы подскочили
  • ноги перепрыгнули
  • перепрыгнул через
  • над ленивым
  • ленивая собака
  • Дождь падает
  • дождь падает с головой
  • падает головой вперед в
  • головой вперед в
  • на равнине

Я пытался использовать [\w'-] для каждого \w выше, но это дает некоторые странности:

Array ( [0] => The quick brown [1] => quick brown fox's [2] => brown fox's feet [3] => fox's feet jumped [4] => 's feet jumped [5] => s feet jumped [6] => feet jumped over [7] => jumped over the [8] => over the lazy [9] => the lazy dog [10] => The rain falls [11] => rain falls head-first [12] => falls head-first in [13] => head-first in the [14] => -first in the [15] => first in the [16] => in the plain )

Что мне не хватает? Спасибо.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
0
295
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Просто замените \w на [^\s.] (не пробел и не точку) и удалите слово boudaries. Другое изменение заключается в добавлении чередования «начало строки ИЛИ пробел» в начале регулярного выражения:

$text = "The quick brown fox's feet jumped over the lazy dog. The rain falls head-first in the plain.";

preg_match_all("/(?=((?<=^|\s)[^\s.]+(?:\s+[^\s.]+|$)(?:\s+[^\s.]+|$)))/",$text,$matches);
print_r($matches[1]);

Выход:

Array
(
    [0] => The quick brown
    [1] => quick brown fox's
    [2] => brown fox's feet
    [3] => fox's feet jumped
    [4] => feet jumped over
    [5] => jumped over the
    [6] => over the lazy
    [7] => the lazy dog
    [8] => The rain falls
    [9] => rain falls head-first
    [10] => falls head-first in
    [11] => head-first in the
    [12] => in the plain
)

Объяснение регулярного выражения:

(?=                     # lookahead
    (                   # start group 1
        (?<=^|\s)       # lookbehind, make sure we have beginning of line or space before
        [^\s.]+         # 1 or more non space, non dot
        (?:             # non capture group
            \s+         # 1 or more spaces
            [^\s.]+     # 1 or more non space, non dot
          |             # OR
            $           # end of line
        )               # end group
        (?:             # non capture group
            \s+         # 1 or more spaces
            [^\s.]+     # 1 or more non space, non dot
          |             # OR
            $           # end of line
        )               # end group
    )                   # end group 1
)                       # end lookahead

Изменить в соответствии с комментарием.

$text = "The quick brown fox's feet jumped over the lazy dog. The rain falls head-first in the plain. 'This is a quote,' I say, and that's that.";

preg_match_all("/(?=((?<=^|\s|')(?:(?<=[a-zA-Z])'(?=[a-zA-Z])|[^\s.,'])+(?:\s+(?:(?<=[a-zA-Z])'(?=[a-zA-Z])|[^\s.,'])+|$){2}))/",$text,$matches);
print_r($matches[1]);

Выход:

Array
(
    [0] => The quick brown
    [1] => quick brown fox's
    [2] => brown fox's feet
    [3] => fox's feet jumped
    [4] => s feet jumped
    [5] => feet jumped over
    [6] => jumped over the
    [7] => over the lazy
    [8] => the lazy dog
    [9] => The rain falls
    [10] => rain falls head-first
    [11] => falls head-first in
    [12] => head-first in the
    [13] => in the plain
    [14] => This is a
    [15] => is a quote
    [16] => and that's that
)

Объяснение регулярного выражения:

(?=                             # lookahead
    (                           # start group 1
        (?<=^|\s|')             # lookbehind, make sure we have beginning of line or space or quote before
        (?:                     # start non capture group
            (?<=[a-zA-Z])       # lookbehind, make sure we have a letter before
            '                   # a single quote
            (?=[a-zA-Z])        # lookahead, make sure we have a letter after
          |                     # OR
            [^\s.,']            # not a space or dot or comma or single quote
        )+                      # group may appear 1 or more times
        (?:                     # non capture group
            \s+                 # 1 or more spaces
            (?:                 # non capture group
                (?<=[a-zA-Z])   # lookbehind, make sure we have a letter before
                '               # a single quote
                (?=[a-zA-Z])    # lookahead, make sure we have a letter after
              |                 # OR
                [^\s.,']        # not a space or dot or comma or single quote
            )+                  # group may appear 1 or more times
          |                     # OR
            $                   # end of line
        ){2}                    # end group, must appear twice
    )                           # end group 1
)                               # end lookahead

Это гениально - спасибо. Теперь я вижу, что он борется с кавычками и запятыми, например, в 'This is a quote,' I say, and that's that. Но, возможно, я прошу здесь слишком многого!

thehattery 30.05.2019 19:08

Во втором примере законными триграммами были бы: Это а, это цитата и все (потому что я говорю, что предложение слишком короткое, чтобы его можно было определить). Я предполагаю, что это, возможно, больше подходит для НЛП, чем для регулярного выражения.

thehattery 30.05.2019 19:25

@thehattery: Вы можете добавить запятую в класс символов, но для одинарной кавычки это немного сложнее, потому что вы хотите сохранить кавычку в fox's. Попробуйте заменить [^\s.]+ на (?:(?<=[a-zA-Z])'(?=[a-zA-Z]|[^\s.,'])+

Toto 30.05.2019 19:26

отсутствует ) где-то?

thehattery 30.05.2019 19:31

Извините, (?:(?<=[a-zA-Z])'(?=[a-zA-Z])|[^\s.,'])+

Toto 30.05.2019 19:33

Это гениально - спасибо! Он пропускает только This is a во втором примере, но отлично получает все остальное в обоих примерах.

thehattery 30.05.2019 19:35

@thehattery: Попробуйте: preg_match_all("/(?=((?<=^|\s|')(?:(?<=[a-zA-Z])'(?=[a-zA-Z]‌​)|[^\s.,'])+(?:\s+(?‌​:(?<=[a-zA-Z])'(?=[a‌​-zA-Z])|[^\s.,'])+|$‌​){2}))/",$text,$matc‌​hes);

Toto 30.05.2019 19:41

Другие вопросы по теме