8

motori di espressioni regolari hanno un concetto di fiammiferi "width zero", alcuni dei quali sono utili per la ricerca di margini di parole:espressioni regolari per abbinare confine tra diversi script Unicode

  • \b - presenti nella maggior parte dei motori a partita qualsiasi confine tra parole e non parole caratteri
  • \< and \> - present in Vim per abbinare solo il contorno all'inizio di una parola, e alla fine di una parola, rispettivamente.

Un concetto più recente in alcuni motori di espressioni regolari è classi Unicode. Una tale classe è script, che può distinguere latino, greco, cirillico, ecc Questi esempi sono tutti equivalenti e abbinare qualsiasi carattere del sistema di scrittura greca:

  • \p{greek}
  • \p{script=greek}
  • \p{script:greek}
  • [:script=greek:]
  • [:script:greek:]

Ma finora nella mia lettura attraverso le fonti sulle espressioni regolari e Unicode non sono stato in grado di determinare se esiste un modo standard o non standard per ottenere una corrispondenza a larghezza zero dove uno script termina e un altro inizia.

Nella stringa παν語 ci sarebbe stato un incontro tra i ν e personaggi, proprio come \b e \< sarebbe partita poco prima del carattere π.

Ora, per questo esempio, ho potuto creare qualcosa insieme sulla ricerca di \p{Greek} seguito da \p{Han}, e ho potuto anche modificare qualcosa in base a tutte le possibili combinazioni di due nomi di script Unicode.

Ma questa non sarebbe una soluzione deterministica in quanto nuovi script vengono ancora aggiunti a Unicode con ogni uscita. C'è un modo a prova di futuro per esprimere questo? O c'è una proposta per aggiungerlo?

+2

Chiudi ma non esattamente lo stesso: http://stackoverflow.com/questions/14942652/how-to-emulate-word-boundary-when-using-unicode-character-properties/14942906#14942906 La mia risposta è per una singola classe di caratteri (e questo vale per qualsiasi classe di caratteri). La tua domanda riguarda il confine tra qualsiasi lingua. – nhahtdh

+0

@nhahtdh: Grazie. Sono sorpreso di non aver trovato la tua domanda nella mia ricerca. – hippietrail

+1

Penso che tutti dovrebbero leggere la sezione 2 di questo: http://www.unicode.org/reports/tr24/ – nhahtdh

risposta

3

EDIT: Ho appena notato che non ha in realtà specificato che lingua pattern-matching si stava utilizzando. Bene, spero che una soluzione Perl funzioni per te, dal momento che le meccaniche necessarie sono molto difficili in qualsiasi altra lingua. Inoltre, se stai realizzando pattern matching con Unicode, Perl è davvero la scelta migliore disponibile per quel particolare tipo di lavoro.


Quando la variabile $rx sotto è impostato sul modello appropriato, questo piccolo frammento di codice Perl:

my $data = "foo1 and Πππ 語語語 done"; 

while ($data =~ /($rx)/g) { 
    print "Got string: '$1'\n"; 
} 

genera questo output:

Got string: 'foo1 and ' 
Got string: 'Πππ ' 
Got string: '語語語 ' 
Got string: 'done' 

Cioè, si tira fuori una stringa latina, una stringa greca, una stringa Han e un'altra stringa latina.Questo è davvero dannatamente chiuso a ciò che penso tu abbia effettivamente bisogno.

Il motivo per cui non l'ho postato ieri è che stavo ottenendo strani core core. Ora so perché.

La mia soluzione utilizza variabili lessicali all'interno di un costrutto (??{...}). Risulta che è instabile prima di v5.17.1, e nella migliore delle ipotesi ha funzionato solo per sbaglio. Fallisce su v5.17.0, ma ha successo su v5.18.0 RC0 e RC2. Quindi ho aggiunto un use v5.17.1 per assicurarmi che tu stia eseguendo qualcosa di abbastanza recente da fidarti di questo approccio.

In primo luogo, ho deciso che in realtà non si desidera eseguire un'esecuzione con lo stesso tipo di script; volevi eseguire lo stesso tipo di script più Comune ed Ereditato. Altrimenti verrai incasinato dalla punteggiatura e dagli spazi bianchi e dalle cifre per Common, e combinando i caratteri per Ereditato. Davvero non penso che tu voglia che questi interrompano la tua serie di "tutti gli stessi script", ma se lo fai, è facile smettere di considerarli.

