Expresii rapide și regulate Swift

În primul tutorial al acestei serii, am explorat elementele de bază ale expresiilor regulate, inclusiv sintaxa pentru a scrie expresii regulate. În acest tutorial, aplicăm ceea ce am învățat până acum pentru a utiliza expresii regulate în Swift.

1. Expresii regulate în Swift

Deschideți Xcode, creați un nou loc de joacă, numiți-l RegExTut, și stabilit Platformă la OS X. Alegerea platformei, iOS sau OS X, nu face nicio diferență în ceea ce privește API pe care o vom folosi.

Înainte de a începe, există încă un lucru despre care trebuie să știți. În Swift, trebuie să utilizați două backslash-uri, \\, pentru fiecare backslash pe care îl utilizați într-o expresie regulată. Motivul are de-a face cu Swift având litere de coarde în stil C. Backslash-ul este procesat ca o evadare de caractere în plus față de rolul său în interpolarea șirului în Swift. Cu alte cuvinte, trebuie să scăpați de caracterul de evadare. Dacă sună ciudat, nu-ți face griji. Amintiți-vă, să folosiți două spate în loc de una.

În primul exemplu, cumva controversat, ne imaginăm că ne străduim printr-un șir care caută un tip foarte specific de adresă de e-mail. Adresa de e-mail îndeplinește următoarele criterii:

  • prima literă este prima literă a numelui persoanei
  • urmată de o perioadă
  • urmată de numele de familie al persoanei
  • urmat de simbolul @
  • urmată de un nume, reprezentând o universitate din Regatul Unit
  • urmat de .ac.uk, domeniul pentru instituțiile academice din Regatul Unit

Adăugați următorul cod la locul de joacă și treceți prin acest fragment de cod pas cu pas.

import Cacao // (1): lăsăm pat = "\\ b ([az]) \\. ([az] 2,) ([az] +) \\. "// (2): lasați testStr =" [email protected], [email protected] [email protected], [email protected], [email protected]. uk "// (3): lasa regex = try! NSRegularExpression (model: pat, opțiuni: []) // (4): permite match = regex.matchesInString (testStr, opțiuni: [], interval: NSRange (locație: 0, lungime: testStr.characters.count))

Pasul 1

Definim un model. Rețineți dublurile scoase din spate. În reprezentarea (normală) regex, cum ar fi cea utilizată pe site-ul RegExr, aceasta ar fi ([A-z]) \. ([A-z] 2) @ ([a-z] +) \. Ac \ .uk. De asemenea, rețineți utilizarea parantezelor. Ele sunt folosite pentru a defini grupurile de captare cu care putem extrage substringurile potrivite cu acea parte a expresiei regulate.

Ar trebui să fii capabil să afli că primul grup de capturare surprinde prima literă a numelui utilizatorului, al doilea ultimul nume, iar al treilea numele universității. Rețineți, de asemenea, folosirea inversă pentru a scăpa de caracterul perioadei pentru a reprezenta sensul său literal. Alternativ, am putea pune-o într-un set de caractere ([.]). În acest caz, nu ar trebui să scăpăm de el.

Pasul 2

Acesta este șirul în care căutăm modelul.

Pasul 3

Creăm a NSRegularExpression obiect, trecând în model fără opțiuni. În lista de opțiuni, puteți specifica NSRegularExpressionOption constante, cum ar fi:

  • CaseInsensitive: Această opțiune specifică faptul că potrivirea este insensibilă pentru litere mici.
  • IgnoreMetacharacters: Utilizați această opțiune dacă doriți să efectuați o potrivire literală, ceea ce înseamnă că metacaractele nu au un înțeles special și se potrivesc ca caractere obișnuite.
  • AnchorMatchLines: Utilizați această opțiune dacă doriți ^ și $ ancore pentru a se potrivi cu începutul și sfârșitul liniilor (separate de linii de pauze) într-un singur șir, mai degrabă decât începutul și sfârșitul întregului șir.

Deoarece inițializatorul aruncă, folosim încerca cuvinte cheie. Dacă transmitem o expresie regulată nevalidă, de exemplu, o eroare este aruncată.

Pasul 4

Căutăm potriviri din șirul de test invocând matchesInString (_: opțiuni: gama :), trecând într-un interval pentru a indica care parte a șirului suntem interesați. Această metodă acceptă și o listă de opțiuni. Pentru a păstra lucrurile simple, nu vom trece în nici un fel opțiunile din acest exemplu. Voi vorbi despre opțiuni în exemplul următor.

