Im ersten Tutorial dieser Serie haben wir die Grundlagen regulärer Ausdrücke einschließlich der Syntax zum Schreiben regulärer Ausdrücke untersucht. In diesem Lernprogramm wenden wir das bisher Gelernte an, um reguläre Ausdrücke in Swift zu nutzen.
Öffne Xcode, erstelle einen neuen Spielplatz und benenne ihn RegExTut, und setzen Plattform zu OS X. Die Wahl der Plattform, iOS oder OS X, macht keinen Unterschied hinsichtlich der API, die wir verwenden werden.
Bevor wir anfangen, gibt es noch etwas, worüber Sie wissen müssen. In Swift müssen Sie zwei Backslashes verwenden, \\
, Für jeden Backslash, den Sie in einem regulären Ausdruck verwenden. Der Grund liegt darin, dass Swift C-String-Literale hat. Der Backslash wird zusätzlich zu seiner Rolle bei der String-Interpolation in Swift als Zeichen-Escape verarbeitet. Mit anderen Worten, Sie müssen dem Escape-Zeichen entkommen. Wenn das komisch klingt, machen Sie sich keine Sorgen. Denken Sie daran, zwei Backslashes anstelle von einem zu verwenden.
In dem ersten, etwas durchdachten Beispiel stützen wir uns auf eine Suche nach einer ganz bestimmten Art von E-Mail-Adresse. Die E-Mail-Adresse erfüllt die folgenden Kriterien:
Fügen Sie dem Spielplatz den folgenden Code hinzu, und gehen Sie Schritt für Schritt durch dieses Code-Snippet.
importiere Kakao // (1): let pat = "\\ b ([az]) \\. ([az] 2,) @ ([az] +) \\. ac \\. uk \\ b "// (2): let testStr =" [email protected], [email protected] [email protected], [email protected], [email protected]. uk "// (3): lass regex = versuchen! NSRegularExpression (Muster: pat, Optionen: []) // (4): let Matches = regex.matchesInString (testStr, Optionen: [], Bereich: NSRange (Speicherort: 0, Länge: testStr.characters.count))
Wir definieren ein Muster. Beachten Sie die doppelt gescheiterten Backslashes. In der (normalen) Regex-Darstellung, wie sie auf der RegExr-Website verwendet wird, wäre dies der Fall ([a-z]) \. ([a-z] 2,) @ ([a-z] +) \. ac \ .uk
. Beachten Sie auch die Verwendung von Klammern. Sie werden verwendet, um Erfassungsgruppen zu definieren, mit denen die Teilzeichenfolgen extrahiert werden können, die mit diesem Teil des regulären Ausdrucks übereinstimmen.
Sie sollten erkennen können, dass die erste Erfassungsgruppe den ersten Buchstaben des Benutzernamens erfasst, der zweite den Nachnamen und der dritte den Namen der Universität. Beachten Sie auch die Verwendung des umgekehrten Schrägstrichs, um das Punktzeichen zu verlassen, um dessen wörtliche Bedeutung darzustellen. Alternativ können wir es auch in einen Zeichensatz setzen ([.]
). In diesem Fall müssten wir uns nicht entziehen.
Dies ist die Zeichenfolge, in der wir nach dem Muster suchen.
Wir schaffen ein NSRegularExpression
Objekt, das Muster ohne Optionen übergeben. In der Liste der Optionen können Sie angeben NSRegularExpressionOption
Konstanten wie:
CaseInsensitive
: Diese Option gibt an, dass bei der Übereinstimmung die Groß- und Kleinschreibung nicht berücksichtigt wird.IgnoreMetacharacters
: Verwenden Sie diese Option, wenn Sie eine Literalübereinstimmung durchführen möchten, dh die Metazeichen haben keine besondere Bedeutung und passen sich selbst als gewöhnliche Zeichen an.AnchorMatchLines
: Verwenden Sie diese Option, wenn Sie möchten ^
und $
Anker, die mit dem Anfang und dem Ende von Zeilen (durch Zeilenumbrüche getrennt) in einer einzelnen Zeichenfolge übereinstimmen, anstatt Anfang und Ende der gesamten Zeichenfolge.Da der Initialisierer wirft, verwenden wir die Versuchen
Stichwort. Wenn wir zum Beispiel einen ungültigen regulären Ausdruck übergeben, wird ein Fehler ausgegeben.
Wir suchen nach Übereinstimmungen in der Testzeichenfolge durch Aufrufen matchesInString (_: Optionen: Bereich :)
, Übergeben eines Bereichs, um anzuzeigen, an welchem Teil der Zeichenfolge wir interessiert sind. Diese Methode akzeptiert auch eine Liste von Optionen. Der Einfachheit halber geben wir in diesem Beispiel keine Optionen ein. Ich werde im nächsten Beispiel über Optionen sprechen.
Die Übereinstimmungen werden als Array von zurückgegeben NSTextCheckingResult
Objekte. Wir können die Übereinstimmungen einschließlich der Erfassungsgruppen wie folgt extrahieren:
für Übereinstimmung in Übereinstimmungen für n in 0…Das obige Snippet durchläuft jedes Element
NSTextCheckingResult
Objekt im Array. DasnumberOfRanges
Die Eigenschaft für jede Übereinstimmung im Beispiel hat einen Wert von 4, eine für den gesamten übereinstimmenden Teilstring, der einer E-Mail-Adresse entspricht (z. B. [email protected]), und die restlichen drei entsprechen den drei Erfassungsgruppen innerhalb der Übereinstimmung ("a", "khan" und "surrey") " beziehungsweise).Das
rangeAtIndex (_ :)
Die Methode gibt den Bereich der Teilzeichenfolgen in der Zeichenfolge zurück, damit wir sie extrahieren können. Beachten Sie das, anstatt zu verwendenrangeAtIndex (0)
, Sie könnten auch die verwendenAngebot
Eigenschaft für das gesamte Spiel.Drücke den Zeige Ergebnis Schaltfläche im Ergebnisfeld rechts. Dies zeigt uns "Surrey", den Wert von
testStr.substringWithRange (r)
für die letzte Iteration der Schleife. Klicken Sie mit der rechten Maustaste auf das Ergebnisfeld und wählen Sie aus Wertehistorie eine Geschichte der Werte anzeigen.Sie können den obigen Code ändern, um mit den Übereinstimmungen und / oder den Capture-Gruppen etwas Sinnvolles zu tun.
Suchen und Ersetzen können auf bequeme Weise mit einer Vorlagenzeichenfolge ausgeführt werden, die eine spezielle Syntax für die Darstellung von Erfassungsgruppen hat. Angenommen, wir wollten jede übereinstimmende E-Mail-Adresse durch einen Teilstring der Form "Nachname, Vorname, Universität" ersetzen. Wir könnten Folgendes tun:
let replaceStr = regex.stringByReplacingMatchesInString (testStr, Optionen: [], Bereich: NSRange (Speicherort: 0, Länge: testStr.characters.count), withTemplate: "($ 2, $ 1, $ 3)")Beachten Sie das
$ n
Syntax in der Vorlage, die als Platzhalter für den Text der Capture-Gruppe fungiertn
. Denk daran, dass$ 0
repräsentiert das gesamte Spiel.2. Ein fortgeschritteneres Beispiel
Das
matchesInString (_: Optionen: Bereich :)
Die Methode ist eine von mehreren Komfortmethoden, auf die sie angewiesen sindenumerateMatchesInString (_: Optionen: Bereich: usingBlock :)
, Dies ist die flexibelste und allgemeinste (und daher komplizierteste) Methode in derNSRegularExpression
Klasse. Diese Methode ruft nach jedem Match einen Block auf, mit dem Sie die gewünschte Aktion ausführen können.Übergeben Sie eine oder mehrere Übereinstimmungsregeln mit
NSMatchingOptions
Konstanten können Sie sicherstellen, dass der Block bei anderen Gelegenheiten aufgerufen wird. Bei lang andauernden Operationen können Sie festlegen, dass der Block regelmäßig aufgerufen wird, und die Operation an einem bestimmten Punkt beenden. Mit demReportCompletion
Option, legen Sie fest, dass der Block nach Abschluss aufgerufen werden soll.Der Block verfügt über einen Flags-Parameter, der einen dieser Zustände meldet, sodass Sie entscheiden können, welche Aktion ausgeführt werden soll. Ähnlich wie einige andere Aufzählungsverfahren in der Stiftung Rahmen kann die Sperre auch nach eigenem Ermessen gekündigt werden. Zum Beispiel, wenn eine lange laufende Übereinstimmung nicht erfolgreich ist oder wenn Sie genügend Übereinstimmungen gefunden haben, um mit der Verarbeitung zu beginnen.
In diesem Szenario durchsuchen wir Text nach Zeichenfolgen, die wie Datumsangaben aussehen, und prüfen, ob ein bestimmtes Datum vorhanden ist. Um das Beispiel überschaubar zu halten, stellen wir uns vor, dass die Datumsfolgen die folgende Struktur haben:
- ein Jahr mit zwei oder vier Ziffern (zum Beispiel 09 oder 2009)
- Nur aus dem gegenwärtigen Jahrhundert (zwischen 2000 und 2099) würde 1982 abgelehnt und 16 automatisch als 2016 interpretiert
- gefolgt von einem Trennzeichen
- gefolgt von einer Zahl zwischen 1 und 12, die den Monat darstellt
- gefolgt von einem Trennzeichen
- abschließend mit einer Zahl zwischen 1 und 31, die den Tag darstellt
Einstellige Monate und Datumsangaben werden möglicherweise mit einer führenden Null aufgefüllt. Gültige Trennzeichen sind ein Bindestrich, ein Punkt und ein Schrägstrich. Abgesehen von den oben genannten Anforderungen können wir nicht prüfen, ob ein Datum tatsächlich gültig ist. Zum Beispiel sind wir gut mit Daten wie 2000-04-31 (April hat nur 30 Tage) und 2009-02-29 (2009 ist kein Schaltjahr, was bedeutet, dass Februar nur 28 Tage hat), die keine echten Daten sind.
Fügen Sie dem Spielplatz den folgenden Code hinzu, und gehen Sie Schritt für Schritt durch dieses Code-Snippet.
// (1): typealias PossibleDate = (Jahr: Int, Monat: Int, Tag: Int) // (2): func dateSearch (Text: String, _ Datum: PossibleDate) -> Bool // (3): let datePattern = "\\ b (?: 20)? (\\ d \\ d) [-. /] (0 - [1-9] | 1 [0-2]) [-. /] (3 [ 0-1] | [1-2] [0-9] | 0? [1-9]) \\ b "let dateRegex = try! NSRegularExpression (Muster: DatePattern, Optionen: []) // (4): var wasFound: Bool = false // (5): dateRegex.enumerateMatchesInString (Text, Optionen: [], Bereich: NSRange (Speicherort: 0, Länge: text.characters.count)) // (6): (match, _, stop) in var dateArr = [Int] () für n in 1… 3 let range = match! .rangeAtIndex (n) let r = text.startIndex.advancedBy (range.location)… < text.startIndex.advancedBy(range.location+range.length) dateArr.append(Int(text.substringWithRange(r))!) // (7): if dateArr[0] == date.year && dateArr[1] == date.month && dateArr[2] == date.day // (8): wasFound = true stop.memory = true return wasFound let text = " 2015/10/10,11-10-20, 13/2/2 1981-2-2 2010-13-10" let date1 = PossibleDate(15, 10, 10) let date2 = PossibleDate(13, 1, 1) dateSearch(text, date1) // returns true dateSearch(text, date2) // returns falseSchritt 1
Das Datum, dessen Existenz wir überprüfen, wird in einem standardisierten Format vorliegen. Wir verwenden ein benanntes Tupel. Wir übergeben nur eine zweistellige ganze Zahl an year, das heißt 16 bedeutet 2016.
Schritt 2
Unsere Aufgabe ist es, Übereinstimmungen aufzuzählen, die wie Datumsangaben aussehen, die Jahr-, Monats- und Tagesbestandteile daraus zu extrahieren und zu prüfen, ob sie dem Datum entsprechen, an dem wir uns übergeben haben. Wir werden eine Funktion erstellen, die all dies für uns erledigt. Die Funktion kehrt zurück
wahr
oderfalsch
abhängig davon, ob das Datum gefunden wurde oder nicht.Schritt 3
Das Datumsmuster weist einige interessante Merkmale auf:
- Beachten Sie das Fragment
(?: 20)?
. Wenn wir dieses Fragment mit ersetzen(20)?
, Hoffentlich würden Sie erkennen, dass dies bedeutet, dass es uns gut geht, dass die "20" (die das Millennium darstellt) im Jahr anwesend ist oder nicht. Die Klammern sind für die Gruppierung notwendig, aber wir möchten mit diesem Klammerpaar keine Capture-Gruppe bilden?:
bisschen ist für.- Die möglichen Trennzeichen innerhalb des Zeichensatzes
[-. /]
Es muss nicht entkommen werden, um ihr wahres Selbst darzustellen. Sie können davon so denken. Der Bindestrich,-
, ist am Anfang und kann daher keinen Bereich darstellen. Und es macht für die Zeit keinen Sinn,.
, jedes Zeichen innerhalb eines Zeichensatzes darzustellen, da er dies auch außerhalb tut.- Wir verwenden den vertikalen Balken intensiv, um die verschiedenen Monats- und Datumsstellen darzustellen.
Schritt 4
Die boolesche Variable
nicht gefunden
wird von der Funktion zurückgegeben und zeigt an, ob das gesuchte Datum gefunden wurde oder nicht.Schritt 5
Das
enumerateMatchesInString (_: Optionen: Bereich: usingBlock :)
wird gerufen Wir verwenden keine der Optionen und übergeben den gesamten Textbereich, den Sie durchsuchen.Schritt 6
Das nach jeder Übereinstimmung aufgerufene Blockobjekt hat drei Parameter:
- das Spiel (a
NSTextCheckingResult
)- Flags, die den aktuellen Status des Übereinstimmungsprozesses darstellen (was wir hier ignorieren)
- ein boolean
halt
Variable, die wir innerhalb des Blocks setzen können, um vorzeitig zu beendenWir verwenden den Booleschen Wert, um den Block zu verlassen, wenn wir das Datum finden, das wir suchen, da wir nicht weiter suchen müssen. Der Code, der die Komponenten des Datums extrahiert, ist dem vorherigen Beispiel sehr ähnlich.
Schritt 7
Wir prüfen, ob die extrahierten Komponenten aus dem übereinstimmenden Teilstring den Komponenten des gewünschten Datums entsprechen. Beachten Sie, dass wir einen Cast zwingen
Int
, was wir sicher nicht scheitern werden, weil wir die entsprechenden Capture-Gruppen erstellt haben, um nur Ziffern zu finden.Schritt 8
Wenn eine Übereinstimmung gefunden wird, setzen wir
nicht gefunden
zuwahr
. Wir verlassen den Block durch Setzenstop.memory
zuwahr
. Wir machen das, weilhalt
ist ein Zeiger-zu-ein-Boolean Die Art und Weise, wie Swift mit dem "Point-to-Memory" umgeht, geschieht über die Memory-Eigenschaft.Beachten Sie, dass der Teilstring "2015/10/10" in unserem Text dem entspricht PossibleDate (15, 10, 10), Deshalb kehrt die Funktion zurück
wahr
im ersten Fall. Es entspricht jedoch keine Zeichenfolge im Text PossibleDate (13, 1, 1), das heißt "2013-01-01" und der zweite Aufruf der Funktion kehrt zurückfalsch
.Fazit
Wir haben uns gemächlich und doch einigermaßen detailliert angeschaut, wie reguläre Ausdrücke funktionieren, aber es gibt noch viel mehr zu lernen, wenn Sie interessiert sind, wie z Schau voraus und Schau hinter dich Assertionen, wendet reguläre Ausdrücke auf Unicode-Zeichenfolgen an und betrachtet die verschiedenen Optionen, die wir in der Foundation-API behandelt haben.
Selbst wenn Sie sich dafür entscheiden, nicht tiefer zu tauchen, haben Sie hoffentlich genug gefunden, um Situationen zu erkennen, in denen sich Regexen als nützlich erweisen könnten, sowie einige Hinweise, wie Sie Regexen entwerfen, um Ihre Mustersuchprobleme zu lösen.