Як співставити всі випадки регулярного вираження


586

Чи є швидкий спосіб знайти кожну відповідність регулярного виразу в Ruby? Я переглянув об'єкт Regex в STL Ruby і шукав безрезультатно в Google.


3
Я прочитав це, як я можу шукати рядок по всіх шаблонах регулярних
виразів

Відповіді:


821

Використання scanповинно зробити трюк:

string.scan(/regex/)

9
Але що опирається на цю справу? "зіставити мене!". scan (/.../) = ["мат", "ch" "я!" ], але всі випадки /.../ були б ["мат", "атс", "тч", "ч", ...]
Майкл Діккенс

13
Не було б. /.../ - це нормальний жадібний регулярний вираз. Це не буде відслідковувати відповідність вмісту. ви можете спробувати скористатися ледачим регулярним виразком, але навіть цього, мабуть, буде недостатньо. подивіться на regexp doc ruby-doc.org/core-1.9.3/Regexp.html, щоб правильно висловити своє regexp :)
Жан

49
це здається як Ruby WTF ... чому це на String замість Regexp з іншими елементами regexp? Він навіть ніде не згадується в документах для Regexp
Anentropic

9
Я думаю, це тому, що це визначено і викликається на String не на Regex ... Але це насправді має сенс. Ви можете написати регулярний вираз, щоб зафіксувати всі матчі, використовуючи Regex # match і перегляньте захоплені групи. Тут ви пишете функцію часткового збігу і хочете, щоб вона застосовувалася неодноразово на заданому рядку, це не є відповідальністю Regexp. Я пропоную вам перевірити реалізацію сканування для кращого розуміння: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Жан

9
@MichaelDickens: у цьому випадку ви можете використовувати /(?=(...))/.
Конрад Боровський

67

Щоб знайти всі відповідні рядки, використовуйте scanметод String .

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

Якщо ви хочете, MatchDataякий тип об'єкта повертається методом Regexp match, використовуйте:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

Перевага використання MatchDataполягає в тому, що ви можете використовувати такі методи, як offset:

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

Дивіться ці питання, якщо хочете дізнатися більше:

Читання про спеціальні змінних $&, $', $1, $2в Ruby , буде корисно теж.


12

якщо у вас є регулярне вираження з групами:

str="A 54mpl3 string w1th 7 numbers scatter3r ar0und"
re=/(\d+)[m-t]/

ви можете використовувати scanметод String для пошуку відповідних груп:

str.scan re
#> [["54"], ["1"], ["3"]]

Щоб знайти відповідну схему:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]

str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]є ідіоматичнішим ніжstr.to_enum(:scan,re).map {$&}
Людина з жерсті

Можливо, ви неправильно зрозуміли. Регулярним виразом прикладу користувача, на який я відповів, було: /(\d+)[m-t]/не /\d+[m-t]/писати: re = /(\d+)[m-t]/; str.scan(re)це те саме, str.scan(/(\d+)[mt]/)але я отримую #>, [["" 54 "], [" 1 "], [" 3 "]]а не "54m", "1t", "3r"]питання. Якщо у мене є регулярний вираз із групою та хочу захопити всі шаблони, не змінюючи регулярних вираз (виходячи з групи), як я можу це зробити? У цьому сенсі можливим рішенням, хоч і трохи кричущим і важким для читання, було:str.to_enum(:scan,re).map {$&}
MVP

-1

Можна використовувати string.scan(your_regex).flatten. Якщо ваш регекс містить групи, він повернеться в єдиний простий масив.

string = "A 54mpl3 string w1th 7 numbers scatter3r ar0und"
your_regex = /(\d+)[m-t]/
string.scan(your_regex).flatten
=> ["54", "1", "3"]

Регекс також може бути названою групою.

string = 'group_photo.jpg'
regex = /\A(?<name>.*)\.(?<ext>.*)\z/
string.scan(regex).flatten

Ви також можете використовувати gsub, це лише ще один спосіб, якщо ви хочете MatchData.

str.gsub(/\d/).map{ Regexp.last_match }

Видаліть групування your_regex = /(\d+)[m-t]/і не потрібно використовувати flatten. Ваш остаточний приклад використовує, last_matchщо в цьому випадку, ймовірно, є безпечним, але є глобальним і, можливо, може бути перезаписане, якщо який-небудь регулярний вирівнювання був узгоджений перед викликом last_match. Натомість, мабуть, безпечніше використовувати string.match(regex).captures # => ["group_photo", "jpg"]або string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]як показано в інших відповідях, залежно від структури та потреб.
Олов'яний чоловік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.