Meciurile sunt returnate ca o matrice de NSTextCheckingResult obiecte. Putem extrage meciurile, inclusiv grupurile de captură, după cum urmează:

pentru meciul în meciuri pentru n în 0 ... 

Fragmentul de mai sus este repetat prin fiecare NSTextCheckingResult obiect în matrice. numberOfRanges proprietatea pentru fiecare meci din exemplu are o valoare de 4, unul pentru întreaga subreversă care corespunde unei adrese de e-mail (de exemplu, [email protected]), iar celelalte trei corespund celor trei grupuri de captură din cadrul meciului ("a", "khan" și "surrey "respectiv).

rangeAtIndex (_ :) metoda returnează intervalul subrețelelor în șir, astfel încât să le putem extrage. Rețineți că, în loc să utilizați rangeAtIndex (0), puteți utiliza, de asemenea, gamă proprietate pentru întregul meci.

Apasă pe Afișați rezultatul butonul din panoul de rezultate din dreapta. Acest lucru ne arată "Surrey", valoarea lui testStr.substringWithRange (r) pentru ultima iterație a buclei. Faceți clic dreapta pe câmpul pentru rezultate și selectați Istoricul valorii pentru a arăta o istorie a valorilor.

Puteți modifica codul de mai sus pentru a face ceva semnificativ cu potrivirile și / sau grupurile de capturare.

Există o modalitate convenabilă de a efectua operații de căutare și înlocuire utilizând un șir de șabloane care are o sintaxă specială pentru reprezentarea grupurilor de captură. Continuând cu exemplul, să presupunem că am vrut să înlocuim fiecare adresă de e-mail potrivită cu un substring al formularului "lastname, initial, university", am putea face următoarele:

