Spogliare tutto tranne i caratteri alfanumerici da una stringa in Python


339

Qual è il modo migliore per rimuovere tutti i caratteri non alfanumerici da una stringa, usando Python?

Le soluzioni presentate nella variante PHP di questa domanda probabilmente funzioneranno con alcune piccole modifiche, ma non mi sembrano molto "pitoniche".

Per la cronaca, non voglio solo eliminare punti e virgole (e altre punteggiatura), ma anche virgolette, parentesi, ecc.


8
Ti interessano i caratteri alfanumerici internazionali, come 'æøå', 'مرحبا', 'สวัสดี', 'こ ん に ち は'?
Pimin Konstantin Kefaloukos,

4
@PiminKonstantinKefaloukos Sì, mi interessano i caratteri internazionali, quindi il mio commento sulla risposta accettata per utilizzare re.UNICODE.
Mark van Lent,

Risposte:


337

Ho appena cronometrato alcune funzioni per curiosità. In questi test sto rimuovendo caratteri non alfanumerici dalla stringa string.printable(parte del stringmodulo integrato). L'uso di compilato '[\W_]+'ed è pattern.sub('', str)stato trovato per essere più veloce.

$ python -m timeit -s \
     "import string" \
     "''.join(ch for ch in string.printable if ch.isalnum())" 
10000 loops, best of 3: 57.6 usec per loop

$ python -m timeit -s \
    "import string" \
    "filter(str.isalnum, string.printable)"                 
10000 loops, best of 3: 37.9 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]', '', string.printable)"
10000 loops, best of 3: 27.5 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]+', '', string.printable)"                
100000 loops, best of 3: 15 usec per loop

$ python -m timeit -s \
    "import re, string; pattern = re.compile('[\W_]+')" \
    "pattern.sub('', string.printable)" 
100000 loops, best of 3: 11.2 usec per loop

2
Risultati molto interessanti: mi sarei aspettato che le espressioni regolari fossero più lente. È interessante notare che ho provato questo con un'altra opzione ( valid_characters = string.ascii_letters + string.digitsseguita da join(ch for ch in string.printable if ch in valid_characters)ed era 6 microsecondi più veloce isalnum()dell'opzione. Comunque molto più lenta della regexp.
DrAl

+1, il tempo di misurazione è buono! (ma nel penultimo, pattern.sub('', string.printable)invece , fai - sciocco chiamare re.sub quando hai un oggetto RE! -).
Alex Martelli,

46
Per la cronaca: usare re.compile('[\W_]+', re.UNICODE)per renderlo unicode sicuro.
Mark van Lent,

3
come si fa senza rimuovere lo spazio bianco?
Maudulus,

6
fallo senza rimuovere lo spazio bianco: re.sub ('[\ W _] +', '', frase, flags = re.UNICODE)
PALEN

269

Espressioni regolari in soccorso:

import re
re.sub(r'\W+', '', your_string)

Con Python definition '\W== [^a-zA-Z0-9_], che esclude tutto numbers, letterse_


2
Cosa fa il segno più nella regexp? (So ​​cosa significa, solo curioso di sapere perché è necessario per il re.sub.)
Mark van Lent

7
@Mark: immagino che accelererebbe la sostituzione poiché il rimpiazzo eliminerà tutti i caratteri non di parole in un blocco in una volta, piuttosto che rimuoverli uno per uno.
DrAl,

2
Sì, l'ho messo in panchina mentre sintonizzavo un codice critico per le prestazioni qualche tempo fa. Se ci sono intervalli significativi di personaggi per sostituire l'accelerazione è enorme.
Ants Aasma,

20
In questo caso potrebbe non essere pertinente, ma \Wmanterrà anche i trattini bassi.
Blixt,

12
Seguendo il suggerimento di @Blixt, se vuoi solo lettere e numeri puoi fare re.sub (r '[^ a-zA-Z0-9]', '', your_string)
Nigini

69

Utilizzare il metodo str.translate () .

Presumendo che lo farai spesso:

