Come abbinare tutte le occorrenze di una regex


586

C'è un modo rapido per trovare ogni corrispondenza di un'espressione regolare in Ruby? Ho esaminato l'oggetto Regex in Ruby STL e ho cercato su Google senza risultati.


3
Ho letto questo è come posso cercare una stringa per tutti i modelli regex ed è stato orribilmente confuso ...
Hugoagogo,

Risposte:


821

Utilizzando scandovrebbe fare il trucco:

string.scan(/regex/)

9
Ma cosa succede in questo caso? "match me!". scan (/.../) = ["mat", "ch" "me!" ], ma tutte le occorrenze di /.../ sarebbero ["mat", "atc", "tch", "ch", ...]
Michael Dickens,

13
Non lo sarebbe. /.../ è una normale regexp golosa. Non tornerà indietro sul contenuto abbinato. potresti provare a usare un regexp pigro ma anche quello probabilmente non sarà abbastanza. dai un'occhiata al documento regexp ruby-doc.org/core-1.9.3/Regexp.html per esprimere correttamente il tuo regexp :)
Jean

49
questo sembra un Ruby WTF ... perché è su String invece che su Regexp con le altre cose regexp? Non è nemmeno menzionato da nessuna parte nei documenti di Regexp
Anentropic del

9
Immagino sia perché è definito e chiamato String non su Regex ... Ma in realtà ha senso. È possibile scrivere un'espressione regolare per acquisire tutte le corrispondenze utilizzando Regex # match e scorrere i gruppi acquisiti. Qui scrivi una funzione di corrispondenza parziale e vuoi che venga applicata più volte su una determinata stringa, questa non è responsabilità di Regexp. Ti suggerisco di controllare l'implementazione della scansione per una migliore comprensione: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Jean

9
@MichaelDickens: in questo caso puoi usare /(?=(...))/.
Konrad Borowski,

67

Per trovare tutte le stringhe corrispondenti, usa il scanmetodo String .

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

Se vuoi, MatchDatache è il tipo di oggetto restituito dal matchmetodo Regexp , usa:

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

Il vantaggio dell'uso MatchDataè che puoi usare metodi come 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]

Vedi queste domande se desideri saperne di più:

Leggendo sulle variabili speciali $&, $', $1,$2 in Ruby sarà utile anche.


12

se hai una regexp con gruppi:

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

puoi usare il scanmetodo String per trovare gruppi corrispondenti:

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

Per trovare il modello corrispondente:

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

str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]è più idiomatico distr.to_enum(:scan,re).map {$&}
l'Uomo di latta il

Forse hai frainteso. L'espressione regolare dell'esempio di un utente a cui ho risposto era: /(\d+)[m-t]/non /\d+[m-t]/scrivere: re = /(\d+)[m-t]/; str.scan(re)è uguale str.scan(/(\d+)[mt]/)ma ottengo #> [["" 54 "], [" 1 "], [" 3 "]]e non "54m", "1t", "3r"]La domanda era: se ho un'espressione regolare con un gruppo e voglio catturare tutti gli schemi senza cambiare il regolare espressione (lasciando il gruppo), come posso farlo? In questo senso, una possibile soluzione, sebbene un po 'criptica e difficile da leggere, era:str.to_enum(:scan,re).map {$&}
MVP

-1

È possibile utilizzare string.scan(your_regex).flatten. Se il tuo regex contiene gruppi, tornerà in un singolo array normale.

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

Anche Regex può essere un gruppo nominato.

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

Puoi anche usare gsub, è solo un altro modo se vuoi MatchData.

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

Rimuovere il raggruppamento da your_regex = /(\d+)[m-t]/e non sarà necessario utilizzarlo flatten. Il tuo ultimo esempio usa last_matchche in questo caso è probabilmente sicuro, ma è globale e potrebbe essere sovrascritto se un regex è stato abbinato prima di chiamare last_match. Invece è probabilmente più sicuro da usare string.match(regex).captures # => ["group_photo", "jpg"]o string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]come mostrato in altre risposte, a seconda del modello e delle esigenze.
Tin Man,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.