2016-01-14 16 views
5

Cerco di sostituire le virgole con il punto e virgola racchiuso tra parentesi graffe.Sostituire le virgole racchiuse tra parentesi graffe

stringa Esempio:

text = "a,b,{'c','d','e','f'},g,h" 

Sono consapevole che si tratta di lookbehinds e lookaheads, ma in qualche modo non funzionerà, come io voglio che:

substr = re.sub(r"(?<=\{)(.+?)(,)(?=.+\})",r"\1;", text) 

ritorna:

a,b,{'c';'d','e','f'},g,h 

Tuttavia, il mio obiettivo è il seguente:

a,b,{'c';'d';'e';'f'},g,h 

Qualche idea su come posso raggiungere questo obiettivo? Qualsiasi aiuto molto apprezzato :)

+0

C'è sempre un solo set di parentesi graffe nella stringa? – gtlambert

+0

Purtroppo no. Questo è un estratto di una risposta API codificata UTF-8 che voglio analizzare in un file CSV. –

risposta

2

È possibile abbinare l'intero blocco {...} (con {[^{}]+}) e sostituire le virgole al suo interno solo con un lambda:

import re 
text = "a,b,{'c','d','e','f'},g,h" 
print(re.sub(r"{[^{}]+}", lambda x: x.group(0).replace(",", ";"), text)) 

Vedi IDEONE demo

uscita: a,b,{'c';'d';'e';'f'},g,h

Dichiarando lambda x possiamo ottenere l'accesso a ogni oggetto della partita e ottenere l'intero valore della corrispondenza usando x.group(0). Quindi, tutto ciò di cui abbiamo bisogno è sostituire una virgola con un punto e virgola.

Questa espressione regolare non supporta i pattern ricorsivi. Per utilizzare uno schema ricorsivo, è necessario PyPi regex module. Qualcosa come m = regex.sub(r"\{(?:[^{}]|(?R))*}", lambda x: x.group(0).replace(",", ";"), text) dovrebbe funzionare.

+1

Wow che funziona come un fascino. Mi sono così appeso a cercare di catturare le virgole multiple che ho completamente dimenticato che puoi semplicemente catturare tutto tra le parentesi graffe e poi usare semplicemente la vecchia funzione di sostituzione per sostituire quei caratteri che devi sostituire. –

+0

Se hai nidificato '{}' s, potresti dare un'occhiata alla soluzione di Jaco. –

+2

Non funziona con parentesi annidate, ad es .: 'a, {b, {'c', 'd', 'e', ​​'f'}, g, h}" ' – Jaco

2

Di seguito ho pubblicato una soluzione che non si basa su un'espressione regolare. Utilizza una pila (list) per determinare se un personaggio si trova all'interno di una parentesi graffa {. Le espressioni regolari sono più eleganti, tuttavia, possono essere più difficili da modificare quando cambiano i requisiti. Si prega di notare che l'esempio di seguito funziona anche per parentesi annidate.

text = "a,b,{'c','d','e','f'},g,h" 
output='' 
stack = [] 
for char in text: 
    if char == '{': 
     stack.append(char) 
    elif char == '}': 
     stack.pop()  
    #Check if we are inside a curly bracket 
    if len(stack)>0 and char==',': 
     output += ';' 
    else: 
     output += char 
print output 

Questo dà:

'a,b,{'c';'d';'e';'f'},g,h 

Si può anche riscrivere questo come una funzione di map se si utilizza una variabile globale per stack:

stack = [] 


def replace_comma_in_curly_brackets(char): 
    if char == '{': 
     stack.append(char) 
    elif char == '}': 
     stack.pop()  
    #Check if we are inside a curly bracket 
    if len(stack)>0 and char==',': 
     return ';' 

    return char 

text = "a,b,{'c','d','e','f'},g,h" 
print ''.join(map(str, map(replace_comma_in_curly_brackets,text))) 

quanto riguarda le prestazioni, quando si esegue quanto sopra due metodi e la soluzione di espressioni regolari proposta da @stribizhev sulla stringa di test alla fine di questo post, ottengo i seguenti tempi:

  1. espressioni regolari (@stribizshev): 0.38 secondi
  2. Mappa di funzione: 26.3 secondi
  3. ciclo for: 251 secondi

Questa è la stringa di testo che è di 55,300,00 caratteri:

text = "a,able,about,across,after,all,almost,{also,am,among,an,and,any,are,as,at,be,because},been,but,by,can,cannot,could,dear,did,do,does,either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,on,only,or,other,our,own,rather,said,say,says,she,should,since,so,some,than,that,the,their,them,then,there,these,they,this,tis,to,too,twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,will,with,would,yet,you,your" * 100000 
+0

Una bella idea. Sapete quale metodo potrebbe essere più veloce se si guardano grossi pezzi di testo - regex o questa (ri) creazione iterativa della lista? Gli iteratori Afaik tendono a diventare relativamente lenti se gestiscono input di grandi dimensioni. –

+0

Farò un test rapido per confrontare il 2. – Jaco

+1

Ho eseguito un test su una stringa di 55.300.000 caratteri. Il ciclo for è molto lento e impiega 251 secondi, la funzione 'map' impiega 26 secondi e l'espressione regolare proposta da @stribizshev richiede 0,38 secondi. I test non includono la stampa dell'output. – Jaco

1

Se non avete annidato tra parentesi graffe, potrebbe essere sufficiente a solo look ahead a ogni ,
se c'è una chiusura } avanti senza alcuna apertura { in mezzo. Ricerca del

,(?=[^{]*}) 

e sostituirlo con ;

  • , partita una virgola letteralmente
  • (?= ... ) il lookahead per controllare
  • se c'è davanti [^{]*any amount di caratteri, that are not{
  • seguita da una chiusura parentesi graffa }

See demo at regex101

+0

Grazie per questa dolce soluzione. Funziona come un fascino. C'è qualcosa che ho potuto fraintendere: Ho sempre pensato che '{' e '}' devono essere salvati con '\', ma sembra non essere il caso quando fa parte di un gruppo che cattura. Qualcuno può chiarire? –

+1

@VincentHahn Dipende dal contesto e dal sapore regex. Nella maggior parte dei gusti è sufficiente sfuggire se si desidera abbinare una stringa di corrispondenza ad es. Letterale 'a {0,1}'. Nel contesto della mia risposta '{' '}' non sono visti come parte di un [quantificatore] (http://www.regular-expressions.info/repeat.html#limit) dal parser. –

+1

@VincentHahn In altre parole: per quanto sintatticamente non è un quantificatore valido, corrisponderà letteralmente. –

Problemi correlati