(1) Una volta, crea una stringa contenente tutti i caratteri che desideri eliminare:

delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())

(2) Ogni volta che vuoi scrunch una stringa:

scrunched = s.translate(None, delchars)

Il costo di installazione probabilmente si confronta favorevolmente con re.compile; il costo marginale è molto più basso:

C:\junk>\python26\python -mtimeit -s"import string;d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s=string.printable" "s.translate(None,d)"
100000 loops, best of 3: 2.04 usec per loop

C:\junk>\python26\python -mtimeit -s"import re,string;s=string.printable;r=re.compile(r'[\W_]+')" "r.sub('',s)"
100000 loops, best of 3: 7.34 usec per loop

Nota: l' utilizzo di string.printable come dati di riferimento conferisce al modello '[\ W _] +' un vantaggio sleale ; tutti i caratteri non alfanumerici sono in un gruppo ... in dati tipici ci sarebbe più di una sostituzione da fare:

C:\junk>\python26\python -c "import string; s = string.printable; print len(s),repr(s)"
100 '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

Ecco cosa succede se dai a re.sub un po 'più di lavoro da fare:

C:\junk>\python26\python -mtimeit -s"d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s='foo-'*25" "s.translate(None,d)"
1000000 loops, best of 3: 1.97 usec per loop

C:\junk>\python26\python -mtimeit -s"import re;s='foo-'*25;r=re.compile(r'[\W_]+')" "r.sub('',s)"
10000 loops, best of 3: 26.4 usec per loop

1
L'uso di translate è in effetti un po 'più veloce. Anche quando si aggiunge un ciclo for subito prima di effettuare la sostituzione / traduzione (per ridurre i costi di installazione in meno), la traduzione è circa 17 volte più veloce del regexp sulla mia macchina. Buono a sapersi.
Mark van Lent,

3
Questa è sicuramente la soluzione più pitonica.
codygman,

1
Questo mi convince quasi, ma suggerirei di usare string.punctuationInvece di''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols il

1
Nota che questo funziona con stroggetti ma non con unicodeoggetti.
Yavar,

@John Machin È essenzialmente una comprensione dell'elenco che viene passata come argomento a .join()?
AdjunctProfessorFalcon,

42

Puoi provare:

print ''.join(ch for ch in some_string if ch.isalnum())

16
>>> import re
>>> string = "Kl13@£$%[};'\""
>>> pattern = re.compile('\W')
>>> string = re.sub(pattern, '', string)
>>> print string
Kl13

1
ho adorato la tua risposta ma rimuove anche i caratteri arabi, puoi dirmi come mantenerli
Charif DZ,

14

Che ne dite di:

def ExtractAlphanumeric(InputString):
    from string import ascii_letters, digits
    return "".join([ch for ch in InputString if ch in (ascii_letters + digits)])

Funziona usando la comprensione dell'elenco per produrre un elenco dei caratteri InputStringse sono presenti nelle combinazioni ascii_letterse nelle digitsstringhe. Quindi unisce l'elenco in una stringa.


Sembra che string.ascii_letters contenga solo lettere (duh) e non numeri. Ho anche bisogno dei numeri ...
Mark van Lent,

L'aggiunta di string.digits risolverebbe davvero il problema che ho appena menzionato. :)
Mark van Lent,

Sì, mi sono reso conto che quando sono tornato a leggere la tua domanda. Nota per se stessi: impara a leggere!
DrAl,

5

Come derivazione da alcune altre risposte qui, offro un modo davvero semplice e flessibile per definire un set di caratteri a cui si desidera limitare il contenuto di una stringa. In questo caso, sto consentendo alfanumerici PLUS trattino e trattino basso. Basta aggiungere o rimuovere caratteri dal mio PERMITTED_CHARScome si adatta al tuo caso d'uso.

PERMITTED_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" 
someString = "".join(c for c in someString if c in PERMITTED_CHARS)

3
Invece di codificare i caratteri consentiti, che sono inclini a errori sottili, usare string.digits + string.ascii_letters + '_-'.
Reti43,

