2009-09-18 16 views
9

ho bisogno di abbinare i due punti (':') in una stringa, ma non quando è racchiuso tra virgolette - una o 'carattere "Regex per abbinare un personaggio, ma non quando è racchiuso tra virgolette

. così il seguente dovrebbe avere 2 partite

something:'firstValue':'secondValue'  
something:"firstValue":'secondValue' 

ma questo dovrebbe avere solo 1 partita

something:'no:match' 
+1

@Jaco: 1) quale lingua? 2) Non è più semplice dividere prima la stringa su ['"] in modo da poter controllare tutti gli elementi non numerati nell'array – Huppie

+0

Sarebbe meglio con un parser .. – Gumbo

+0

@Gumbo ... Immagino che sia il mio consiglio: leggere byte-saggio e utilizzare una bandiera se si è tra virgolette – Scoregraphic

risposta

3

Se l'implementazione delle espressioni regolari supporta guardare intorno affermazioni, provate questo:

:(?:(?<=["']:)|(?=["'])) 

Ciò corrisponderà a qualsiasi colon che è sia preceduto o seguito da un preventivo doppia o singola. Quindi considera solo la costruzione come hai detto tu. something:firstValue non corrisponderebbe.

Sarebbe meglio se si costruisca un parser che legge l'input byte per byte e si ricorda quando la quotazione è aperta.

+0

Questo funziona abbastanza bene, ma non riesce nel caso degenerato qualcosa: 'nessuna corrispondenza:' –

+2

Sono d'accordo con Gumbo - è meglio costruire un parser piccolo –

1

Uppps ... colto il punto. Dimenticate il resto. E 'abbastanza difficile da fare questo perché non è regex bravo a contare il chara equilibrato cters (ma l'implementazione .NET ad esempio ha un'estensione che può farlo, ma è un po 'complicato).

È possibile utilizzare gruppi di caratteri negati per eseguire questa operazione.

[^'"]:[^'"] 

È possibile ulteriormente racchiudere le virgolette in gruppi non acquisiti.

(?:[^'"]):(?:[^'"]) 

Oppure è possibile utilizzare l'asserzione.

(?<!['"]):(?!['"]) 
0

mi è venuta in mente la seguente costruzione un po 'preoccupante:

(?<=^('[^']*')*("[^"]*")*[^'"]*):

Esso utilizza un'asserzione lookbehind per essere sicuri di abbinare un numero di citazioni a partire dall'inizio della riga al colon corrente . Permette di incorporare una singola virgoletta tra virgolette e viceversa. Come in:

'a":b':c::"':" (corrisponde alle posizioni 6, 8 e 9)

EDIT

Gumbo è giusto, utilizzando * all'interno di uno sguardo dietro affermazione non è permesso.

+0

Questa espressione si abbinerà solo se la stringa inizia s con una citazione singola a causa della dichiarazione (? <=^('[^ ... –

+0

@Daniel - (' [^ '] *') * corrisponde a zero o più istanze di qualcosa tra virgolette singole, quindi non devo iniziare con una citazione. Detto questo, il mio è rotto, vedi la mia modifica –

+1

In generale, le asserzioni look-behind non permettono infiniti quantificatori come '*'. – Gumbo

3

Le espressioni regolari sono senza stato. Tracciare se si è all'interno di virgolette o no è informazioni di stato. Pertanto, è impossibile gestirlo correttamente utilizzando solo una singola espressione regolare. (Si noti che alcune implementazioni "espressioni regolari" aggiungere estensioni che possono rendere questo possibile,. Sto parlando solo di "veri" espressioni regolari qui)

Farlo con due espressioni regolari è possibile, anche se, a condizione che sei disposto a modificare la stringa originale oa lavorare con una copia di essa.In Perl:

$string =~ s/['"][^'"]*['"]//g; 
my $match_count = $string =~ /:/g; 

Il primo troverà ogni sequenza costituita da una citazione, seguita da un numero qualsiasi di caratteri non preventivo e terminato da una seconda citazione, e rimuovere tutti tali sequenze dalla stringa. Questo eliminerà tutti i due punti che sono tra virgolette. (something:"firstValue":'secondValue' diventa something:: e something:'no:match' diventa something:)

La seconda fa un semplice conteggio dei rimanenti due punti, che saranno quelli che non erano all'interno apici per iniziare.

Solo il conteggio dei due punti non quotati non sembra una cosa particolarmente utile da fare nella maggior parte dei casi, tuttavia, quindi sospetto che il tuo vero obiettivo è dividere la stringa in campi con due punti come delimitatore di campo, nel qual caso questa soluzione basata su regex non è adatta, in quanto distruggerà tutti i dati nei campi citati. In tal caso, è necessario utilizzare un parser reale (la maggior parte dei parser CSV consente di specificare il delimitatore e sarebbe l'ideale per questo) o, nel peggiore dei casi, percorrere la stringa carattere per carattere e dividerlo manualmente.

Se ci dici la lingua che stai utilizzando, sono sicuro che qualcuno potrebbe suggerire una buona libreria di parser per quella lingua.

+0

Sto usando C# ma ho pensato che avrei potuto farlo con un Regex (che è indipendente dalla lingua) ... Penso che sia meglio solo analizzarlo senza Regex tho –

+1

Questo è il problema; una regex non è indipendente dalla lingua/libreria; le parti che non possono farlo. – reinierpost

Problemi correlati