Quindi quello che facciamo è guardare avanti per il primo carattere che ha un tipo di script diverso da Comune o Ereditato. Oltre a ciò, estraiamo da esso ciò che effettivamente è quel tipo di script e usiamo queste informazioni per costruire un nuovo pattern che è un numero qualsiasi di caratteri il cui tipo di script è Common, Ereditato o qualsiasi tipo di script che abbiamo appena trovato e salvato. Quindi valutiamo quel nuovo modello e continuiamo.

Ehi, I ha detto era peloso, no?

Nel programma che sto per mostrare, ho lasciato in alcune dichiarazioni di debug commentate che mostrano solo ciò che sta facendo. Se li si rimuove il commento, si ottiene questa uscita per l'ultima esecuzione, che dovrebbe aiutare a capire l'approccio:

DEBUG: Got peekahead character f, U+0066 
DEBUG: Scriptname is Latin 
DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Latin}]*} 
Got string: 'foo1 and ' 
DEBUG: Got peekahead character Π, U+03a0 
DEBUG: Scriptname is Greek 
DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Greek}]*} 
Got string: 'Πππ ' 
DEBUG: Got peekahead character 語, U+8a9e 
DEBUG: Scriptname is Han 
DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Han}]*} 
Got string: '語語語 ' 
DEBUG: Got peekahead character d, U+0064 
DEBUG: Scriptname is Latin 
DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Latin}]*} 
Got string: 'done' 

E qui, finalmente è il grosso problema peloso:

use v5.17.1; 
use strict; 
use warnings; 
use warnings FATAL => "utf8"; 
use open qw(:std :utf8); 
use utf8; 

use Unicode::UCD qw(charscript); 

# regex to match a string that's all of the 
# same Script=XXX type 
# 
my $rx = qr{ 
    (?= 
     [\p{Script=Common}\p{Script=Inherited}] * 
     (?<CAPTURE> 
      [^\p{Script=Common}\p{Script=Inherited}] 
     ) 
    ) 
    (??{ 
     my $capture = $+{CAPTURE}; 
    #####printf "DEBUG: Got peekahead character %s, U+%04x\n", $capture, ord $capture; 
     my $scriptname = charscript(ord $capture); 
    #####print "DEBUG: Scriptname is $scriptname\n"; 
     my $run = q([\p{Script=Common}\p{Script=Inherited}\p{Script=) 
       . $scriptname 
       . q(}]*); 
    #####print "DEBUG: string to re-interpolate as regex is q{$run}\n"; 
     $run; 
    }) 
}x; 


my $data = "foo1 and Πππ 語語語 done"; 

$| = 1; 

while ($data =~ /($rx)/g) { 
    print "Got string: '$1'\n"; 
} 

Sì, ci oughta essere un modo migliore. Non penso ci sia ancora.

Quindi per ora, divertiti.

+0

Oh specificatamente non ho specificato un dialetto regex, piuttosto ho chiesto di "standard", "non standard" e "proposto". In realtà sto giocando con XRegExp e sto leggendo attraverso UTS # 18 e regular-expressions.info ma sono più abituato alle implementazioni di Perl e Vim. Credo di voler sapere cosa dovrei essere in grado di fare, anche se dialetti specifici non l'hanno ancora implementato. Per soluzioni alternative, suppongo che JavaScript o persino un'estensione di XRegExp siano i migliori. (Sto scrivendo questo prima di leggere il corpo della tua risposta tra l'altro ...) – hippietrail

+1

@hippietrail UTS # 18 non coprirebbe questo almeno fino al Livello 3, e nessuno lo implementa ancora. Quindi ci accontentiamo di ciò che possiamo nel frattempo. Non l'ho ancora guardato ultimamente, quindi non so se questo sarebbe possibile al Livello 3. – tchrist

+0

Oltre a te, naturalmente, chi sta attivamente promuovendo lo sviluppo regolare di Unicode in questi giorni? So che Perl ha di gran lunga il miglior supporto per Unicode ed è uno dei motivi principali per cui è stata la mia lingua principale per anni, ma ora mi sono spostato per altri motivi in ​​una lingua con il supporto Unicode peggiore. Posso sicuramente creare uno splitter non regex, ma mi sembrava una caratteristica ovvia da includere in un motore regex. Forse dovrei presentare alcune proposte? – hippietrail