să înlocuiascăStr = regex.stringByReplacingMatchesInString (testStr, opțiuni: [], interval: NSRange (locație: 0, lungime: testStr.characters.count), cuTemplate: "($ 2, $ 1, $ 3)

Rețineți $ n sintaxă în șablon, care acționează ca un substituent pentru textul grupului de captare n. Ține minte că $ cu 0 reprezintă întregul meci.

2. Un exemplu mai avansat

matchesInString (_: opțiuni: gama :) metoda este una dintre metodele de confort pe care se bazează enumerateMatchesInString (_: opțiuni: interval: usingBlock :), care este metoda cea mai flexibilă și mai generală (și, prin urmare, complicată) în NSRegularExpression clasă. Această metodă numește un bloc după fiecare potrivire, permițându-vă să efectuați acțiunile pe care le doriți.

Prin trecerea în una sau mai multe reguli de potrivire, folosind NSMatchingOptions constante, puteți să vă asigurați că blocul este invocat în alte ocazii. Pentru operațiile de lungă durată, puteți specifica faptul că blocul este invocat periodic și termină operația la un moment dat. Cu ReportCompletion , specificați că blocul trebuie invocat la finalizare.

Blocul are un parametru de steaguri care raportează oricare dintre aceste stări, astfel încât să puteți decide ce acțiune să faceți. Similar cu alte metode de enumerare din fundație cadru, blocul poate fi de asemenea oprit la discreția dvs. De exemplu, dacă un meci de lungă durată nu reușește sau dacă ați găsit suficiente potriviri pentru a începe procesarea.

În acest scenariu, vom căuta prin intermediul unui text pentru șiruri de caractere care arată ca date și verificați dacă o anumită dată este prezentă. Pentru a păstra exemplul ușor de gestionat, ne vom imagina că șirurile de date au următoarea structură:

  • un an cu două sau patru cifre (de exemplu, 09 sau 2009)
  • numai din secolul prezent (între 2000 și 2099), deci 1982 ar fi respins și 16 ar fi interpretat în mod automat în 2016
  • urmat de un separator
  • urmat de un număr între 1 și 12 reprezentând luna
  • urmat de un separator
  • încheind cu un număr între 1 și 31 reprezentând ziua

Numerele și datele de o singură cifră ar putea fi eventual căptușite cu zero zero. Separatorii valabili sunt o linie, o perioadă și o slash înainte. În afară de cerințele de mai sus, nu vom verifica dacă o dată este efectiv validă. De exemplu, suntem bine cu date cum ar fi 2000-04-31 (aprilie are doar 30 de zile) și 2009-02-29 (2009 nu este un an jumătate, ceea ce înseamnă că februarie are doar 28 de zile) care nu sunt date reale.

Adăugați următorul cod la locul de joacă și treceți prin acest fragment de cod pas cu pas.

// (1): typealias PossibleDate = (anul: Int, luna: Int, ziua: Int) // (2): func dateSearch (text: String, _date: PossibleDate) (a) [] / [(1)] - [] / [(] 0-1] | [1-2] [0-9] | 0? [1-9]) \\ b "lasă dateRegex = încercați! NSRegularExpression (model: datePattern, opțiuni: []) // (4): var a fost dat: Bool = false // (5): dateRegex.enumerateMatchesInString (text, ()) // (6): (potrivire, _, oprire) în var dateArr = [Int] () pentru n în 1 ... 3  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 false

Pasul 1

Data a cărei existență o verificăm va fi într-un format standardizat. Folosim o tuplă numită. Transmitem doar un număr întreg de două cifre la un an, adică 16 înseamnă 2016.

Pasul 2

Sarcina noastră este să enumerăm prin meciuri care arată ca date, să extragem componentele anului, lunii și zilei și să verificăm dacă se potrivesc cu data la care am trecut. Vom crea o funcție pentru a face toate acestea pentru noi. Funcția revine Adevărat sau fals în funcție de faptul că data a fost găsită sau nu.

Pasul 3

Modelul de date are câteva caracteristici interesante:

  • Notați fragmentul (: 20)?. Dacă am înlocuit acest fragment cu (20)?, speram ca veti recunoaste ca acest lucru a insemnat ca suntem bine cu "20" (reprezentand mileniul) care este prezent in anul sau nu. Parantezele sunt necesare pentru grupare, dar nu ne pasă să formăm un grup de captare cu această pereche de paranteze și asta este ceea ce ?: bit este pentru.
  • Posibilele separatoare din setul de caractere [-. /] nu trebuie să scăpați pentru a reprezenta sinele lor literal. Te poți gândi așa. Dash, -, este la început, astfel încât nu poate reprezenta o gamă. Și nu are sens pentru perioadă, ., pentru a reprezenta orice caracter din interiorul unui set de caractere, deoarece îl face la fel de bine în afara.
  • Folosim în mod greu bara verticală pentru alternare pentru a reprezenta diferitele posibilități pentru lună și data.

Pasul 4

Variabila booleană nu a fost gasit vor fi returnate de funcție, indicând dacă data căutării a fost sau nu găsită.

Pasul 5

 enumerateMatchesInString (_: opțiuni: interval: usingBlock :) este numit. Nu folosim niciuna dintre opțiuni și trecem în întreaga gamă de text căutat.

Pasul 6

Obiectul bloc, invocat după fiecare meci, are trei parametri:

  • meciul (a NSTextCheckingResult)
  • steaguri reprezentând starea actuală a procesului de potrivire (pe care îl ignorăm aici)
  • un boolean Stop variabilă, pe care o putem seta în bloc pentru a ieși devreme

Utilizăm booleanul pentru a ieși din bloc dacă găsim data pe care o căutăm, deoarece nu mai trebuie să ne uităm mai departe. Codul care extrage componentele datei este destul de similar cu cel din exemplul anterior.

Pasul 7

Verificăm dacă componentele extrase din substringul corespunzător sunt egale cu componentele datei dorite. Rețineți că vom forța o distribuție Int, care suntem siguri că nu vom eșua, deoarece am creat grupurile corespunzătoare de captare pentru a se potrivi numai cifre.

Pasul 8

Dacă se găsește un meci, setăm nu a fost gasit la Adevărat. Iesim blocul prin setare stop.memoryla Adevărat. Facem asta pentru că Stop este a pointer-la-un-boolean iar modul în care Swift se ocupă de memoria "evidențiată" este prin proprietatea memoriei.

Observați că substringul "2015/10/10" din textul nostru corespunde Posibile date (15, 10, 10), de aceea funcția revine Adevărat în primul caz. Cu toate acestea, niciun șir din text nu corespunde Posibilă dată (13, 1, 1), adică, "2013-01-01" și se întoarce al doilea apel la funcție fals.

Concluzie

Am analizat cu ușurință modul în care funcționează expresiile regulate, dar există multe de învățat dacă sunteți interesat, cum ar fi priveste inainte și uita-te in spate afirmații, aplicarea expresiilor regulate în șiruri de caractere Unicode, pe lângă analizarea diferitelor opțiuni pe care le-am redimensionat în API-ul Fundației.

Chiar dacă vă decideți să nu vă dați mai adânc, sperați că ați reușit să identificați situațiile în care regexurile ar putea fi la îndemână, precum și unele indicații despre cum să creați regexuri pentru a vă rezolva problemele de căutare a modelului.

Cod