2011-10-03 13 views
6

Domyślam się, że będzie to głupi błąd, ale dla mnie poniższy zwraca tablicę zawierającą tylko "M". Zobacz:Ruby Regex, tylko jedno przechwytywanie (bardzo proste!)

/(.)+?/.match("Many many characters!").captures 
=> ["M"] 

Dlaczego nie zwraca tablicy wszystkich znaków? Musiałem przegapić coś jawnie oczywistego, ponieważ nie widzę co w tym złego?

Edycja: Właśnie zdałem sobie sprawę, że nie potrzebuję +? ale nadal nie działa bez niego.

Edytuj: Przepraszam! Wyjaśnię: moim celem jest umożliwienie użytkownikom wprowadzenia wyrażenia regularnego i stylizacji oraz wejściowego pliku tekstowego, gdziekolwiek jest dopasowanie, tekst zostanie otoczony elementem html i zostanie zastosowana stylizacja, nie dzielę tylko łańcuch znaków do znaków, użyłem tylko podanego wyrażenia regularnego, ponieważ było to najprostsze, choć z mojej strony było to głupie. Jak uzyskać grupy przechwytywania ze skanowania() lub czy jest to niemożliwe? Widzę, że $ 1 zawiera "!" (ostatni mecz?) i żadnych innych.

Edytuj: Gosh, to naprawdę nie jest mój dzień. Jak poinformował mnie injekt, przechwyty są przechowywane w osobnych tablicach. Jak uzyskać przesunięcie tych przechwytów z oryginalnego ciągu? Chciałbym móc uzyskać przesunięcie przechwytywania, a następnie otoczyć go innym ciągiem. A może po to jest gsub? (Myślałem, że tylko wymienić mecz, a nie grupa przechwytywania)

Mam nadzieję, że ostateczna edit: prawo, niech po prostu zacznij to znowu: P

Tak, mam ciąg. Użytkownik użyje pliku konfiguracyjnego do wprowadzenia wyrażenia regularnego, a następnie do stylu powiązanego z każdą grupą przechwytywania. Muszę być w stanie przeskanować cały ciąg i uzyskać początek i koniec lub przesunięcie i rozmiar każdego dopasowania grupy.

Więc jeśli użytkownik nie ustawił ([\w-\.]+)@((?:[\w]+\.)+)([a-zA-Z]{2,4}) (adres e-mail), a następnie powinny być w stanie uzyskać:

[ ["elliotpotts", 0, 11], 
    ["sample.",  12, 7], 
    ["com",   19, 3] ] 

od napisu: "[email protected]"

Jeśli tak nie jest jasne, po prostu coś jest nie tak ze mną: P. Wielkie dzięki, chłopaki, i dziękuję, że jesteście tak cierpliwi!

+0

Właśnie widziałem twoją edycję, przechwytywanie grup ze skanowania są przechowywane w oddzielnych tablicach, po prostu spróbuj wyrażenie regularne i ciąg testowy w irb zobaczysz. Odpowiedzi pozostają takie same z dołączoną edycją. –

+0

Po obejrzeniu kolejnej edycji musisz zaktualizować ją, podając więcej informacji. Jestem trochę zdezorientowany: P Możesz swobodnie wymyślić bardziej kompletny przykład, niezależnie od tego, jak bardzo jest on wymyślny, więc dokładnie wiemy, czego potrzebujesz, aby wyodrębnić –

+0

W porządku, zaktualizowałeś moją odpowiedź, wprowadzając ostatnią zmianę. Jestem teraz trochę związany z czasem, więc jest to kompletne rozwiązanie bez żadnego wyjaśnienia, daj mi znać, jeśli to nie ma sensu, a ja je zaktualizuję. –

Odpowiedz

9

Ponieważ przechwytywania jest dopasowanie tylko jeden pojedynczy znak.(.)+ nie jest taka sama jak (.+)

>> /(.)+?/.match("Many many characters!").captures 
=> ["M"] 
>> /(.+)?/.match("Many many characters!").captures 
=> ["Many many characters!"] 
>> /(.+?)/.match("Many many characters!").captures 
=> ["M"] 

Jeśli chcesz, aby dopasować każdy znak rekursywnie użyć String#scan lub String#split jeśli nie dbają o grupach przechwytywania

Korzystanie skanowania:

"Many many characters!".scan(/./) 
#=> ["M", "a", "n", "y", " ", "m", "a", "n", "y", " ", "c", "h", "a", "r", "a", "c", "t", "e", "r", "s", "!"] 

Note ta inna odpowiedź używa (.), podczas gdy to jest w porządku, jeśli zależy ci na grupie przechwytującej, to jest trochę bezsensowne, jeśli tego nie zrobisz, w przeciwnym razie zwróci KAŻDY CHARAKTER w swoim n oddzielny Array, tak:

[["M"], ["a"], ["n"], ["y"], [" "], ["m"], ["a"], ["n"], ["y"], [" "], ["c"], ["h"], ["a"], ["r"], ["a"], ["c"], ["t"], ["e"], ["r"], ["s"], ["!"]] 

W przeciwnym razie po prostu używać split: "Many many characters!".split(' ')"

EDIT W odpowiedzi na twój EDIT:

reg = /([\w-\.]+)@((?:[\w]+\.)+)([a-zA-Z]{2,4})/ 
str = "[email protected]" 
str.scan(reg).flatten.map { |capture| [capture, str.index(capture), capture.size] } 
#=> [["elliotpotts", 0, 11], ["sample.", 12, 7], ["com", 19, 3]]` 

Aha, i nie ma potrzeby skanowania , tak naprawdę nie skanujesz, więc nie musisz się przemieszczać, przynajmniej nie z podanym przykładem:

str.match(reg).captures.map { |capture| [capture, str.index(capture), capture.size] } 

będzie również działać

+0

Dziękuję! Znalazłem również alternatywną odpowiedź i opublikuję ją teraz. Dziękuję Ci! – Ell

+0

Dwa podane fragmenty kodu nie działają poprawnie dla przesunięć w ogólnym przypadku, działają tylko wtedy, gdy dopasowane podciągi są różne. Jeśli, na przykład, są 3 dopasowania dla "h", to ten sam indeks (pierwsza instancja "h") jest zwracana 3 razy. str.index (przechwyt) zwraca indeks pierwszej instancji przechwyconego podłańcucha. – jpwynn

0

Zwraca tylko jedną postać, ponieważ jest to wszystko, o co ją poprosiłeś. Prawdopodobnie chcesz użyć scan zamiast:

str = "Many many characters!" 
matches = str.scan(/(.)/) 
1

Tak, coś ważnego zostało brakowało ;-)

(...) wprowadza tylko jedna grupa przechwytywania: ilość razy mecze grupowe nie ma znaczenia jak indeks jest określona tylko przez regularne samo wyrażenie, a nie dane wejściowe.

Klucz jest "globalnym wyrażeniem regularnym", które zastosuje wyrażenie regularne wiele razy w kolejności. Ruby to odbywa się odwracanie od Regex#match do String#scan (wiele innych języków mają „/ g” modyfikator wyrażenie regularne):

"Many many chara­cters!".sc­an(/(.)+?/­) 
# but more simply (or see answers using String#split) 
"Many many chara­cters!".sc­an(/(.)/­) 

Szczęśliwy kodowania

0

Poniższy kod jest od Get index of string scan results in ruby i modyfikowane na mój gust .

[].tap {|results| 
    "abab".scan(/a/) {|capture| 
     results.push(([capture, Regexp::last_match.offset(0)]).flatten) 
    } 
} 

=> [["a", 0], ["a", 2]]