Разделить строку в список, но сохранить шаблон разделения - PullRequest
14 голосов
/ 05 августа 2011

В настоящее время я разбиваю строку по шаблону, например так:

outcome_array=the_text.split(pattern_to_split_by)

Проблема в том, что сам шаблон, по которому я делюсь, всегда опускается.

Как мне получитьэто включить сам шаблон расщепления?

Ответы [ 3 ]

26 голосов
/ 06 августа 2011

Спасибо Марку Уилкинсу за вдохновение, но вот короткий код для его выполнения:

irb(main):015:0> s = "split on the word on okay?"
=> "split on the word on okay?"
irb(main):016:0> b=[]; s.split(/(on)/).each_slice(2) { |s| b << s.join }; b
=> ["split on", " the word on", " okay?"]

или:

s.split(/(on)/).each_slice(2).map(&:join)

См. Объяснение ниже сгиба.


Вот как это работает.Сначала мы разбиваем слово «on», но заключаем в скобки, чтобы превратить его в группу соответствияКогда в регулярном выражении есть группа совпадений, переданная в split, Ruby включит эту группу в вывод:

s.split(/(on)/)
# => ["split", "on", "the word", "on", "okay?"

Теперь мы хотим объединить каждый экземпляр «on» с предыдущей строкой.each_slice(2) помогает, передавая два элемента одновременно в свой блок.Давайте просто вызовем each_slice(2), чтобы увидеть, какие результаты.Поскольку each_slice при вызове без блока возвращает перечислитель, мы применим to_a к перечислителю, чтобы мы могли видеть, над чем перечислитель будет перечислять:

s.split(/(on)/).each_slice(2).to_a
# => [["split", "on"], ["the word", "on"], ["okay?"]]

Мы получаемблизко.Теперь все, что нам нужно сделать, это соединить слова вместе.И это приводит нас к полному решению выше.Я разверну его в отдельные строки, чтобы было легче следовать:

b = []
s.split(/(on)/).each_slice(2) do |s|
  b << s.join
end
b
# => ["split on", "the word on" "okay?"]

Но есть отличный способ убрать временный b и значительно сократить код:

s.split(/(on)/).each_slice(2).map do |a|
  a.join
end

map передает каждый элемент своего входного массива в блок;результат блока становится новым элементом в этой позиции в выходном массиве.В MRI> = 1.8.7 вы можете сократить его еще больше до эквивалента:

s.split(/(on)/).each_slice(2).map(&:join)
6 голосов
/ 02 сентября 2014

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

s = "split on the word on okay?"
s.split(/(?<=on)/)
=> ["split on", " the word on", " okay?"]

Или положительное прогнозирование для разделения непосредственно перед включением:

s = "split on the word on okay?"
s.split(/(?=on)/)
=> ["split ", "on the word ", "on okay?"]

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

"don't split on assertion".split(/(?<=\bon\b)\s*/)
=> ["don't split on", "assertion"]
4 голосов
/ 05 августа 2011

Если вы используете шаблон с группами, он также возвращает шаблон в результатах:

irb(main):007:0> "split it here and here okay".split(/ (here) /)
=> ["split it", "here", "and", "here", "okay"]

Редактировать В дополнительной информации указывалось, что цель состоит в том, чтобы включить элемент, на который он был разделен, одной из половин разделенных элементов. Я думаю, что есть простой способ сделать это, но я этого не знаю и сегодня не успел поиграть с этим. Таким образом, в отсутствие умного решения, следующий способ - это грубая сила. Используйте метод split, как описано выше, чтобы включить элементы разбиения в массив. Затем выполните итерацию по массиву и объедините каждую вторую запись (которая по определению является значением разделения) с предыдущей записью.

s = "split on the word on and include on with previous"
a = s.split(/(on)/)

# iterate through and combine adjacent items together and store
# results in a second array
b = []
a.each_index{ |i|
   b << a[i] if i.even?
   b[b.length - 1] += a[i] if i.odd?
   }

print b

Результаты в этом:

["split on", " the word on", " and include on", " with previous"]
...