|
|||
Fixnum
. Integers außerhalb dieses Bereichs sind
Objekte der Klasse Bignum
(die zur Zeit als variable
Menge von kurzen Integerwerten implementiert ist). Dieser Prozess ist
transparent und Ruby verwaltet die Konvertierung zwischen den beiden Klassen
automatisch.
num = 8 7.times do print num.type, " ", num, "\n" num *= num end |
Fixnum 8 Fixnum 64 Fixnum 4096 Fixnum 16777216 Bignum 281474976710656 Bignum 79228162514264337593543950336 Bignum 6277101735386680763835789423207666416102355444464034512896 |
0
für
oktale, 0x
für hexadezimale oder 0b
für binäre Werte)
gefolgt von einer Zeichenkette von Zahlen zur passenden Basis geschrieben.
Unterstriche werden dabei ignoriert.
123456 # Fixnum 123_456 # Fixnum (Unterstrich ignoriert) -543 # Negatives Fixnum 123_456_789_123_345_789 # Bignum 0xaabb # Hexadezimal 0377 # Oktal -0b101_010 # Binär (negiert) |
value & 0x9f
'', die
``Meta''-Version dasselbe wie value | 0x80
''.
Schlussendlich erzeugt ?\C-? ein ``ASCII Delete'' (Löschzeichen),
0177
.
?a # Zeichencode ?\n # Zeichencode eines Newlines (0x0a) ?\C-a # Control a = ?A & 0x9f = 0x01 ?\M-a # Meta setzt Bit 7 ?\M-\C-a # Meta und Control a ?\C-? # Deletezeceichen |
double
der verwendeten Architektur, in ein
Float
-Objekt umgewandelt. Dem Dezimalpunkt muss eine
Ziffer folgen, da ansonsten 1.e3
versuchen würde, die Methode
e3
der Klasse Fixnum
aufzurufen.
Alle Zahlen sind Objekte und reagieren auf eine Vielfalt von Nachrichten (die
vollständige Auflistung finden Sie ab den Seiten 294, 318, 319, 328 und 354).
Somit können Sie, anders als in (sagen wir) C++, den Absolutwert einer Zahl
mittels eineZahl.abs
herausfinden und nicht mit
abs(eineZahl)
.
Integers unterstützen auch eine Reihe nützlicher Iteratoren. Einen haben wir
bereits gesehen -- 7.times
im Codebeispiel auf Seite 49. Andere sind
etwa upto
und downto
, mit denen man über zwei
Integerwerten auf- und abwärts iterieren kann, sowie step
, das
eher wie eine herkömmliche for
-Schleife arbeitet.
3.times { print "X " } 1.upto(5) { |i| print i, " " } 99.downto(95) { |i| print i, " " } 50.step(80, 5) { |i| print i, " " } |
X X X 1 2 3 4 5 99 98 97 96 95 50 55 60 65 70 75 80 |
DATA.each do |line| vals = line.split # Aufspalten der Zeile, Zeichen in vals speichern. print vals[0] + vals[1], " " end |
3 4 5 6 7 8 |
String#to_i
,
um den String in eine Zahl zu konvertieren.
DATA.each do |line| vals = line.split print vals[0].to_i + vals[1].to_i, " " end |
7 11 15 |
String
.
Strings werden oft mittels Zeichenkettensymbolen erzeugt -- Sequenzen von
Buchstaben zwischen Begrenzern. Da es ansonsten schwer wäre, binäre Daten
innerhalb der Programmsourcen zu repräsentieren, können Sie innerhalb von
Strings verschiedene Escapesequenzen verwenden. Jedes Einzelne wird während des
Kompilierens mit dem entsprechenden Binärwert ersetzt. Der verwendete Begrenzer
bestimmt den Grad der Ersetzungen: Innerhalb von einfachen Anführungszeichen
werden zwei aufeinanderfolgende Backslashes durch einen einzelnen ersetzt und
ein Backslash mit einfachem Anführungszeichen wird zu einem einfachen
'
.
'escape using "\\"' |
» | escape using "\" |
'That\'s right' |
» | That's right |
#{
ausdruck }
den Wert jeder
Ruby-Anweisung in einen String einsetzen. Wenn der Ausdruck nur eine globale
Variable, eine Klassenvariable oder eine Instanzvariable ist, können Sie die
geschwungenen Klammern auslassen.
"Seconds/day: #{24*60*60}" |
» | Seconds/day: 86400 |
"#{'Ho! '*3}Merry Christmas" |
» | Ho! Ho! Ho! Merry Christmas |
"This is line #$." |
» | This is line 3 |
%q
, %Q
und ``Here-Dokumente.''
%q
und %Q
verhalten sich wie einfache oder doppelte
Anführungszeichen.
%q/String wie mit einfachen Anführungszeichen/ |
» | String wie mit einfachen Anführungszeichen |
%Q!String wie mit doppelten Anführungszeichen! |
» | String wie mit doppelten Anführungszeichen |
%Q{Sekunden/Tag: #{24*60*60}} |
» | Sekunden/Tag: 86400 |
{
, [
, (
oder <
wird der String bis zum
nächsten passenden Symbol (also >
, )
, ]
oder
}
) gelesen. Andernfalls wird der String bis zum nächsten Vorkommen
desselben Begrenzers gelesen.
Schlussendlich können Sie Strings mit einem Here-Dokument erzeugen.
aString = <<ENDE_DES_STRINGS Der String besteht aus allen Eingabezeilen bis zur nächsten, die mit dem Text, der auf '<<' folgte, endet. ENDE_DES_STRINGS |
<<
angeben (der aber nicht inkludiert
wird). Normalerweise muss dieser Begrenzer in der ersten Spalten stehen. Wenn
Sie jedoch nach dem <<
ein Minuszeichen schreiben, können Sie
den Begrenzer einrücken.
print <<-STRING1, <<-STRING2 ver STRING1 ketten STRING2 |
ver ketten |
String
ist mit mehr als 75 Standardmethoden vermutlich
die größte in Ruby eingebaute Klasse. Wir werden hier nicht alle vorstellen;
die Bibliotheksreferenz bietet eine komplette Liste. Stattdessen schauen wir
uns einige geläufige Eigentümlichkeiten von Strings an -- Dinge, auf die Sie
während des tagtäglichen Programmierens wahrscheinlich treffen werden.
Gehen wir zurück zu unserer Jukebox. Obwohl sie entworfen wurde, um mit dem
Internet verbunden zu sein, speichert sie auch Kopien einiger beliebter Songs
auf einer lokalen Festplatte. Auch wenn ein Eichhörnchen sich durch unsere
Netzverbindung schraubt, ist es uns auf diese Weise trotzdem möglich, unsere
Kunden zu unterhalten.
Aus historischen Gründen (gibt's überhaupt andere?) ist die Liste der Songs in
Zeilen in einer normalen Datei gespeichert. Jede Zeile enthält den Namen der
Datei, in dem der Song gespeichert ist, die Länge des Songs, den Künstler und
den Titel, alles in durch vertikale Striche getrennten Feldern. Eine typische
Datei könnte so beginnen:
/jazz/j00132.mp3 | 3:45 | Fats Waller | Ain't Misbehavin' /jazz/j00319.mp3 | 2:58 | Louis Armstrong | Wonderful World /bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red : : : : |
String
verwenden werden, um die
Felder zu extrahieren und zu säubern, bevor wir ein darauf beruhendes Objekt
von Song
erstellen können. Zumindest müssen wir:
String#split
wird diese Arbeit gut verrichten. In unserem Fall übergeben wir
split
den regulären Ausdruck /\s*\|\s*/
, der die Zeile
immer dann aufspaltet, wenn split
einen vertikalen Strich findet,
der optional von Leerzeichen umgeben ist. Und da die Zeile, die aus der Datei
gelesen wird, immer noch ein Newline enthält, verwenden wir
String#chomp
,
um es abzuschneiden, bevor wir split
anwenden.
songs = SongList.new songFile.each do |line| file, length, name, title = line.chomp.split(/\s*\|\s*/) songs.append Song.new(title, name, length) end puts songs[1] |
Song: Wonderful World--Louis Armstrong (2:58) |
String#squeeze
,
die wiederholte Zeichen wegschneidet. Wir verwenden die Form
squeeze!
, die den String auf der Stelle verändert.
songs = SongList.new songFile.each do |line| file, length, name, title = line.chomp.split(/\s*\|\s*/) name.squeeze!(" ") songs.append Song.new(title, name, length) end puts songs[1] |
Song: Wonderful World--Louis Armstrong (2:58) |
split
benutzen und diesmal die Felder um den Doppelpunkt herum
ausschneiden.
mins, secs = length.split(/:/) |
String#scan
ist split
ähnlich, da es Strings anhand von Mustern in Stücke
schneidet. Anders als split
gibt man bei scan
aber
das Muster an, das man erhalten will. In diesem Fall, möchten wir eine Ziffer
oder mehrere Ziffern für die Minuten und die Sekunden finden. Das Muster für
mindestens eine Ziffer ist /\d+/
.
songs = SongList.new songFile.each do |line| file, length, name, title = line.chomp.split(/\s*\|\s*/) name.squeeze!(" ") mins, secs = length.scan(/\d+/) songs.append Song.new(title, name, mins.to_i*60+secs.to_i) end puts songs[1] |
Song: Wonderful World--Louis Armstrong (178) |
String
.
class WordIndex def initialize @index = Hash.new(nil) end def index(anObject, *phrases) phrases.each do |aPhrase| aPhrase.scan /\w[-\w']+/ do |aWord| # jedes Wort extrahieren aWord.downcase! @index[aWord] = [] if @index[aWord].nil? @index[aWord].push(anObject) end end end def lookup(aWord) @index[aWord.downcase] end end |
String#scan
extrahiert Elemente aus einem String, die auf einen regulären Ausdruck passen.
In diesem Fall findet das Muster ``\w[-\w']+
'' jedes Zeichen, das in
einem Wort vorkommen kann, gefolgt von einem oder mehreren der Dinge die in den
eckigen Klammern angegeben sind (ein Bindestrich, noch ein Wortzeichen und ein
einfaches Anführungszeichen). Wir erläutern reguläre Ausdrücke ab Seite 58
näher. Damit unsere Suche nicht zwischen Klein- und Großbuchstaben
unterscheidet, wandeln wir sowohl die Wörter, die wir extrahieren als auch die
Wörter, die wir für unsere Suche verwenden, in Kleinbuchstaben um. Beachten Sie
das Rufzeichen am Ende der ersten downcase!
-Methode.
Wie auch in der Methode squezze!
, die wir vorhin benutzt haben,
ist das ein Zeichen dafür, dass die Methode den Empfänger auf der Stelle
verändern wird, in unserem Fall also den String in Kleinbuchstaben
umwandelt.[In diesem Codebeispiel ist ein kleiner Bug: Das Lied
``Gone, Gone, Gone'' würde drei Mal in den Index aufgenommen werden. Können
Sie das in Ordnung bringen?]
Wir erweitern unsere Klasse SongList
, damit sie Songs
schon beim Hinzufügen in den Index aufnimmt und fügen auch eine Methode ein,
mit der man mit einem gegebenen Wort nach einem Lied suchen kann.
class SongList def initialize @songs = Array.new @index = WordIndex.new end def append(aSong) @songs.push(aSong) @index.index(aSong, aSong.name, aSong.artist) self end def lookup(aWord) @index.lookup(aWord) end end |
songs = SongList.new songFile.each do |line| file, length, name, title = line.chomp.split(/\s*\|\s*/) name.squeeze!(" ") mins, secs = length.scan(/\d+/) songs.append Song.new(title, name, mins.to_i*60+secs.to_i) end puts songs.lookup("Fats") puts songs.lookup("ain't") puts songs.lookup("RED") puts songs.lookup("WoRlD") |
Song: Ain't Misbehavin'--Fats Waller (225) Song: Ain't Misbehavin'--Fats Waller (225) Song: Texas Red--Strength in Numbers (249) Song: Wonderful World--Louis Armstrong (178) |
String
anzusehen. Schauen wir uns aber stattdessen einen
einfacheren Datentyp an: Ranges.
1..10 'a'..'z' 0...anArray.length |
Range
gespeichert, die Referenzen auf
zwei Objekte von Fixnum
enthält. Wenn Sie müssen, können
Sie einen Bereich mit der Methode to_a
in eine Liste umwandeln.
(1..10).to_a |
» | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
('bar'..'bat').to_a |
» | ["bar", "bas", "bat"] |
digits = 0..9 |
||
digits.include?(5) |
» | true |
digits.min |
» | 0 |
digits.max |
» | 9 |
digits.reject {|i| i < 5 } |
» | [5, 6, 7, 8, 9] |
digits.each do |digit| |
||
dial(digit) |
||
end |
succ
reagieren müssen und
dabei das nächste Objekt in der Reihe zurückgeben. Weiter muss das Objekt
mit dem allgemeinen Vergleichsoperator <=>
vergleichbar sein.
Dieser Operator, manchmal auch Raumschiffoperator (Spaceship Operator)
genannt, gibt -1, 0 oder 1 zurück, je nachdem, ob der erste Wert kleiner,
gleich oder größer als der zweite Wert ist.
Hier eine einfache Klasse, die Zeilen mit ``#''-Zeichen repräsentiert. Wir
könnten Sie als textbasierte Kontrolle benutzen, wenn wir die
Lautstärkenfunktion unserer Jukebox ausprobieren.
class VU include Comparable attr :volume def initialize(volume) # 0..9 @volume = volume end def inspect '#' * @volume end # Support for ranges def <=>(other) self.volume <=> other.volume end def succ raise(IndexError, "Volume too big") if @volume >= 9 VU.new(@volume.succ) end end |
VU
-Objekten bilden:
medium = VU.new(4)..VU.new(7) |
||
medium.to_a |
» | [####, #####, ######, #######] |
medium.include?(VU.new(3)) |
» | false |
while gets print if /start/../end/ end |
===
, dem case equality operator.
(1..10) === 5 |
» | true |
(1..10) === 15 |
» | false |
(1..10) === 3.14159 |
» | true |
('a'..'j') === 'c' |
» | true |
('a'..'j') === 'z' |
» | false |
case
-Ausdrucks auf Seite 83 zeigt den Einsatz
dieses Tests. Dabei wird anhand eines gegebenen Jahres der Stil eines Jazzsongs
festgestellt wird.
line.split(/\s*\|\s*/)
einen mit
optionalen Leerzeichen umgebenen senkrechten Strich findet. Erforschen wir nun
die Details regulärer Ausdrücke, um festzustellen, warum diese Behauptung wahr
ist.
Reguläre Ausdrücke werden verwendet um ein Muster in einem String zu finden.
Rubys eingebaute Unterstützung erlaubt es, Muster bequem und prägnant zu
suchen und zu ersetzen. In diesem Abschnitt erarbeiten wir alle wichtigen
Eigenschaften regulärer Ausdrücke. Einige Details werden wir nicht besprechen:
schauen Sie für weitere Informationen auf Seite 207.
Reguläre Ausdrücke sind Objekte vom Type Regexp
. Sie können erstellt
werden, indem man den Konstruktor explizit aufruft oder die Abkürzungen in den
Formen /muster/ und %r\muster\ verwendet.
a = Regexp.new('^\s*[a-z]') |
» | /^\s*[a-z]/ |
b = /^\s*[a-z]/ |
» | /^\s*[a-z]/ |
c = %r{^\s*[a-z]} |
» | /^\s*[a-z]/ |
Regexp#match(einString)
oder den Suchoperatoren
=~
(positive Suche) oder !~
(negierte Suche) in einem String
suchen.
Die Suchoperatoren sind sowohl für Objekte von String
als auch für
Objekte von Regexp
definiert. Sind beide Operanden des Suchoperators
vom Typ String
, wird der rechte Operand in einen regulären Ausdruck
umgewandelt.
a = "Fats Waller" |
||
a =~ /a/ |
» | 1 |
a =~ /z/ |
» | nil |
a =~ "ll" |
» | 7 |
$&
enthält den Teil des Strings, in dem das
Muster gefunden wurde, $`
enthält den Teil, der der Fundstelle
voranging und $'
den String, der nach der Fundstelle kam. Das können
wir verwenden, um eine Methode showRE
zu schreiben, die zeigt, wo
ein bestimmtes Muster gefunden wurde.
def showRE(a,re) |
||
if a =~ re |
||
"#{$`}<<#{$&}>>#{$'}" |
||
else |
||
"no match" |
||
end |
||
end |
||
|
||
showRE('very interesting', /t/) |
» | very in<<t>>eresting |
showRE('Fats Waller', /ll/) |
» | Fats Wa<<ll>>er |
$~
und
$1
bis $9
. Die Variable $~
ist ein Objekt der
Klasse MatchData
(die ab Seite 340 beschrieben wird),
die alles enthält, was Sie vielleicht über einen Suchvorgang wissen möchten.
$1
und so weiter speichern Teile des gefundenen Musters. Darüber
sprechen wir später. Und Leuten, die sich krümmen, wenn sie diese an Perl
erinnernden Variablen sehen, sei gesagt: halten Sie sich bereit. Am Ende dieses
Kapitels gibt's gute Nachrichten.
showRE('kangaroo', /angar/) |
» | k<<angar>>oo |
showRE('!@%&-_=+', /%&/) |
» | !@<<%&>>-_=+ |
/\s*\|\s*/
, das wir verwendet
haben, um eine Songzeile aufzuteilen. \|
bedeutet ``finde einen
senkrechten Strich''. Ohne Backslash würde ``|
'' Alternation
bedeuten (wir beschreiben das später).
showRE('yes | no', /\|/) |
» | yes <<|>> no |
showRE('yes (no)', /\(no\)/) |
» | yes <<(no)>> |
showRE('are you sure?', /e\?/) |
» | are you sur<<e?>> |
#{...}
-Form enthalten.
/iss/
im String ``Mississippi'' suchen, wird der Substring ``iss'', der an Position 1
beginnt, gefunden. Was aber, wenn Sie ein Muster zwingen wollen nur den Anfang
oder das Ende eines Strings zu finden?
Die Muster ^
und $
findet den Anfang bzw. das Ende einer
Zeile. Diese Muster werden häufig benutzt, um einen Anker in einem
Suchmuster zu setzen: /^option/
beispielsweise findet das Wort
``option'' nur, wenn es am Beginn einer Zeile steht. \A
findet den
Anfang, und \z
und \Z
das Ende eines Strings. (Eigentlich
findet \Z
das Ende eines Strings, außer der String endet auf
``\n''. In diesem Fall findet es einfach das Zeichen vor dem ``\n''.)
showRE("this is\nthe time", /^the/) |
» | this is\n<<the>> time |
showRE("this is\nthe time", /is$/) |
» | this <<is>>\nthe time |
showRE("this is\nthe time", /\Athis/) |
» | <<this>> is\nthe time |
showRE("this is\nthe time", /\Athe/) |
» | no match |
\b
und \B
, die Wortgrenzen
bzw. keine Wortgrenzen finden. Wortzeichen sind Buchstaben, Zahlen und der
Unterstrich.
showRE("this is\nthe time", /\bis/) |
» | this <<is>>\nthe time |
showRE("this is\nthe time", /\Bis/) |
» | th<<is>> is\nthe time |
[
Zeichen]
findet jedes einzelne Zeichen, das
zwischen den Klammern steht. [aeiou]
findet einen Vokal,
[,.:;!?]
findet Satzzeichen usw. Die Bedeutung der speziellen Zeichen
in regulären Ausdrücken -- .|()[}+^$*?
-- ist innerhalb der Klammern
abgeschaltet. Normale Stringersetzung tritt jedoch ein, somit steht (zum
Beispiel) \b
für ein Backspace und \n
für ein
Newline-Zeichen (siehe Tabelle 18.2 auf Seite 205). Zusätzlich können Sie die
Abkürzungen verwenden, die in Tabelle 5.1 auf Seite 62 gezeigt werden, sodass
(zum Beispiel) \s
jedes Whitespace-Zeichen (also Leerzeichen,
Tabulator, Neue Zeile usw.) und nicht nur ein Leerzeichen findet.
showRE('It costs $12.', /[aeiou]/) |
» | It c<<o>>sts $12. |
showRE('It costs $12.', /[\s]/) |
» | It<< >>costs $12. |
]
und -
innerhalb von Zeichenklassen
verwenden, so müssen diese am Beginn stehen.
a = 'Gamma [Design Patterns-page 123]' |
||
showRE(a, /[]]/) |
» | Gamma [Design Patterns-page 123<<]>> |
showRE(a, /[B-F]/) |
» | Gamma [<<D>>esign Patterns-page 123] |
showRE(a, /[-]/) |
» | Gamma [Design Patterns<<->>page 123] |
showRE(a, /[0-9]/) |
» | Gamma [Design Patterns-page <<1>>23] |
^
schreiben: [^a-z]
findet jedes Zeichen, das kein
Kleinbuchstabe ist.
Einige Zeichenklassen kommen so häufig vor, dass Ruby Abkürzungen für sie
bereitstellt, die Sie in Tabelle 5.1 auf Seite 62 finden können. Diese
Abkürzungen können sowohl in Zeichenklassen als auch innerhalb von Suchmustern
selbst vorkommen.
showRE('It costs $12.', /\s/) |
» | It<< >>costs $12. |
showRE('It costs $12.', /\d/) |
» | It costs $<<1>>2. |
Abkürzungen für Zeichenklasse
|
a = 'It costs $12.' |
||
showRE(a, /c.s/) |
» | It <<cos>>ts $12. |
showRE(a, /./) |
» | <<I>>t costs $12. |
showRE(a, /\./) |
» | It costs $12<<.>> |
/\s*\|\s*/
angegeben haben, das eine Zeile aus der
Liederdatei aufspalten sollte, sagten wir, dass wir damit einen senkrechten
Strich finden wollten, der von einer unbestimmten Zahl von Leerzeichen umgeben
ist. Wir wissen nun, dass \s
ein einzelnes Whitespace-Zeichen findet,
somit scheint es wahrscheintlich zu sein, dass ein Stern ``eine unbestimmte
Zahl'' bedeutet. Tatsächlich ist der Stern eines der Zeichen, die es erlauben,
mehrere Vorkommen eines Musters zu finden.
Wenn r für den unmittelbar zuvor stehenden regulären Ausdruck
innerhalb einer Musters steht, dann findet:
r* |
null oder mehr Vorkommen von r. |
r+ |
ein oder mehr Vorkommen von r. |
r? |
null oder ein Vorkommen von r. |
r{m,n} |
mindestens ``m'' und höchstens ``n'' Vorkommen von r. |
r{m,} |
mindestens ``m'' Vorkommen von r. |
/ab+/
findet ein ``a'' gefolgt ein einem oder mehreren ``b''s, keine Sequenz von
``ab''s. Gehen Sie auch mit *
-Konstrukten vorsichtig um -- das Muster
/a*/ findet jeden String; in jedem String kommen null oder mehr ``a''s vor.
Diese Mustern werden gierig genannt, da sie, sofern nicht anders
angegeben, so viel wie möglich vom String zurückliefern. Dieses Verhalten
können Sie ändern, indem Sie ein Fragezeichen anhängen, damit der reguläre
Ausdruck nur den kleinstmöglichen Teilstring findet.
a = "The moon is made of cheese" |
||
showRE(a, /\w+/) |
» | <<The>> moon is made of cheese |
showRE(a, /\s.*\s/) |
» | The<< moon is made of >>cheese |
showRE(a, /\s.*?\s/) |
» | The<< moon >>is made of cheese |
showRE(a, /[aeiou]{2,99}/) |
» | The m<<oo>>n is made of cheese |
showRE(a, /mo?o/) |
» | The <<moo>>n is made of cheese |
a = "red ball blue sky" |
||
showRE(a, /d|e/) |
» | r<<e>>d ball blue sky |
showRE(a, /al|lu/) |
» | red b<<al>>l blue sky |
showRE(a, /red ball|angry sky/) |
» | <<red ball>> blue sky |
showRE('banana', /an*/) |
» | b<<an>>ana |
showRE('banana', /(an)*/) |
» | <<>>banana |
showRE('banana', /(an)+/) |
» | b<<anan>>a |
a = 'red ball blue sky' |
||
showRE(a, /blue|red/) |
» | <<red>> ball blue sky |
showRE(a, /(blue|red) \w+/) |
» | <<red ball>> blue sky |
showRE(a, /(red|blue) \w+/) |
» | <<red ball>> blue sky |
showRE(a, /red|blue \w+/) |
» | <<red>> ball blue sky |
showRE(a, /red (ball|angry) sky/) |
» | no match |
a = 'the red angry sky' |
||
showRE(a, /red (ball|angry) sky/) |
» | the <<red angry sky>> |
\1
auf die erste Gruppe, mit \2
auf die zweite Gruppe
usw. verweisen. Außerhalb des Musters dienen die speziellen Variablen
$1
, $2
usw. dem selben Zweck.
"12:50am" =~ /(\d\d):(\d\d)(..)/ |
» | 0 |
"Hour is #$1, minute #$2" |
» | "Hour is 12, minute 50" |
"12:50am" =~ /((\d\d):(\d\d))(..)/ |
» | 0 |
"Time is #$1" |
» | "Time is 12:50" |
"Hour is #$2, minute #$3" |
» | "Hour is 12, minute 50" |
"AM/PM is #$4" |
» | "AM/PM is am" |
# match duplicated letter |
||
showRE('He said "Hello"', /(\w)\1/) |
» | He said "He<<ll>>o" |
# match duplicated substrings |
||
showRE('Mississippi', /(\w+)\1/) |
» | M<<ississ>>ippi |
showRE('He said "Hello"', /(["']).*?\1/) |
» | He said <<"Hello">> |
showRE("He said 'Hello'", /(["']).*?\1/) |
» | He said <<'Hello'>> |
/a.*b.*c.*d.*e/
durchsuchen und ``absconded'' oder ``ambuscade''
finden. Das muss doch etwas wert sein.
Manchmal kommt es jedoch vor, dass Sie Dinge anhand eines Musters ändern
müssen. Gehen wir zurück zu unserer Datei mit der Songliste. Wer auch immer sie
erstellte, hat die Namen der Künstler in Kleinbuchstaben eingebeben. Wenn wir
sie aber am Display unserer Jukebox anzeigen, würden Sie aber in gemischter
Schreibweise besser aussehen. Wie können wir nun den ersten Buchstaben jedes
Wortes in einen Großbuchstaben ändern?
Die Methoden
String#sub
und
String#gsub
suchen nach einem Teil eines Strings, der auf ihr erstes Argument passt und
ersetzen ihn mit ihrem zweiten Argument
String#sub
führt nur eine Änderung durch, während
String#gsub
jedes Vorkommen des Musters ersetzt. Beide Routinen geben ein Objekt von
String
mit der ersetzen Version des Strings zurück. Die
Versionen
String#sub!
und
String#gsub!
verändern den ursprünglichen String.
a = "the quick brown fox" |
||
a.sub(/[aeiou]/, '*') |
» | "th* quick brown fox" |
a.gsub(/[aeiou]/, '*') |
» | "th* q**ck br*wn f*x" |
a.sub(/\s\S+/, '') |
» | "the brown fox" |
a.gsub(/\s\S+/, '') |
» | "the" |
String
oder
ein Block sein. Wenn ein Block verwendet wird, wird der
String
mit dem Wert des Blocks ersetzt.
a = "the quick brown fox" |
||
a.sub(/^./) { $&.upcase } |
» | "The quick brown fox" |
a.gsub(/[aeiou]/) { $&.upcase } |
» | "thE qUIck brOwn fOx" |
\b\w
-- such' nach einer Wortgrenze gefolgt von einem Wortzeichen.
Wir kombinieren das mit gsub
und können die Künstlernamen
verändern:
def mixedCase(aName) |
||
aName.gsub(/\b\w/) { $&.upcase } |
||
end |
||
|
||
mixedCase("fats waller") |
» | "Fats Waller" |
mixedCase("louis armstrong") |
» | "Louis Armstrong" |
mixedCase("strength in numbers") |
» | "Strength In Numbers" |
\1
, \2
usw.
innerhalb eines Musters verfügbar sind und für die n. Gruppe stehen,
die bis jetzt gefunden wurde.
Dieselben Sequenzen können auch im zweiten Argument der Methoden
sub
und gsub
angesprochen werden.
"fred:smith".sub(/(\w+):(\w+)/, '\2, \1') |
» | "smith, fred" |
"nercpyitno".gsub(/(.)(.)/, '\2\1') |
» | "encryption" |
\&
(letzter Fund), \+
(letzte gefundene
Gruppe), \`
(String vor dem Fund), \'
(String nach dem Fund)
und \\
(ein Backslash). Es wird verwirrend, wenn Sie versuchen, einen
Backslash in einer Ersetzung zu verwenden. Das Offensichtlichste wäre,
folgendes zu schreiben:
str.gsub(/\\/, '\\\\') |
str
durch
zwei zu ersetzen. Der Programmierer verdoppelte die Backslashes im
Ersetzungstext, wobei er wusste, dass sie bei der Syntaxanalyse zu
``\\
'' konvertiert werden würden.
Wenn die Ersetzung jedoch durchgeführt wird, bearbeitet die Maschine für
reguläre Ausdrücke den String ein weiteres Mal und konvertiert ``\\
''
zu ``\
'', sodass der übrigegebliebene Effekt ist, dass ein einzelner
Backslash durch einen anderen einzelnen Backslash ersetzt wird. Sie müssen
gsub(/\\/, '\\\\\\\\')
schreiben!
str = 'a\b\c' |
» | "a\b\c" |
str.gsub(/\\/, '\\\\\\\\') |
» | "a\\b\\c" |
\&
durch den
gefundenen String ersetzt wird, können Sie das auch so schreiben:
str = 'a\b\c' |
» | "a\b\c" |
str.gsub(/\\/, '\&\&') |
» | "a\\b\\c" |
gsub
verwenden, wird der String, mit
dem ersetzt werden soll, nur ein einziges Mal analysiert, und das Ergebnis ist
so, wie Sie es beabsichtigt haben.
str = 'a\b\c' |
» | "a\b\c" |
str.gsub(/\\/) { '\\\\' } |
» | "a\\b\\c" |
case
-Ausdruck, den wir ab Seite 83 besprechen.
def unescapeHTML(string) str = string.dup str.gsub!(/&(.*?);/n) { match = $1.dup case match when /\Aamp\z/ni then '&' when /\Aquot\z/ni then '"' when /\Agt\z/ni then '>' when /\Alt\z/ni then '<' when /\A#(\d+)\z/n then Integer($1).chr when /\A#x([0-9a-f]+)\z/ni then $1.hex.chr end } str end puts unescapeHTML("1<2 && 4>3") puts unescapeHTML(""A" = A = A") |
1<2 && 4>3 "A" = A = A |
Regexp
(die ab Seite 366 dokumentiert
wird).
re = /cat/ |
||
re.type |
» | Regexp |
Regexp#match
findet einen regulären Ausdruck in einem String. Wenn sie erfolglos war, gibt
sie nil
zurück, andernfalls eine Instanz der Klasse
MatchData
, deren Dokumentation auf Seite 340 beginnt.
Und dieses Objekt von MatchData
lässt Ihnen Zugriff auf
alle verfügbaren Informationen über einen Treffer. All die feinen Dinge, die
Sie über die $-Variablen bekommen können, sind in einem handlichen kleinen
Objekt zusammengefasst.
re = /(\d+):(\d+)/ # match a time hh:mm |
||
md = re.match("Time: 12:34am") |
||
md.type |
» | MatchData |
md[0] # == $& |
» | "12:34" |
md[1] # == $1 |
» | "12" |
md[2] # == $2 |
» | "34" |
md.pre_match # == $` |
» | "Time: " |
md.post_match # == $' |
» | "am" |
Regexp
-Objekt in zwei Strings. Jede
Suche gibt ein eigenes MatchData
-Objekt zurück. Wir
untersuchen die beiden Objekte, indem wir die beiden Musterfelder überprüfen.
re = /(\d+):(\d+)/ # match a time hh:mm |
||
md1 = re.match("Time: 12:34am") |
||
md2 = re.match("Time: 10:30pm") |
||
md1[1, 2] |
» | ["12", "34"] |
md2[1, 2] |
» | ["10", "30"] |
nil
oder ein
MatchData
-Objekt) in einer für den Thread lokalen
Variable (die mit $~
ansprechbar ist).
Alle anderen Variablen werden dann von diesem Objekt abgeleitet.
Obwohl wir uns nicht wirklich einen Verwendungszweck für den folgenden Code
vorstellen können, zeigt er, dass all die anderen
MatchData
-ähnlichen $-Variablen wirklich vom Wert von
$~
abgeleitet werden.
re = /(\d+):(\d+)/ |
||
md1 = re.match("Time: 12:34am") |
||
md2 = re.match("Time: 10:30pm") |
||
[ $1, $2 ] # last successful match |
» | ["10", "30"] |
$~ = md1 |
||
[ $1, $2 ] # previous successful match |
» | ["12", "34"] |
MatchData
-Objekten zu kümmern.
Für den tagtäglichen Gebrauch sind sie einfach bequemer. Manchmal können wir's
einfach nicht lassen, pragmatisch zu sein.