Il tuo suggerimento non è sbagliato, ma non salva anche molti caratteri di "battitura" se questo è il tuo obiettivo. Se copi il mio post, non avrai nemmeno un refuso! Il vero punto, tuttavia, della mia risposta è consentire un mezzo esplicito, aperto e semplice per definire esattamente quali caratteri si desidera consentire.
BuvinJ,

Come via di mezzo, potresti combinare questi suggerimenti in SPECIAL_CHARS = '_-'e poi usarestring.digits + string.ascii_letters + SPECIAL_CHARS
BuvinJ

Era un suggerimento in termini di ciò che è ragionevole, a meno che non stiamo facendo il golf del codice. "Camminare" intorno alla tastiera per scrivere 52 lettere alfabetiche in ordine richiede molto più tempo dell'importazione di un pacchetto per usare un oggetto o due. E questo non include il tempo di ricontrollare che hai digitato tutto correttamente. Si tratta di buone pratiche, tutto qui.
Reti43,

Ti sento! Il mio vero punto qui è l'estrema flessibilità, nel caso in cui desideri essere più specifico con il tuo set di caratteri.
BuvinJ,

5
sent = "".join(e for e in sent if e.isalpha())

Proverò a spiegare: passa attraverso tutti i caratteri stringa e for e in sente controlla tramite if e.isalpha()istruzione se il carattere corrente è un simbolo alfabetico, in tal caso - lo unisce alla sentvariabile tramite sent = "".join()e tutti i simboli non alfabetici verranno sostituiti con ""(stringa vuota) perché di joinfunzione.
Sysanin,

dal momento che questo sta facendo un ciclo per personaggio piuttosto che fare affidamento su C regex, non è estremamente lento?
dcsan,

3
for char in my_string:
    if not char.isalnum():
        my_string = my_string.replace(char,"")

2

Tempismo con stringhe casuali di stampabili ASCII:

from inspect import getsource
from random import sample
import re
from string import printable
from timeit import timeit

pattern_single = re.compile(r'[\W]')
pattern_repeat = re.compile(r'[\W]+')
translation_tb = str.maketrans('', '', ''.join(c for c in map(chr, range(256)) if not c.isalnum()))


def generate_test_string(length):
    return ''.join(sample(printable, length))


def main():
    for i in range(0, 60, 10):
        for test in [
            lambda: ''.join(c for c in generate_test_string(i) if c.isalnum()),
            lambda: ''.join(filter(str.isalnum, generate_test_string(i))),
            lambda: re.sub(r'[\W]', '', generate_test_string(i)),
            lambda: re.sub(r'[\W]+', '', generate_test_string(i)),
            lambda: pattern_single.sub('', generate_test_string(i)),
            lambda: pattern_repeat.sub('', generate_test_string(i)),
            lambda: generate_test_string(i).translate(translation_tb),

        ]:
            print(timeit(test), i, getsource(test).lstrip('            lambda: ').rstrip(',\n'), sep='\t')


if __name__ == '__main__':
    main()

Risultato (Python 3.7):

       Time       Length                           Code                           
