2009-03-10 17 views
32

Non sono sicuro che sia possibile, quindi mi rivolgo a voi.Regex per raccogliere virgole al di fuori delle virgolette

Mi piacerebbe trovare un'espressione regolare che selezionerà tutte le virgole che non rientrano nei set di virgolette.

Ad esempio:

'foo' => 'bar', 
'foofoo' => 'bar,bar' 

Questo sarebbe scegliere la sola virgola sulla linea 1, dopo 'bar',

Non mi importa davvero di singolo vs doppi apici.

Qualcuno ha qualche idea? Credo che questo dovrebbe essere possibile con i readaheads, ma il mio regex fu troppo debole.

+3

Risposta finale ufficiale, data da MarkusQ: (,) (? = (?: [^ "'] | [" |'] [^ "'] *" * $) – SocialCensus

+0

Come nota, questo non funziona interruzioni di riga di mid-quote. – SocialCensus

+0

@SocialCensus vedere la mia nota allegata alla risposta. – MarkusQ

risposta

74

Questo corrisponderà a qualsiasi stringa fino a includere il primo non quotato ",". E 'quello che stai volendo?

/^([^"]|"[^"]*")*?(,)/ 

Se si desidera che tutti loro (e come contro-esempio per il ragazzo che ha detto che non era possibile) si potrebbe scrivere:

/(,)(?=(?:[^"]|"[^"]*")*$)/ 

che corrisponderà tutti loro. Così

'test, a "comma,", bob, ",sam,",here'.gsub(/(,)(?=(?:[^"]|"[^"]*")*$)/,';') 

sostituisce tutte le virgole non citazioni interne con un punto e virgola, e produce:

'test; a "comma,"; bob; ",sam,";here' 

Se avete bisogno di lavorare tutta la linea va in crisi proprio aggiungere la (multilinea) bandiera m.

+0

Sembra che funzioni correttamente - con virgolette doppie. (,) (? = (?: [^ "'] | [" |'] [^ "'] *") * $) Credo che funzioni con virgolette singole o virgolette doppie. Grazie! – SocialCensus

+1

Volevo sottolineare che questo non funziona tra le interruzioni di riga. – SocialCensus

+0

@SocialCensus Quindi utilizzare il flag m. Inoltre, il tuo esempio nel commento sopra ha diversi bug. Ad esempio, prende le virgolette doppie, le virgolette singole e le barre verticali come virgolette di apertura ma prende solo le virgolette doppie come virgolette di chiusura. – MarkusQ

1

Prova questa espressione regolare:

(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*=>\s*(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*, 

Ciò permettono anche le stringhe come “'foo\'bar' => 'bar\\',”.

+0

Questo non sembra funzionare per me ... – SocialCensus

1

La risposta di MarkusQ ha funzionato benissimo per me per circa un anno, fino a quando non è stato così. Ho appena ricevuto un errore di overflow di stack su una riga con circa 120 virgole e 3682 caratteri in totale. In Java, in questo modo:

 String[] cells = line.split("[\t,](?=(?:[^\"]|\"[^\"]*\")*$)", -1); 

Ecco la mia sostituzione estremamente poco elegante che non Stack Overflow:

private String[] extractCellsFromLine(String line) { 
    List<String> cellList = new ArrayList<String>(); 
    while (true) { 
     String[] firstCellAndRest; 
     if (line.startsWith("\"")) { 
      firstCellAndRest = line.split("([\t,])(?=(?:[^\"]|\"[^\"]*\")*$)", 2); 
     } 
     else { 
      firstCellAndRest = line.split("[\t,]", 2);     
     } 
     cellList.add(firstCellAndRest[0]); 
     if (firstCellAndRest.length == 1) { 
      break; 
     } 
     line = firstCellAndRest[1]; 
    } 
    return cellList.toArray(new String[cellList.size()]); 
} 
1

@SocialCensus, L'esempio che ha dato nel commento al MarkusQ, dove si gettano nel ' accanto a ", non funziona con l'esempio MarkusQ ha dato sopra che se cambiamo sam a sam's: (test, una" virgola ", bob,", sam's ", qui) non ha alcuna corrispondenza con (,) (? = (?: [^ "'] | [" |'] [^ "'] ") $). In effetti, il problema è "Non mi interessa davvero le virgolette singole o doppie", è ambiguo. Devi essere chiaro cosa intendi citando o con "o con", ad esempio, è consentito il nesting o no? Se sì, a quanti livelli? Se solo 1 livello nidificato, cosa succede a una virgola al di fuori della citazione interna annidata ma all'interno della citazione di annidamento esterno? Dovresti anche considerare che le virgolette singole avvengono da sole come apostrofi (cioè, come il contro-esempio che ho dato in precedenza con Sam's).Infine, la regex che hai fatto non considera le virgolette singole alla pari con virgolette doppie poiché presuppone che l'ultimo tipo di virgolette sia necessariamente una virgoletta doppia - e anche la sostituzione dell'ultima virgoletta con ['| "] ha un problema se il testo non viene fornito con virgolette corrette (o se vengono utilizzati gli apostrofi), suppongo che probabilmente potremmo assumere che tutte le virgolette siano delineate correttamente.

L'espressione regolare di MarkusQ risponde alla domanda: trova tutte le virgole che hanno un numero pari di doppi apici dopo di esso (cioè, sono al di fuori delle virgolette doppie) e ignorare tutte le virgole che hanno un numero dispari di virgolette doppie (cioè, sono racchiuse tra virgolette doppie) .Questa è generalmente la stessa soluzione di quello che si vorrebbe, ma facciamo guarda alcune anomalie: in primo luogo, se qualcuno lascia una virgoletta alla fine, allora questa espressione regex trova tutte le virgole sbagliate piuttosto che trovare quelle desiderate o non riuscendo a corrispondere a nessuno. Ovviamente, se manca una virgoletta doppia, tutte le scommesse sono off poiché potrebbe non essere chiaro se quello mancante appartiene alla fine o invece appartiene all'inizio; tuttavia, esiste un caso legittimo e in cui la regex potrebbe fallire (questa è la seconda "anomalia"). Se modifichi l'espressione regolare per passare attraverso le righe di testo, allora dovresti sapere che la citazione di più paragrafi consecutivi richiede che tu inserisca una virgoletta singola all'inizio di ogni paragrafo e che lasci la citazione alla fine di ogni paragrafo eccetto che per fine dell'ultimo paragrafo. Ciò significa che nello spazio di quei paragrafi, la regex fallirà in alcuni punti e avrà successo in altri.

Esempi e brevi discussioni sulla quotazione di paragrafi e di quotazioni annidate sono disponibili qui http://en.wikipedia.org/wiki/Quotation_mark.

+4

Questo non fornisce una risposta alla domanda. Per critica o richiesta chiarimento da parte di un autore, lascia un commento sotto il loro post – mattt

+0

Devo dare un'altra occhiata a questo problema, ma ho notato che la mia "risposta" era piuttosto lunga. Sarebbe giusto come commento? Inoltre, la mia vecchia risposta sembra rispondere che non esiste una risposta corretta e necessariamente corretta a causa delle ambiguità nella domanda (ho fornito esempi). Probabilmente ho pensato che questa risposta/critica andasse oltre l'osservazione dell'autore e aggiungesse il contesto a chi cercava una risposta. modifica la domanda o dovrei fare affidamento su qualcun altro? [ho bisogno di approfondire il problema sollevato quando trovo il momento] –

+0

@mattt ha fatto non significa che sembra ignorare la tua richiesta. Sono in ritardo in questo momento. –

6

Le espressioni regolari qui sotto corrisponde a tutti i del comma che sono presenti al di fuori delle virgolette doppie,

,(?=(?:[^"]*"[^"]*")*[^"]*$) 

DEMO

O (PCRE solo)

"[^"]*"(*SKIP)(*F)|, 

"[^"]*" partite tutte le doppio blocco citato. Cioè, in questo input buz,"bar,foo", questa espressione regolare corrisponderebbe solo a "bar,foo". Ora il seguente (*SKIP)(*F) fallisce la corrispondenza. Quindi passa al modello che era accanto al simbolo | e cerca di far corrispondere i caratteri della stringa rimanente. Cioè, nella nostra produzione , accanto al modello | corrisponderà solo alla virgola che era appena dopo a buz. Nota che questo non corrisponderà alla virgola che era presente tra virgolette, perché abbiamo già fatto saltare la parte doppia citazione.

DEMO


Il sotto espressione regolare sarebbe partita tutta del comma che sono presenti all'interno delle virgolette,

,(?!(?:[^"]*"[^"]*")*[^"]*$) 

DEMO

2

Mentre è possibile incidere con una regex (e mi piace abusare delle regex tanto quanto il prossimo), ti metteresti nei guai prima o poi proverai a gestire sottostringhe senza un altro parser avanzato. I modi possibili per mettersi nei guai includono citazioni misti e citazioni sfuggite.

Questa funzione dividerà una stringa su virgole, ma non quelle virgole che si trovano all'interno di una stringa con virgolette singole o doppie.Può essere facilmente esteso con caratteri aggiuntivi da utilizzare come citazioni (anche se coppie di caratteri come «» avrebbe bisogno di un paio di righe di codice) e sarà anche dirvi se avete dimenticato di chiudere una citazione nei dati:

function splitNotStrings(str){ 
    var parse=[], inString=false, escape=0, end=0 

    for(var i=0, c; c=str[i]; i++){ // looping over the characters in str 
    if(c==='\\'){ escape^=1; continue} // 1 when odd number of consecutive \ 
    if(c===','){ 
     if(!inString){ 
     parse.push(str.slice(end, i)) 
     end=i+1 
     } 
    } 
    else if(splitNotStrings.quotes.indexOf(c)>-1 && !escape){ 
     if(c===inString) inString=false 
     else if(!inString) inString=c 
    } 
    escape=0 
    } 
    // now we finished parsing, strings should be closed 
    if(inString) throw SyntaxError('expected matching '+inString) 
    if(end<i) parse.push(str.slice(end, i)) 
    return parse 
} 

splitNotStrings.quotes="'\"" // add other (symmetrical) quotes here