Come posso rimuovere caratteri non ASCII ma lasciare punti e spazi usando Python?


100

Sto lavorando con un file .txt. Voglio una stringa di testo dal file senza caratteri non ASCII. Tuttavia, voglio lasciare spazi e periodi. Al momento sto spogliando anche quelli. Ecco il codice:

def onlyascii(char):
    if ord(char) < 48 or ord(char) > 127: return ''
    else: return char

def get_my_string(file_path):
    f=open(file_path,'r')
    data=f.read()
    f.close()
    filtered_data=filter(onlyascii, data)
    filtered_data = filtered_data.lower()
    return filtered_data

Come devo modificare onlyascii () per lasciare spazi e punti? Immagino non sia troppo complicato ma non riesco a capirlo.


Grazie (sinceramente) per il chiarimento Giovanni. Ho capito che spazi e punti sono caratteri ASCII. Tuttavia, stavo rimuovendo entrambi involontariamente mentre cercavo di rimuovere solo caratteri non ASCII. Capisco come la mia domanda avrebbe potuto implicare diversamente.

@PoliticalEconomist: il tuo problema è ancora molto sotto specificato. Vedi la mia risposta.
John Machin

Risposte:


187

Puoi filtrare tutti i caratteri dalla stringa che non sono stampabili utilizzando string.printable , in questo modo:

>>> s = "some\x00string. with\x15 funny characters"
>>> import string
>>> printable = set(string.printable)
>>> filter(lambda x: x in printable, s)
'somestring. with funny characters'

string.printable sulla mia macchina contiene:

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c

EDIT: su Python 3, il filtro restituirà un iterabile. Il modo corretto per ottenere una stringa indietro sarebbe:

''.join(filter(lambda x: x in printable, s))

2
che succede con quei caratteri stampabili inferiori al numero ordinale 48?
joaquin

38
L'unico problema con l'utilizzo filterè che restituisce un iterabile. Se avete bisogno di un back stringa (come ho fatto perché avevo bisogno di questo quando si fa la compressione lista) allora fare questo: ''.join(filter(lambda x: x in string.printable, s).
cjbarth,

5
@cjbarth - il commento è specifico per Python 3, ma molto utile. Grazie!
scossa

7
Perché non usare espressioni regolari: re.sub(r'[^\x00-\x7f]',r'', your-non-ascii-string). Vedi questo thread stackoverflow.com/a/20079244/658497
Noam Manos

1
@ NoamManos questo è stato 4-5 volte più veloce per me che nella join ... filter ... soluzione lambda, grazie.
artfulrobot

95

Un modo semplice per passare a un codec diverso è usare encode () o decode (). Nel tuo caso, vuoi convertire in ASCII e ignorare tutti i simboli che non sono supportati. Ad esempio, la lettera svedese å non è un carattere ASCII:

    >>>s = u'Good bye in Swedish is Hej d\xe5'
    >>>s = s.encode('ascii',errors='ignore')
    >>>print s
    Good bye in Swedish is Hej d

Modificare:

Python3: str -> byte -> str

>>>"Hej då".encode("ascii", errors="ignore").decode()
'hej d'

Python2: unicode -> str -> unicode

>>> u"hej då".encode("ascii", errors="ignore").decode()
u'hej d'

Python2: str -> unicode -> str (decodifica e codifica in ordine inverso)

>>> "hej d\xe5".decode("ascii", errors="ignore").encode()
'hej d'

16
OttengoUnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 27
Xodarap777

2
Ho ricevuto quell'errore quando ho inserito il carattere unicode effettivo nella stringa tramite copia incolla. Quando si specifica una stringa come u'thestring 'la codifica funziona correttamente.
Ben Liyanage

2
Funziona solo su Py3, ma è elegante.
gaborous

7
Per coloro che ottengono lo stesso errore di @ Xodarap777: dovresti prima .decode () la stringa e solo dopo quella codifica. Ad esempios.decode('utf-8').encode('ascii', errors='ignore')
Spc_555


6

La tua domanda è ambigua; le prime due frasi prese insieme implicano che si crede che lo spazio e il "punto" non siano caratteri ASCII. Questo non è corretto. Tutti i caratteri tali che ord (char) <= 127 siano caratteri ASCII. Ad esempio, la tua funzione esclude questi caratteri! "# $% & \ '() * +, -. / Ma include molti altri, ad esempio [] {}.

Per favore, fai un passo indietro, pensa un po 'e modifica la tua domanda per dirci cosa stai cercando di fare, senza menzionare la parola ASCII, e perché pensi che i caratteri tali che ord (char)> = 128 siano ignorabili. Inoltre: quale versione di Python? Qual è la codifica dei dati di input?

Nota che il tuo codice legge l'intero file di input come una singola stringa e il tuo commento ("ottima soluzione") a un'altra risposta implica che non ti interessano le nuove righe nei tuoi dati. Se il tuo file contiene due righe come questa:

this is line 1
this is line 2

il risultato sarebbe 'this is line 1this is line 2'... è quello che vuoi veramente?

Una soluzione più ampia includerebbe:

  1. un nome migliore per la funzione di filtro rispetto a onlyascii
  2. riconoscimento che una funzione di filtro deve semplicemente restituire un valore vero se l'argomento deve essere mantenuto:

    def filter_func(char):
        return char == '\n' or 32 <= ord(char) <= 126
    # and later:
    filtered_data = filter(filter_func, data).lower()

Questa risposta è molto utile per quelli di noi che vengono a chiedere qualcosa di simile all'OP, e la tua risposta proposta è utilmente pitonica. Tuttavia, trovo strano che non ci sia una soluzione più efficiente al problema come lo hai interpretato (cosa che spesso incontro) - carattere per carattere, questo richiede molto tempo in un file molto grande.
Xodarap777

5

È possibile utilizzare il codice seguente per rimuovere le lettere non inglesi:

import re
str = "123456790 ABC#%? .(朱惠英)"
result = re.sub(r'[^\x00-\x7f]',r'', str)
print(result)

Questo tornerà

123456790 ABC #%? . ()


1

Se vuoi caratteri ASCII stampabili, probabilmente dovresti correggere il tuo codice in:

if ord(char) < 32 or ord(char) > 126: return ''

questo è equivalente, a string.printable(risposta da @jterrace), tranne per l'assenza di ritorni e tabulazioni ('\ t', '\ n', '\ x0b', '\ x0c' e '\ r') ma non corrisponde a l'intervallo sulla tua domanda


1
Leggermente più semplice: lambda x: 32 <= ord (x) <= 126
jterrace

non è lo stesso di string.printable perché esclude string.whitespace, anche se potrebbe essere ciò che vuole l'OP, dipende da cose come \ ne \ t.
jterrace

@jterrace right, include lo spazio (ord 32) ma nessun ritorni e tabulazioni
joaquin

sì, sto solo commentando "questo è equivalente a string.printable", ma non è vero
jterrace

Ho modificato la risposta, grazie! la domanda OP è fuorviante se non la leggi attentamente.
joaquin

1

Mi faccio strada attraverso Fluent Python (Ramalho) - altamente raccomandato. Elenca la comprensione di battute univoche ispirate al Capitolo 2:

onlyascii = ''.join([s for s in data if ord(s) < 127])
onlymatch = ''.join([s for s in data if s in
              'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'])

Ciò non consentirebbe simboli ASCII standard, come punti elenco, simbolo dei gradi, simbolo del copyright, simbolo dello yen, ecc. Inoltre, il tuo primo esempio include simboli non stampabili, come BELL, che non è desiderabile.
SherylHohman
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.