class Regexp

Ein regulärer Ausdruck (auch regexp genannt) ist ein Abgleichmuster (auch einfach Muster genannt).

Eine gängige Notation für eine Regexp verwendet einschließende Schrägstriche

/foo/

Eine Regexp kann auf einen Zielstring angewendet werden; Der Teil des Strings (falls vorhanden), der dem Muster entspricht, wird als Treffer bezeichnet und kann als übereinstimmend bezeichnet werden

re = /red/
re.match?('redirect') # => true   # Match at beginning of target.
re.match?('bored')    # => true   # Match at end of target.
re.match?('credit')   # => true   # Match within target.
re.match?('foo')      # => false  # No match.

Verwendung von Regexp

Eine Regexp kann verwendet werden

Regexp-Objekte

Ein Regexp-Objekt hat

Erstellen einer Regexp

Ein regulärer Ausdruck kann erstellt werden mit

Methode match

Jede der Methoden Regexp#match, String#match und Symbol#match gibt ein MatchData-Objekt zurück, wenn ein Treffer gefunden wurde, ansonsten nil; jede setzt auch globale Variablen

'food'.match(/foo/) # => #<MatchData "foo">
'food'.match(/bar/) # => nil

Operator =~

Jeder der Operatoren Regexp#=~, String#=~ und Symbol#=~ gibt einen ganzzahligen Offset zurück, wenn ein Treffer gefunden wurde, ansonsten nil; jeder setzt auch globale Variablen

/bar/ =~ 'foo bar' # => 4
'foo bar' =~ /bar/ # => 4
/baz/ =~ 'foo bar' # => nil

Methode match?

Jede der Methoden Regexp#match?, String#match? und Symbol#match? gibt true zurück, wenn ein Treffer gefunden wurde, ansonsten false; keine setzt globale Variablen

'food'.match?(/foo/) # => true
'food'.match?(/bar/) # => false

Globale Variablen

Bestimmte regexp-orientierte Methoden weisen globalen Variablen Werte zu

Die betroffenen globalen Variablen sind

Diese Variablen, außer $~, sind Kurzformen für Methoden von $~. Siehe Globale Variablen-Äquivalenz bei MatchData.

Beispiele

# Matched string, but no matched groups.
'foo bar bar baz'.match('bar')
$~ # => #<MatchData "bar">
$& # => "bar"
$` # => "foo "
$' # => " bar baz"
$+ # => nil
$1 # => nil

# Matched groups.
/s(\w{2}).*(c)/.match('haystack')
$~ # => #<MatchData "stac" 1:"ta" 2:"c">
$& # => "stac"
$` # => "hay"
$' # => "k"
$+ # => "c"
$1 # => "ta"
$2 # => "c"
$3 # => nil

