Как сохранить разделители при разбиении строки Ruby?

У меня есть текст вроде:

content = "Do you like to code? How I love to code! I'm always coding." 

Я пытаюсь разбить его на ? или . или !:

content.split(/[?.!]/)

Когда я распечатываю результаты, ограничители препинания отсутствуют.

Вам нравится код

Как я люблю код

Я всегда кодирую

Как я могу сохранить пунктуацию?

Ответ 1

Ответ

Используйте положительное выражение lookbehind (т.е. ?<=) внутри группы захвата скобок, чтобы сохранить разделитель в конце каждой строки:

content.split(/(?<=[?.!])/)

# Returns an array with:
# ["Do you like to code?", " How I love to code!", " I'm always coding."]

Это оставляет пробел в начале второй и третьей строк. Добавьте совпадение для нуля или более пробелов (\s*) после группы захвата, чтобы исключить его:

content.split(/(?<=[?.!])\s*/)

# Returns an array with:
# ["Do you like to code?", "How I love to code!", "I'm always coding."]

Дополнительные примечания

Хотя это не имеет смысла в вашем примере, разделитель можно смещать в начало строк, начиная со второго. Это делается с помощью позитивного регулярного выражения (т.е. ?=). Ради кого-либо, кто ищет эту технику, вот как это сделать:

content.split(/(?=[?.!])/)

# Returns an array with:
# ["Do you like to code", "? How I love to code", "! I'm always coding", "."]

Лучшим примером для иллюстрации поведения является:

content = "- the - quick brown - fox jumps"
content.split(/(?=-)/)

# Returns an array with:
# ["- the ", "- quick brown ", "- fox jumps"]

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

Ответ 2

Чтобы ответить на заголовок вопроса, добавление группы захвата в ваше разделенное регулярное выражение сохранит разделительные разделители:

"Do you like to code? How I love to code! I'm always coding.".split /([?!.])/
  => ["Do you like to code", "?", " How I love to code", "!", " I'm always coding", "."]

Оттуда довольно просто восстановить предложения (или сделать другое массирование, поскольку проблема вызывает его):

s.split(/([?!.])/).each_slice(2).map(&:join).map(&:strip)
 => ["Do you like to code?", "How I love to code!", "I'm always coding."]

Регулярные выражения, приведенные в других ответах, более четко выполняют тело вопроса.

Ответ 3

Используйте partition. Пример из документации:

"hello".partition("l")         #=> ["he", "l", "lo"]

Ответ 4

Я бы использовал что-то вроде:

content.scan(/.+?[?!.]/)
# => ["Do you like to code?", " How I love to code!", " I'm always coding."]

Если вы хотите избавиться от промежуточных пространств, используйте:

content.scan(/.+?[?!.]/).map(&:lstrip)
# => ["Do you like to code?", "How I love to code!", "I'm always coding."]

Ответ 5

Самый надежный способ сделать это - с библиотекой обработки естественного языка: Rails gem, чтобы разбить абзац на ряд предложений

Вы также можете разделить по группам:

@content.split(/(\?+)|(\.+)|(!+)/)

После разделения на группы вы можете присоединиться к предложению и разделителю.

@content.split(/(\?+)|(\.+)|(!+)/).each_slice(2) {|slice| puts slice.join}