2012-12-06 15 views
27

Sto facendo un po 'di normalizzazione del testo usando python ed espressioni regolari. Mi piacerebbe sostituire tutti e sei con te. Ecco quello che ho fatto finora:Python re.sub(): come sostituire tutti 'u' o 'U' con 'tu'

import re 
text = 'how are u? umberella u! u. U. [email protected] U# u ' 
print re.sub (' [u|U][s,.,?,!,W,#,@ (^a-zA-Z)]', ' you ', text) 

L'output che ottengo è:

how are you you berella you you you you you you 

Come si può vedere il problema è che 'umberella' viene modificato in 'berella'. Inoltre voglio mantenere il personaggio che appare dopo una 'u'. Ad esempio, voglio "tu!" essere cambiato in "voi!". Qualcuno può dirmi cosa sto facendo male e qual è il modo migliore per scrivere l'espressione regolare?

risposta

48

In primo luogo, perché la soluzione non funziona. Mescoli molti concetti. Per lo più character class con altri. Nella prima classe di caratteri si utilizza | che deriva da alternation. Nelle classi di caratteri non hai bisogno della pipa. Basta elencare tutti i personaggi (e intervalli di caratteri) che si desidera:

[Uu] 

O semplicemente scrivere u se si utilizza il modificatore maiuscole e minuscole. Se scrivi una pipe lì, la classe di caratteri corrisponderà effettivamente alle pipe nella stringa dell'oggetto.

Ora nella seconda classe di caratteri si usa la virgola per separare i propri personaggi per qualche strano motivo. Questo non fa altro che includere le virgole nei caratteri accoppiabili. s e W probabilmente sono le classi di caratteri incorporate. Allora fuggi da loro! In caso contrario, corrisponderanno semplicemente al letterale s e al letterale W. Ma poi lo \W include già tutto ciò che hai elencato lì, quindi un solo \W sarebbe stato sufficiente (senza parentesi quadre). E anche l'ultima parte (^a-zA-Z) non funziona, perché includerà semplicemente ^, (, ) e tutte le lettere nella classe di caratteri. La sintassi della negazione funziona solo per intere classi di caratteri come [^a-zA-Z].

Quello che in realtà si vuole è affermare che non c'è nessuna lettera davanti o dopo il tuo u. È possibile utilizzare lookarounds per quello. Il vantaggio è che essi non saranno inclusi nella partita e quindi non verranno rimossi:

r'(?<![a-zA-Z])[uU](?![a-zA-Z])' 

noti che ho usato una stringa raw. È generalmente una buona pratica per le espressioni regolari, per evitare problemi con le sequenze di escape.

Questi sono negativi che assicurano che non ci sia carattere lettera prima o dopo il tuo u. Questa è una differenza importante nell'affermare che c'è un carattere non lettera attorno (che è simile a quello che hai fatto), perché quest'ultimo approccio non funzionerà all'inizio o alla fine della stringa.

Ovviamente, è possibile rimuovere gli spazi intorno a you dalla stringa di sostituzione.

Se non si desidera sostituire u che sono accanto a cifre, si può facilmente comprendere le cifre nelle classi di personaggi:

r'(?<![a-zA-Z0-9])[uU](?![a-zA-Z0-9])' 

E se per qualche motivo una sottolineatura adiacente Sarebbe anche squalificare il tuo u per la sostituzione, potresti includere anche quello. Ma poi la classe del personaggio coincide con il built-in \w:

r'(?<!\w)[uU](?!\w)' 

Che è, in questo caso, equivale a EarlGray di r'\b[uU]\b'.

Come accennato in precedenza è possibile accorciare tutti questi, utilizzando il modificatore senza distinzione tra maiuscole e minuscole. Prendendo la prima espressione come un esempio:

re.sub(r'(?<![a-z])u(?![a-z])', 'you', text, flags=re.I) 

o

re.sub(r'(?<![a-z])u(?![a-z])', 'you', text, flags=re.IGNORECASE) 

a seconda delle preferenze.

Suggerisco di leggere un po 'il tutorial che ho collegato più volte in questa risposta. Le spiegazioni sono molto complete e dovrebbero darti un buon vantaggio sulle espressioni regolari, che probabilmente incontrerai prima o poi.

+2

La tua risposta era eccezionale. Grazie! – user823743

+0

questa è una tecnica generale interessante, ma preferirei usare \ b per abbinare un'interruzione di parola –

+2

@Sam Volevo solo assicurarmi che le implicazioni dell'uso di '\ b' fossero chiare (in particolare, le cifre e i caratteri di sottolineatura sono incluso). –

11

utilizzare un carattere speciale \b, che corrisponde a stringa vuota all'inizio o alla fine di una parola:

print re.sub(r'\b[uU]\b', 'you', text) 

spazi non sono una soluzione affidabile, perché ci sono anche un sacco di altri segni di punteggiatura, quindi un il carattere astratto \b è stato inventato per indicare l'inizio o la fine di una parola.

+2

eccezione del fatto che ' '\ b'' è lo stesso di'' \ x08''. devi scappare (''\\ b'' o' r' \ b'')! – mata

+1

Questo è l'output del tuo codice su "testo" come definito nel codice: come stai? umberella tu! u. U. U @ U # u Quindi nessuno dei tuoi è stato cambiato. – user823743

+1

@ user823743 Sì, ho dimenticato 'r' prima della mia espressione regolare, poiché è stato modificato da Wooble (grazie!). –

0

Un'altra possibile soluzione mi è venuta era:

re.sub(r'([uU]+(.)?\s)',' you ', text)