# No match.
'foo'.match('bar')
$~ # => nil
$& # => nil
$` # => nil
$' # => nil
$+ # => nil
$1 # => nil

Beachten Sie, dass Regexp#match?, String#match? und Symbol#match? keine globalen Variablen setzen.

Quellen

Wie oben gezeigt, verwendet die einfachste Regexp einen literalen Ausdruck als Quelle

re = /foo/              # => /foo/
re.match('food')        # => #<MatchData "foo">
re.match('good')        # => nil

Eine reichhaltige Sammlung verfügbarer Unterdrücke verleiht der Regexp große Macht und Flexibilität

Sonderzeichen

Regexp-Sonderzeichen, genannt Metazeichen, haben in bestimmten Kontexten besondere Bedeutungen; je nach Kontext sind dies manchmal Metazeichen

. ? - + * ^ \ | $ ( ) [ ] { }

Um ein Metazeichen buchstäblich abzugleichen, maskieren Sie es mit einem Backslash

# Matches one or more 'o' characters.
/o+/.match('foo')  # => #<MatchData "oo">
# Would match 'o+'.
/o\+/.match('foo') # => nil

Um einen Backslash buchstäblich abzugleichen, maskieren Sie ihn mit einem Backslash

/\./.match('\.')  # => #<MatchData ".">
/\\./.match('\.') # => #<MatchData "\\.">

Die Methode Regexp.escape gibt einen maskierten String zurück

Regexp.escape('.?-+*^\|$()[]{}')
# => "\\.\\?\\-\\+\\*\\^\\\\\\|\\$\\(\\)\\[\\]\\{\\}"

Quellenliterale

Das Quellenliteral verhält sich weitgehend wie ein doppelt Anführungszeichen enthaltender String; siehe Literale mit doppelten Anführungszeichen.

Insbesondere kann ein Quellenliteral interpolierte Ausdrücke enthalten

s = 'foo'         # => "foo"
/#{s}/            # => /foo/
/#{s.capitalize}/ # => /Foo/
/#{2 + 2}/        # => /4/

Es gibt Unterschiede zwischen einem gewöhnlichen String-Literal und einem Quellenliteral; siehe Kurzschreibzeichenklassen.

Zeichenklassen

Eine Zeichenklasse ist durch eckige Klammern abgegrenzt; sie gibt an, dass bestimmte Zeichen an einer bestimmten Stelle im Zielstring übereinstimmen

# This character class will match any vowel.
re = /B[aeiou]rd/
re.match('Bird') # => #<MatchData "Bird">
re.match('Bard') # => #<MatchData "Bard">
re.match('Byrd') # => nil

Eine Zeichenklasse kann Bindestriche enthalten, um Zeichenbereiche anzugeben

# These regexps have the same effect.
/[abcdef]/.match('foo') # => #<MatchData "f">
/[a-f]/.match('foo')    # => #<MatchData "f">
/[a-cd-f]/.match('foo') # => #<MatchData "f">

Wenn das erste Zeichen einer Zeichenklasse ein Caret (^) ist, wird die Bedeutung der Klasse umgekehrt: Sie entspricht jedem Zeichen außer denen, die angegeben sind.

/[^a-eg-z]/.match('f') # => #<MatchData "f">

Eine Zeichenklasse kann eine andere Zeichenklasse enthalten. An sich ist dies nicht nützlich, da [a-z[0-9]] die gleiche Menge wie [a-z0-9] beschreibt.

Zeichenklassen unterstützen jedoch auch den Operator &&, der die Schnittmenge seiner Argumente durchführt. Die beiden können wie folgt kombiniert werden

/[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))

Dies ist äquivalent zu

/[abh-w]/

Kurzschreibzeichenklassen

Jedes der folgenden Metazeichen dient als Kurzform für eine Zeichenklasse

Anker

Ein Anker ist eine Metasekvens, die eine Null-Breiten-Position zwischen Zeichen im Zielstring abgleicht.

Für einen Unterausdruck ohne Anker kann der Abgleich an jeder Stelle im Zielstring beginnen

/real/.match('surrealist') # => #<MatchData "real">

Für einen Unterausdruck mit einem Anker muss der Abgleich am übereinstimmenden Anker beginnen.

Rand-Anker

Jeder dieser Anker gleicht eine Grenze ab

Lookaround-Anker

Lookahead-Anker

Lookbehind-Anker

Das folgende Muster verwendet positives Lookahead und positives Lookbehind, um Text innerhalb von -Tags abzugleichen, ohne die Tags in den Treffer einzuschließen

/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favors the <b>bold</b>.")
# => #<MatchData "bold">

Das Muster im Lookbehind muss eine feste Breite haben. Aber Top-Level-Alternativen können unterschiedliche Längen haben. z. B. (?<=a|bc) ist OK. (?<=aaa(?:b|cd)) ist nicht erlaubt.

Match-Reset-Anker

Alternation

Das vertikale Balken-Metazeichen (|) kann innerhalb von Klammern verwendet werden, um Alternation auszudrücken: zwei oder mehr Unterausdrücke, von denen jeder den Zielstring abgleichen kann.

Zwei Alternativen

re = /(a|b)/
re.match('foo') # => nil
re.match('bar') # => #<MatchData "b" 1:"b">

Vier Alternativen

re = /(a|b|c|d)/
re.match('shazam') # => #<MatchData "a" 1:"a">
re.match('cold')   # => #<MatchData "c" 1:"c">

Jede Alternative ist ein Unterausdruck und kann aus anderen Unterausdrücken bestehen

re = /([a-c]|[x-z])/
re.match('bar') # => #<MatchData "b" 1:"b">
re.match('ooz') # => #<MatchData "z" 1:"z">

Die Methode Regexp.union bietet eine bequeme Möglichkeit, eine Regexp mit Alternativen zu erstellen.

Quantifizierer

Eine einfache Regexp gleicht ein Zeichen ab

/\w/.match('Hello')  # => #<MatchData "H">

Ein hinzugefügter Quantifizierer gibt an, wie viele Übereinstimmungen erforderlich oder zulässig sind

Gieriges, Lässiges oder Besessener Abgleich

Quantifizierer-Abgleiche können gierig, lässig oder besessener sein

Mehr

Gruppen und Erfassungen

Eine einfache Regexp hat (höchstens) einen Treffer

re = /\d\d\d\d-\d\d-\d\d/
re.match('1943-02-04')      # => #<MatchData "1943-02-04">
re.match('1943-02-04').size # => 1
re.match('foo')             # => nil

Das Hinzufügen von einem oder mehreren Klammerpaaren, (unterausdruck), definiert Gruppen, die zu mehreren übereinstimmenden Teilstrings führen können, die als Erfassungen bezeichnet werden

re = /(\d\d\d\d)-(\d\d)-(\d\d)/
re.match('1943-02-04')      # => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
re.match('1943-02-04').size # => 4

Die erste Erfassung ist der gesamte übereinstimmende String; die anderen Erfassungen sind die übereinstimmenden Teilstrings aus den Gruppen.

Eine Gruppe kann einen Quantifizierer haben

re = /July 4(th)?/
re.match('July 4')   # => #<MatchData "July 4" 1:nil>
re.match('July 4th') # => #<MatchData "July 4th" 1:"th">

re = /(foo)*/
re.match('')       # => #<MatchData "" 1:nil>
re.match('foo')    # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">

re = /(foo)+/
re.match('')       # => nil
re.match('foo')    # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">

Das zurückgegebene MatchData-Objekt bietet Zugriff auf die übereinstimmenden Teilstrings

re = /(\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04')
# => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
md[0] # => "1943-02-04"
md[1] # => "1943"
md[2] # => "02"
md[3] # => "04"

Nicht-erfassende Gruppen

Eine Gruppe kann nicht-erfassend gemacht werden; sie ist immer noch eine Gruppe (und kann z.B. einen Quantifizierer haben), aber ihr übereinstimmender Teilstring ist nicht unter den Erfassungen enthalten.

Eine nicht-erfassende Gruppe beginnt mit ?: (innerhalb der Klammern)

# Don't capture the year.
re = /(?:\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04') # => #<MatchData "1943-02-04" 1:"02" 2:"04">

Rückbezüge

Ein Gruppen-Treffer kann auch innerhalb der Regexp selbst referenziert werden; eine solche Referenz wird als Rückbezug bezeichnet

/[csh](..) [csh]\1 in/.match('The cat sat in the hat')
# => #<MatchData "cat sat in" 1:"at">

Diese Tabelle zeigt, wie jeder Unterausdruck in der obigen Regexp einen Teilstring im Zielstring abgleicht

| Subexpression in Regexp   | Matching Substring in Target String |
|---------------------------|-------------------------------------|
|       First '[csh]'       |            Character 'c'            |
|          '(..)'           |        First substring 'at'         |
|      First space ' '      |      First space character ' '      |
|       Second '[csh]'      |            Character 's'            |
| '\1' (backreference 'at') |        Second substring 'at'        |
|           ' in'           |            Substring ' in'          |

Eine Regexp kann beliebig viele Gruppen enthalten

Benannte Erfassungen

Wie oben gezeigt, kann auf eine Erfassung durch ihre Nummer verwiesen werden. Eine Erfassung kann auch einen Namen haben, vorangestellt als ?<name> oder ?'name', und der Name (symbolisiert) kann als Index in MatchData[] verwendet werden

md = /\$(?<dollars>\d+)\.(?'cents'\d+)/.match("$3.67")
# => #<MatchData "$3.67" dollars:"3" cents:"67">
md[:dollars]  # => "3"
md[:cents]    # => "67"
# The capture numbers are still valid.
md[2]         # => "67"

Wenn eine Regexp eine benannte Erfassung enthält, gibt es keine unbenannten Erfassungen

/\$(?<dollars>\d+)\.(\d+)/.match("$3.67")
# => #<MatchData "$3.67" dollars:"3">

Eine benannte Gruppe kann als \k<name> zurückbezogen werden

/(?<vowel>[aeiou]).\k<vowel>.\k<vowel>/.match('ototomy')
# => #<MatchData "ototo" vowel:"o">

Wenn (und nur wenn) eine Regexp benannte Erfassungsgruppen enthält und vor dem =~-Operator steht, werden die erfassten Teilstrings lokalen Variablen mit entsprechenden Namen zugewiesen

/\$(?<dollars>\d+)\.(?<cents>\d+)/ =~ '$3.67'
dollars # => "3"
cents   # => "67"

Die Methode Regexp#named_captures gibt einen Hash der Erfassungsnamen und Teilstrings zurück; die Methode Regexp#names gibt ein Array der Erfassungsnamen zurück.

Atomare Gruppierung

Eine Gruppe kann mit (?>unterausdruck) atomar gemacht werden.

Dies führt dazu, dass der Unterausdruck unabhängig vom Rest des Ausdrucks abgeglichen wird, so dass der übereinstimmende Teilstring für den Rest des Abgleichs fest wird, es sei denn, der gesamte Unterausdruck muss aufgegeben und anschließend wieder besucht werden.

Auf diese Weise wird Unterausdruck als unteilbares Ganzes behandelt. Atomare Gruppierung wird typischerweise verwendet, um Muster zu optimieren und unnötiges Backtracking zu verhindern.

Beispiel (ohne atomare Gruppierung)

/".*"/.match('"Quote"') # => #<MatchData "\"Quote\"">

Analyse

  1. Der führende Unterausdruck " im Muster gleicht das erste Zeichen " im Zielstring ab.

  2. Der nächste Unterausdruck .* gleicht den nächsten Teilstring Quote" (einschließlich des abschließenden Anführungszeichens) ab.

  3. Nun ist nichts mehr im Zielstring übrig, um den abschließenden Unterausdruck " im Muster abzugleichen; dies würde dazu führen, dass der Gesamt-Abgleich fehlschlägt.

  4. Der übereinstimmende Teilstring wird um eine Position zurückverfolgt: Quote.

  5. Der letzte Unterausdruck " gleicht nun den letzten Teilstring " ab, und der Gesamt-Abgleich gelingt.

Wenn der Unterausdruck .* atomar gruppiert wird, wird das Backtracking deaktiviert und der Gesamt-Abgleich schlägt fehl

/"(?>.*)"/.match('"Quote"') # => nil

Atomare Gruppierung kann die Leistung beeinträchtigen; siehe Atomic Group.

Unterausdruck-Aufrufe

Wie oben gezeigt, gibt eine Rückbezugsnummer (\n) oder ein Name (\k<name>) Zugriff auf einen erfassten Teilstring; der entsprechende Regexp-Unterausdruck kann ebenfalls über die Nummer (\gn) oder den Namen (\g<name>) abgerufen werden

/\A(?<paren>\(\g<paren>*\))*\z/.match('(())')
# ^1
#      ^2
#           ^3
#                 ^4
#      ^5
#           ^6
#                      ^7
#                       ^8
#                       ^9
#                           ^10

Das Muster

  1. Passt am Anfang des Strings, d. h. vor dem ersten Zeichen.

  2. Gibt eine benannte Gruppe paren ein.

  3. Passt das erste Zeichen im String, '('.

  4. Ruft die Gruppe paren erneut auf, d. h. rekursiert zum zweiten Schritt.

  5. Ruft die Gruppe paren erneut auf.

  6. Passt das zweite Zeichen im String, '('.

  7. Versucht, paren ein drittes Mal aufzurufen, scheitert jedoch, da dies einen erfolgreichen Gesamt-Abgleich verhindern würde.

  8. Passt das dritte Zeichen im String, ')'; markiert das Ende des zweiten rekursiven Aufrufs

  9. Passt das vierte Zeichen im String, ')'.

  10. Passt das Ende des Strings.

Siehe Unterausdruck-Aufrufe.

Bedingte Ausdrücke

Die bedingte Konstruktion hat die Form (?(cond)yes|no), wobei

Beispiele

re = /\A(foo)?(?(1)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" 1:"foo" 2:"T" 3:nil>
re.match('F')    # => #<MatchData "F" 1:nil 2:nil 3:"F">
re.match('fooF') # => nil
re.match('T')    # => nil

re = /\A(?<xyzzy>foo)?(?(<xyzzy>)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" xyzzy:"foo">
re.match('F')    # => #<MatchData "F" xyzzy:nil>
re.match('fooF') # => nil
re.match('T')    # => nil

Abwesenheitsoperator

Der Abwesenheitsoperator ist eine spezielle Gruppe, die alles abgleicht, was die enthaltenen Unterausdrücke *nicht* abgleicht.

/(?~real)/.match('surrealist') # => #<MatchData "surrea">
/(?~real)ist/.match('surrealist') # => #<MatchData "ealist">
/sur(?~real)ist/.match('surrealist') # => nil

Unicode

Unicode-Eigenschaften

Die Konstruktion /\p{property_name}/ (mit Kleinbuchstaben p) gleicht Zeichen anhand eines Unicode-Eigenschaftsnamens ab, ähnlich wie eine Zeichenklasse; die Eigenschaft Alpha spezifiziert alphabetische Zeichen

/\p{Alpha}/.match('a') # => #<MatchData "a">
/\p{Alpha}/.match('1') # => nil

Eine Eigenschaft kann invertiert werden, indem dem Namen ein Caret-Zeichen (^) vorangestellt wird

/\p{^Alpha}/.match('1') # => #<MatchData "1">
/\p{^Alpha}/.match('a') # => nil

Oder indem \P (Großbuchstabe P) verwendet wird

/\P{Alpha}/.match('1') # => #<MatchData "1">
/\P{Alpha}/.match('a') # => nil

Siehe Unicode-Eigenschaften für Regexps, die auf den zahlreichen Eigenschaften basieren.

Einige häufig verwendete Eigenschaften entsprechen POSIX-Klammerausdrücken

Diese werden ebenfalls häufig verwendet

Unicode-Zeichenkategorien

Ein Unicode-Zeichenkategoriename

Beispiele

/\p{lu}/                # => /\p{lu}/
/\p{LU}/                # => /\p{LU}/
/\p{Uppercase Letter}/  # => /\p{Uppercase Letter}/
/\p{Uppercase_Letter}/  # => /\p{Uppercase_Letter}/
/\p{UPPERCASE-LETTER}/  # => /\p{UPPERCASE-LETTER}/

Nachfolgend sind die Abkürzungen und Namen der Unicode-Zeichenkategorien aufgeführt. Aufzählungen von Zeichen in jeder Kategorie finden Sie unter den Links.

Buchstaben

Markierungen

Zahlen

Satzzeichen

Unicode-Skripte und -Blöcke

Zu den Unicode-Eigenschaften gehören

POSIX-Klammerausdrücke

Ein POSIX-Klammerausdruck ist ebenfalls ähnlich einer Zeichenklasse. Diese Ausdrücke bieten eine portable Alternative zu den oben genannten, mit dem zusätzlichen Vorteil, Nicht-ASCII-Zeichen einzuschließen

Die POSIX-Klammerausdrücke

Ruby unterstützt auch diese (nicht-POSIX) Klammerausdrücke

Comments

Ein Kommentar kann mit der Konstruktion (?#Kommentar) in einem Regexp-Muster enthalten sein, wobei Kommentar ein Teilstring ist, der ignoriert werden soll. beliebiger Text, der vom Regexp-Engine ignoriert wird

/foo(?#Ignore me)bar/.match('foobar') # => #<MatchData "foobar">

Der Kommentar darf kein unmaskiertes Trennzeichen enthalten.

Siehe auch Erweiterter Modus.

Modi

Jeder dieser Modifikatoren setzt einen Modus für die Regexp

Keine, alle oder keine dieser können angewendet werden.

Modifikatoren i, m und x können auf Unterausdrücke angewendet werden

Beispiel

re = /(?i)te(?-i)st/
re.match('test') # => #<MatchData "test">
re.match('TEst') # => #<MatchData "TEst">
re.match('TEST') # => nil
re.match('teST') # => nil

re = /t(?i:e)st/
re.match('test') # => #<MatchData "test">
re.match('tEst') # => #<MatchData "tEst">
re.match('tEST') # => nil

Die Methode Regexp#options gibt eine Ganzzahl zurück, deren Wert die Einstellungen für den Groß-/Kleinschreibungsunempfindlichen Modus, den Mehrzeiligen Modus und den Erweiterten Modus anzeigt.

Groß-/Kleinschreibungsunempfindlicher Modus

Standardmäßig ist eine Regexp Groß-/Kleinschreibungs-sensitiv

/foo/.match('FOO')  # => nil

Der Modifikator i aktiviert den Groß-/Kleinschreibungsunempfindlichen Modus

/foo/i.match('FOO')
# => #<MatchData "FOO">

Die Methode Regexp#casefold? gibt zurück, ob der Modus Groß-/Kleinschreibungs-unempfindlich ist.

Mehrzeiliger Modus

Der Mehrzeilige Modus in Ruby ist das, was man allgemein als "Dot-All-Modus" bezeichnet

Im Gegensatz zu anderen Sprachen beeinflusst der Modifikator m die Anker ^ und $ nicht. Diese Anker entsprechen in Ruby immer Zeilengrenzen.

Erweiterter Modus

Der Modifikator x aktiviert den Erweiterten Modus, was bedeutet, dass

Im Erweiterten Modus können Leerzeichen und Kommentare verwendet werden, um eine selbstdokumentierende Regexp zu erstellen.

Regexp nicht im Erweiterten Modus (gleicht einige römische Ziffern ab)

pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
re = /#{pattern}/
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">

Regexp im Erweiterten Modus

pattern = <<-EOT
  ^                   # beginning of string
  M{0,3}              # thousands - 0 to 3 Ms
  (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs),
                      #            or 500-800 (D, followed by 0 to 3 Cs)
  (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs),
                      #        or 50-80 (L, followed by 0 to 3 Xs)
  (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is),
                      #        or 5-8 (V, followed by 0 to 3 Is)
  $                   # end of string
EOT
re = /#{pattern}/x
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">

Interpolationsmodus

Der Modifikator o bedeutet, dass beim ersten Auftreten einer literalen Regexp mit Interpolationen das generierte Regexp-Objekt gespeichert und für alle zukünftigen Auswertungen dieser literalen Regexp verwendet wird. Ohne den Modifikator o wird die generierte Regexp nicht gespeichert, so dass jede Auswertung der literalen Regexp ein neues Regexp-Objekt generiert.

Ohne Modifikator o

def letters; sleep 5; /[A-Z][a-z]/; end
words = %w[abc def xyz]
start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/) }
Time.now - start # => 15.0174892

Mit Modifikator o

start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/o) }
Time.now - start # => 5.0010866

Beachten Sie, dass, wenn die literale Regexp keine Interpolationen hat, das o-Verhalten der Standard ist.

Kodierungen

Standardmäßig hat eine Regexp mit nur US-ASCII-Zeichen eine US-ASCII-Kodierung

re = /foo/
re.source.encoding # => #<Encoding:US-ASCII>
re.encoding        # => #<Encoding:US-ASCII>

Eine reguläre Ausdruck, die Nicht-US-ASCII-Zeichen enthält, wird angenommen, die Quellkodierung zu verwenden. Dies kann mit einem der folgenden Modifikatoren überschrieben werden.

Eine Regexp kann mit einem Zielstring abgeglichen werden, wenn entweder

Wenn ein Abgleich zwischen inkompatiblen Kodierungen versucht wird, wird eine Encoding::CompatibilityError-Ausnahme ausgelöst.

Beispiel

re = eval("# encoding: ISO-8859-1\n/foo\\xff?/")
re.encoding                 # => #<Encoding:ISO-8859-1>
re =~ "foo".encode("UTF-8") # => 0
re =~ "foo\u0100"           # Raises Encoding::CompatibilityError

Die Kodierung kann explizit festgelegt werden, indem Regexp::FIXEDENCODING in das zweite Argument für Regexp.new aufgenommen wird

# Regexp with encoding ISO-8859-1.
re = Regexp.new("a".force_encoding('iso-8859-1'), Regexp::FIXEDENCODING)
re.encoding  # => #<Encoding:ISO-8859-1>
# Target string with encoding UTF-8.
s = "a\u3042"
s.encoding   # => #<Encoding:UTF-8>
re.match(s)  # Raises Encoding::CompatibilityError.

Timeouts

Wenn entweder die Regexp-Quelle oder der Zielstring aus nicht vertrauenswürdigen Eingaben stammen, könnten bösartige Werte zu einem Denial-of-Service-Angriff werden; um einen solchen Angriff zu verhindern, ist es ratsam, ein Timeout festzulegen.

Regexp hat zwei Timeout-Werte

Wenn regexp.timeout nil ist, "fällt" das Timeout auf Regexp.timeout durch; wenn regexp.timeout nicht nil ist, steuert dieser Wert das Timeout

| regexp.timeout Value | Regexp.timeout Value |            Result           |
|----------------------|----------------------|-----------------------------|
|         nil          |          nil         |       Never times out.      |
|         nil          |         Float        | Times out in Float seconds. |
|        Float         |          Any         | Times out in Float seconds. |

Optimierung

Für bestimmte Werte des Musters und des Zielstrings kann die Abgleichzeit polynomisch oder exponentiell in Bezug auf die Eingabegröße wachsen; die daraus resultierende potenzielle Schwachstelle ist der Denial-of-Service-Angriff durch reguläre Ausdrücke (ReDoS).

Regexp-Abgleich kann eine Optimierung anwenden, um ReDoS-Angriffe zu verhindern. Wenn die Optimierung angewendet wird, erhöht sich die Abgleichzeit linear (nicht polynomisch oder exponentiell) in Bezug auf die Eingabegröße, und ein ReDoS-Angriff ist nicht möglich.

Diese Optimierung wird angewendet, wenn das Muster diese Kriterien erfüllt

Sie können die Methode Regexp.linear_time? verwenden, um festzustellen, ob ein Muster diese Kriterien erfüllt

Regexp.linear_time?(/a*/)     # => true
Regexp.linear_time?('a*')     # => true
Regexp.linear_time?(/(a*)\1/) # => false

Ein nicht vertrauenswürdiger Quellcode ist jedoch möglicherweise nicht sicher, selbst wenn die Methode true zurückgibt, da die Optimierung Memoisation verwendet (was zu einem großen Speicherverbrauch führen kann).

Referenzen

Lesen

Entdecken, testen