1.4.1.16. fejezet, Reguláris kifejezések
Mintát készíthetünk két jobbra dőlő perjel között (pl.: //). A minta illeszkedésének tesztelését sokféle kép végezhetjük. Most a =~ operátorral próbáljuk ki, pl.:
puts( /abc/ =~ 'abc' ) #=> returns 0
A minta illesztés sikerült, egy számot kapunk vissza, hogy a string-en belül hol talált rá a tesztelés. Ha nincs találat, nil értékkel tér vissza. A következő minta kétszer is előfordul, de meg egyenlőre csak az elsőt találtuk meg:
puts( /abc/ =~ 'xyzabcxyzabc' ) #=> returns 3
Ezek mind reguláris kifejezések:
regex1 = Regexp.new('^[a-z]*$') regex2 = /^[a-z]*$/ regex3 = %r{^[a-z]*$}
a %r-nél külön jelölést használtunk, - ami nem alfanumerikus karakter -, lásd a 3. fejezetet.
RegEx = /def/ Str1 = 'abcdef' Str2 = 'ghijkl' if RegEx =~ Str1 then puts( 'true' ) else puts( 'false' ) end #=> displays: true if RegEx =~ Str2 then puts( 'true' ) else puts( 'false' ) end #=> displays: false
A fenti tesztelés már több if..then..else elemet is tartalmaz. Fájlokon is elvégezhetjük a minta illesztést:
File.foreach( 'regex1.rb' ){ |line| if line =~ /^\s*#/ then puts( line ) end }
Zárójelekkel csoportokat alakíthatunk ki a mintába.
/(hi).*(h...o)/ =~ "The word 'hi' is short for 'hello'."
Csoportosítás eredményeként a $ kezdetű változók kapnak értéket, úgy mint $1 és $2. Ha csak az egyik csoport is hiányzik, nil értéket ad vissza a kifejezés, és a globális változók nem kapnak értéket.
A következő minta a # megjegyzés sorokat C stílusú // formára alakítja
File.foreach( 'regex1.rb' ){ |line| line = line.sub(/(^\s*)#(.*)/, '\1//\2') puts( line ) }
Itt a sub metódus második paraméterében a \1 és \2 a megtalált két karaktercsoportot jelenti.
Adat megtalálás
A =~ nem az egyetlen módja a minta megtalálásának.
puts( /cde/ =~ 'abcdefg' ) #=> 2 puts( /cde/.match('abcdefg') ) #=> cde
Valójában a második egy MatchData osztály, ami tartalmaz egy stringet. A találati lista tömbön végig is lépegethetünk a capture vagy a to_a metódussal:
x = /(^.*)(#)(.*)/.match( 'def myMethod # This is a very nice method' ) puts( '---x.captures---' ) p( x.captures ) x.captures.each{ |item| puts( item ) } puts( "\n---x.to_a---" ) p( x.to_a ) x.to_a.each{ |item| puts( item ) }
Eredmények:
---x.captures--- ["def myMethod ", "#", " This is a very nice method"] def myMethod # This is a very nice method ---x.to_a--- ["def myMethod # This is a very nice method", "def myMethod ", "#", " This is a very nice method"] def myMethod # This is a very nice method def myMethod # This is a very nice method
Apró különbség, hogy a to_a metódus a 0. indexében az egész stringet tartalmazza.
Van két metódus még, ami a találat előtti és a találat utáni stringet adja vissza:
x = /#/.match( 'def myMethod # This is a very nice method' ) puts( x.pre_match ) #=> def myMethod puts( x.post_match ) #=> This is a very nice method
Alternatívaként használhatjuk a $ változót a backquote ($`) és normal quote ($') mellett. Előbbi a találat előttit, utóbbi az utána lévő stringet adja vissza.
Ha a match metódust csoportokkal használjuk, tömb stílusú indexelést használhatunk a MatchData objektumra. A nulladik indexen az eredeti szöveg található.
puts( /(.)(.)(.)/.match("abc")[2] ) #=> "b"
Egy speciális változó, a $~ tartalmazza az utoljára keletkezett MatchData objektumot. És el ne feledjük, hogy használhatunk tömb indexelést itt is:
puts( $~[0], $~[1], $~[3] )
Habár a tömb metódusainak egész tárházát használhatjuk, van olyan metódus, amit csak a to_a és captures metódusokon keresztül érünk el.
puts( $~.captures.sort )
Falánk mintaillesztés
Ha a keresés nem áll meg az első megtalált illeszkedésnél, akkor beszélünk falánk mintaillesztésről. Ez a viselkedés a * vagy + jelek után tett ?-el megváltoztatható:
puts( /.*at/.match('The cat sat on the mat!') ) #=> returns: The cat sat on the mat puts( /.*?at/.match('The cat sat on the mat!') ) #=> returns: The cat puts( /.+\\/.match('C:\mydirectory\myfolder\myfile.txt') ) #=> C:\mydirectory\myfolder\ puts( /.+?\\/.match('C:\mydirectory\myfolder\myfile.txt') ) #=> C:\
String metódusok
A =~ ,match és a sub metódusok mellett ott van még a scan, ami végiglépked a stringen, keresve az összes illeszkedő találatot.
TESTSTR = "abc is not cba" puts( "\n--match--" ) b = /[abc]/.match( TESTSTR ) #=> MatchData: "a" puts( "--scan--" ) a = TESTSTR.scan(/[abc]/) #=> Array: ["a", "b", "c", "c", "b", "a"]
A scan eredménye átadható egy blokknak:
a = TESTSTR.scan(/[abc]/){|c| print( c.upcase ) } #=> ABCCBA
Egyik változata a String.slice metódusnak paraméterül kap egy kifejezést, és az illeszkedő rész stringeket adja vissza. Ha String.slice! metódust (vegyük észre a ! jelet) hajtunk végre, a talált rész stringeket kiveszi az eredeti szövegből, és az így előállt stringet adja vissza.
s = "def myMethod # a comment meteroid" puts( s.slice( /m.*?d/ ) ) puts( s ) puts( s.slice!( /m.*d/ ) ) puts( s )
Eredmények:
myMethod def myMethod # a comment meteroid myMethod # a comment meteroid def
A split metódussal tömb elemekre vágható az adott kifejezések mentén.
s = "def myMethod # a comment" p( s.split( /\s/ ) ) #=> ["def", "myMethod", "#", "a", "comment"]
A sub metódussal egy mintára kereshetünk, és kicserélhetjük az első találatot egy stringre, mint azt korábban láthattuk. A gsub és a gsub()! pedig az összes illeszkedő részt kicseréli a második paraméterében kapott stringre. A sub!() metódus pedig ugyanazt teszi, mint a sub, csak megváltoztatja a hatáskört arra a stringre, amielyikből hívták.
Fájl műveletek
Most nézzük meg, hogyan számlálja a szavakat a Ruby reguláris kifejezéssel.
count = 0 File.foreach( 'regex1.rb' ){ |line| count += line.scan( /[a-z0-9A-Z]+/ ).size } puts( "There are #{count} words in this file." )
És hogy hogyan veszi ki a megjegyzés jeleket a fájlból:
File.open( 'testfile1.txt', 'w' ){ |f| File.foreach( 'regex1.rb' ){ |line| f.puts( line.sub(/(^\s*)#(.*)/, '\1//\2') ) } }
A Ruby reguláris kifejezései tesztelhetők a http://rubular.com weblapon. Itt található egy kis táblázat is a kifejezések jelentéséről.
- A hozzászóláshoz be kell jelentkezni