6.3716264850008880  00  ''.join(c for c in generate_test_string(i) if c.isalnum())
5.7285426190064750  00  ''.join(filter(str.isalnum, generate_test_string(i)))
8.1875841680011940  00  re.sub(r'[\W]', '', generate_test_string(i))
8.0002205439959650  00  re.sub(r'[\W]+', '', generate_test_string(i))
5.5290945199958510  00  pattern_single.sub('', generate_test_string(i))
5.4417179649972240  00  pattern_repeat.sub('', generate_test_string(i))
4.6772285089973590  00  generate_test_string(i).translate(translation_tb)
23.574712151996210  10  ''.join(c for c in generate_test_string(i) if c.isalnum())
22.829975890002970  10  ''.join(filter(str.isalnum, generate_test_string(i)))
27.210196289997840  10  re.sub(r'[\W]', '', generate_test_string(i))
27.203713296003116  10  re.sub(r'[\W]+', '', generate_test_string(i))
24.008979928999906  10  pattern_single.sub('', generate_test_string(i))
23.945240008994006  10  pattern_repeat.sub('', generate_test_string(i))
21.830899796994345  10  generate_test_string(i).translate(translation_tb)
38.731336012999236  20  ''.join(c for c in generate_test_string(i) if c.isalnum())
37.942474347000825  20  ''.join(filter(str.isalnum, generate_test_string(i)))
42.169366310001350  20  re.sub(r'[\W]', '', generate_test_string(i))
41.933375883003464  20  re.sub(r'[\W]+', '', generate_test_string(i))
38.899814646996674  20  pattern_single.sub('', generate_test_string(i))
38.636144253003295  20  pattern_repeat.sub('', generate_test_string(i))
36.201238164998360  20  generate_test_string(i).translate(translation_tb)
49.377356811004574  30  ''.join(c for c in generate_test_string(i) if c.isalnum())
48.408927293996385  30  ''.join(filter(str.isalnum, generate_test_string(i)))
53.901889764994850  30  re.sub(r'[\W]', '', generate_test_string(i))
52.130339455994545  30  re.sub(r'[\W]+', '', generate_test_string(i))
50.061149017004940  30  pattern_single.sub('', generate_test_string(i))
49.366573111998150  30  pattern_repeat.sub('', generate_test_string(i))
46.649754120997386  30  generate_test_string(i).translate(translation_tb)
63.107938601999194  40  ''.join(c for c in generate_test_string(i) if c.isalnum())
65.116287978999030  40  ''.join(filter(str.isalnum, generate_test_string(i)))
71.477421126997800  40  re.sub(r'[\W]', '', generate_test_string(i))
66.027950693998720  40  re.sub(r'[\W]+', '', generate_test_string(i))
63.315361931003280  40  pattern_single.sub('', generate_test_string(i))
62.342320287003530  40  pattern_repeat.sub('', generate_test_string(i))
58.249303059004890  40  generate_test_string(i).translate(translation_tb)
73.810345625002810  50  ''.join(c for c in generate_test_string(i) if c.isalnum())
72.593953348005020  50  ''.join(filter(str.isalnum, generate_test_string(i)))
76.048324580995540  50  re.sub(r'[\W]', '', generate_test_string(i))
75.106637657001560  50  re.sub(r'[\W]+', '', generate_test_string(i))
74.681338128997600  50  pattern_single.sub('', generate_test_string(i))
72.430461594005460  50  pattern_repeat.sub('', generate_test_string(i))
69.394243567003290  50  generate_test_string(i).translate(translation_tb)

str.maketrans& str.translateè il più veloce, ma include tutti i caratteri non ASCII. re.compile& pattern.subè più lento, ma è in qualche modo più veloce di ''.join& filter.


-1

Se ho capito correttamente il modo più semplice è usare l'espressione regolare in quanto ti offre molta flessibilità, ma l'altro metodo semplice da usare per il ciclo che segue è il codice con l'esempio ho anche contato l'occorrenza della parola e memorizzata nel dizionario.

s = """An... essay is, generally, a piece of writing that gives the author's own 
argument — but the definition is vague, 
overlapping with those of a paper, an article, a pamphlet, and a short story. Essays 
have traditionally been 
sub-classified as formal and informal. Formal essays are characterized by "serious 
purpose, dignity, logical 
organization, length," whereas the informal essay is characterized by "the personal 
element (self-revelation, 
individual tastes and experiences, confidential manner), humor, graceful style, 
rambling structure, unconventionality 
or novelty of theme," etc.[1]"""

d = {}      # creating empty dic      
words = s.split() # spliting string and stroing in list
for word in words:
    new_word = ''
    for c in word:
        if c.isalnum(): # checking if indiviual chr is alphanumeric or not
            new_word = new_word + c
    print(new_word, end=' ')
    # if new_word not in d:
    #     d[new_word] = 1
    # else:
    #     d[new_word] = d[new_word] +1
print(d)

si prega di valutare questo se questa risposta è utile!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.