Le espressioni regolari non possono essere utilizzate per gestire stringhe molto complesse che presentano una grammatica, come il codice sorgente di un linguaggio di programmazione, le annotazioni che descrivono i tipi di dati composti per i metodi, le espressioni matematiche, i calcoli, le formule e altro ancora. Il motivo è che si tratta di forme di stringa così complesse, contenenti molte regole, che dobbiamo semplicemente elaborarle in pezzi più piccoli.
Quando un computer elabora il codice sorgente PHP, ad esempio, lo scompone prima in tante piccole parti che hanno un proprio significato. Queste parti sono chiamate "tokens" e rappresentano i più piccoli blocchi autoconsistenti del linguaggio.
Il principio dell'elaborazione delle stringhe e del linguaggio è suddiviso in diverse fasi:
Un altro grande vantaggio di questo approccio è che conosciamo la posizione del token nella stringa (sia la riga che il carattere specifico di inizio e fine del token) man mano che scorriamo il token, in modo da poter affrontare con precisione la posizione del problema se viene lanciata un'eccezione.
Immaginate, ad esempio, di implementare un algoritmo per risolvere un esempio matematico. La matematica ha molte regole, come le priorità degli operatori, le parentesi, le chiamate di funzione e così via.
Se possiamo dividere la stringa di input in token elementari, possiamo lavorare con essa a un livello completamente diverso. Ad esempio, possiamo trovare facilmente le singole parentesi, sottrarre i token dalla parentesi iniziale a quella finale, passare una sottoespressione a una funzione ricorsiva per l'elaborazione e così via.
La tokenizzazione ci permette di risolvere in modo molto elegante anche problemi di parsing complessi.
Non abbiamo bisogno di molte conoscenze per scrivere il nostro tokenizer. In sostanza, è sufficiente conoscere il principio delle espressioni regolari e scrivere un piccolo oggetto di parsing.
Per gli scopi di questo articolo, ho preparato una versione di base di un tokenizzatore basato sul tokenizzatore Latte (Nette). L'autore dell'implementazione originale è David Grudl, che vorrei ringraziare per una funzione così semplice che risolve tutti i problemi.
final class Token{public string $value;public int $offset;public string $type;}final class Tokenizer{public const TokenTypes = ['array' => 'array','<' => '\<','>' => '\>','{' => '\{','}' => '\}','o' => '\|','elenco' => '\[\]','tipo' => '[a-zA-Z]+','spazio' => '\s+','virgola' => ',','altro' => '.+?',];/*** @Ritorno array<int, Token>*/public static function tokenize(string $haystack): array{$re = '~(' . implode(')|(', self::TokenTypes) . ')~A';$types = array_keys(self::TokenTypes);preg_match_all($re, $haystack, $tokenMatch, PREG_SET_ORDER);$len = 0;$count = count($types);$tokens = [];foreach ($tokenMatch as $match) {$type = null;for ($i = 1; $i <= $count; $i++) {if (isset($match[$i]) === false) {break;}if ($match[$i] !== '') {$type = $types[$i - 1];break;}}$token = new Token;$token->value = $match[0];$token->offset = $len;$token->type = (string) $type;$tokens[] = $token;$len += strlen($match[0]);}if ($len !== strlen($haystack)) {$text = substr($haystack, 0, $len);$line = substr_count($text, "\n") + 1;$col = $len - strrpos("\n" . $text, "\n") + 1;$token = str_replace("\n", '\n', substr($haystack, $len, 10));throw new \LogicException(sprintf('Inaspettato "%s" alla riga %s, colonna %s.', $token, $line, $col));}return $tokens;}}
Questo tokenizer può analizzare, ad esempio, una stringa così complessa (il formato è volutamente intervallato da spazi per mostrare che il tokenizer può gestire un'ampia gamma di casi):
array<int, array<bool, array<string, float> >
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | it