Come fare in modo che l'interprete Python gestisca correttamente i caratteri non ASCII nelle operazioni sulle stringhe?


104

Ho una stringa che sembra così:

6 918 417 712

Il modo chiaro per tagliare questa stringa (come ho capito Python) è semplicemente dire che la stringa si trova in una variabile chiamata s, otteniamo:

s.replace('Â ', '')

Questo dovrebbe fare il trucco. Ma ovviamente si lamenta del fatto che il carattere non ASCII '\xc2'nel file blabla.py non è codificato.

Non sono mai riuscito a capire come passare da una codifica all'altra.

Ecco il codice, è proprio come sopra, ma ora è nel contesto. Il file viene salvato come UTF-8 nel blocco note e ha la seguente intestazione:

#!/usr/bin/python2.4
# -*- coding: utf-8 -*-

Il codice:

f = urllib.urlopen(url)

soup = BeautifulSoup(f)

s = soup.find('div', {'id':'main_count'})

#making a print 's' here goes well. it shows 6Â 918Â 417Â 712

s.replace('Â ','')

save_main_count(s)

Non va oltre s.replace...


1
Finora ho provato tutte e 4 le risposte. No go. Ricevo ancora UnicodeDecodeError: il codec 'ascii' non può decodificare il byte 0xc2 in posizione 1: ordinale non compreso nell'intervallo (128)
adergaard

la tua stringa Unicode deve essere preceduta dau
SilentGhost

@SilentGhost: come puoi vedere, non c'è modo di essere sicuri che sia una stringa Unicode. Ottengo una stringa che ha il contenuto mostrato sopra, ma contiene stringhe non ascii. Questo è il vero problema. Immagino che sia Unicode poiché non è nei primi 128.
adergaard,

L'errore non ha nulla a che fare con la stringa in arrivo. È una stringa nel codice che genera questo errore!
SilentGhost

2
Scommetto che questo è il motivo per cui Python 3 è così severo sulla differenza tra stringhe e sequenze di byte, solo per evitare questo tipo di confusione.
Mark Ransom

Risposte:


84

Python 2 usa asciicome codifica predefinita per i file sorgente, il che significa che devi specificare un'altra codifica all'inizio del file per usare caratteri Unicode non ASCII in letterali. Python 3 utilizza utf-8come codifica predefinita per i file sorgente, quindi questo è un problema minore.

Vedi: http://docs.python.org/tutorial/interpreter.html#source-code-encoding

Per abilitare la codifica sorgente utf-8, questo andrebbe in una delle prime due righe:

# -*- coding: utf-8 -*-

Quanto sopra è nei documenti, ma funziona anche:

# coding: utf-8

Considerazioni aggiuntive:

  • Il file sorgente deve essere salvato utilizzando la codifica corretta anche nel tuo editor di testo.

  • In Python 2, il letterale unicode deve avere un uprima di esso, come in s.replace(u"Â ", u"")Ma in Python 3, usa solo le virgolette. In Python 2, puoi from __future__ import unicode_literalsottenere il comportamento di Python 3, ma tieni presente che questo influisce sull'intero modulo corrente.

  • s.replace(u"Â ", u"")fallirà anche se snon è una stringa Unicode.

  • string.replace restituisce una nuova stringa e non la modifica in posizione, quindi assicurati di utilizzare anche il valore restituito


4
In realtà hai solo bisogno # coding: utf-8. -*-non è per la decorazione, ma è improbabile che tu ne abbia mai bisogno. Penso che fosse lì per vecchie conchiglie.
fmalina

157
def removeNonAscii(s): return "".join(filter(lambda x: ord(x)<128, s))

modifica: il mio primo impulso è sempre quello di utilizzare un filtro, ma l'espressione del generatore è più efficiente in termini di memoria (e più breve) ...

def removeNonAscii(s): return "".join(i for i in s if ord(i)<128)

Tieni presente che è garantito che funzioni con la codifica UTF-8 (perché tutti i byte nei caratteri multibyte hanno il bit più alto impostato su 1).


1
Ottengo: TypeError: ord () prevedeva un carattere, ma è stata trovata una stringa di lunghezza 2
Ivelin

@Ivelin è perché il "carattere" non viene interpretato come unicode corretto ... controlla che la tua stringa di origine abbia il prefisso use è un letterale.
Fortran

35
>>> unicode_string = u"hello aåbäcö"
>>> unicode_string.encode("ascii", "ignore")
'hello abc'

4
Vedo i voti che ottieni ma quando lo provo dice: No. UnicodeDecodeError: il codec "ascii" non può decodificare il byte 0xc2 nella posizione 1: ordinale non compreso nell'intervallo (128). Potrebbe essere che la mia stringa originale non sia in Unicode? Bene in ogni caso. ha bisogno
adergaard

2
Bene grazie. Posso suggerire di usare .decode () sul risultato per ottenerlo nella codifica originale?
AkiRoss

Se ottieni UnicodeDecodeError: "ascii", prova a convertire la stringa nel formato "UTF-8" prima di applicare la funzione di codifica.
Sateesh

16

Il codice seguente sostituirà tutti i caratteri non ASCII con punti interrogativi.

"".join([x if ord(x) < 128 else '?' for x in s])

Per curiosità, volevo sapere che, c'è qualche motivo specifico per sostituirlo con il punto interrogativo?
Mohsin

6

Utilizzando Regex:

import re

strip_unicode = re.compile("([^-_a-zA-Z0-9!@#%&=,/'\";:~`\$\^\*\(\)\+\[\]\.\{\}\|\?\<\>\\]+|[^\s]+)")
print strip_unicode.sub('', u'6Â 918Â 417Â 712')

5

Troppo tardi per una risposta, ma la stringa originale era in UTF-8 e "\ xc2 \ xa0" è UTF-8 per NO-BREAK SPACE. s.decode('utf-8')Decodifica semplicemente la stringa originale come (\ xa0 viene visualizzato come uno spazio se decodificato in modo errato come Windows-1252 o latin-1:

Esempio (Python 3)

s = b'6\xc2\xa0918\xc2\xa0417\xc2\xa0712'
print(s.decode('latin-1')) # incorrectly decoded
u = s.decode('utf8') # correctly decoded
print(u)
print(u.replace('\N{NO-BREAK SPACE}','_'))
print(u.replace('\xa0','-')) # \xa0 is Unicode for NO-BREAK SPACE

Produzione

6 918 417 712
6 918 417 712
6_918_417_712
6-918-417-712

3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

s = u"6Â 918Â 417Â 712"
s = s.replace(u"Â", "") 
print s

Questo verrà stampato 6 918 417 712


No. UnicodeDecodeError: il codec "ascii" non può decodificare il byte 0xc2 nella posizione 1: ordinale non compreso nell'intervallo (128). Potrebbe essere che la mia stringa originale non sia in Unicode? Bene in ogni caso. Probabilmente sto facendo qualcosa di sbagliato.
adergaard

@adergaard, hai aggiunto # - - coding: utf-8 - - all'inizio del file sorgente?
Nadia Alramli

Sì, vedi di nuovo la parte superiore di questa pagina, ho modificato questo in e inserito il codice ei commenti di intestazione. Grazie per la tua assistenza.
adergaard

Penso che dovrai capire come ottenere le stringhe dal documento html o xml in unicode. Maggiori informazioni su questo qui: diveintopython.org/xml_processing/unicode.html
Isaiah

2

So che è un vecchio thread, ma mi sono sentito obbligato a menzionare il metodo di traduzione, che è sempre un buon modo per sostituire tutti i codici di caratteri superiori a 128 (o altri se necessario).

Utilizzo : str. translate ( table [, deletechars] )

>>> trans_table = ''.join( [chr(i) for i in range(128)] + [' '] * 128 )

>>> 'Résultat'.translate(trans_table)
'R sultat'
>>> '6Â 918Â 417Â 712'.translate(trans_table)
'6  918  417  712'

A partire da Python 2.6 , puoi anche impostare la tabella su Nessuno e usare deletechars per eliminare i caratteri che non vuoi come negli esempi mostrati nei documenti standard su http://docs.python.org/library/stdtypes. html .

Con le stringhe Unicode, la tabella di traduzione non è una stringa di 256 caratteri ma un dict con l'ord () di caratteri rilevanti come chiavi. Ma comunque ottenere una stringa ascii corretta da una stringa unicode è abbastanza semplice, usando il metodo menzionato da truppo sopra, ovvero: unicode_string.encode ("ascii", "ignore")

In sintesi, se per qualche motivo hai assolutamente bisogno di ottenere una stringa ascii (ad esempio, quando sollevi un'eccezione standard con raise Exception, ascii_message), puoi usare la seguente funzione:

trans_table = ''.join( [chr(i) for i in range(128)] + ['?'] * 128 )
def ascii(s):
    if isinstance(s, unicode):
        return s.encode('ascii', 'replace')
    else:
        return s.translate(trans_table)

La cosa buona di translate è che puoi effettivamente convertire i caratteri accentati in caratteri ascii non accentati pertinenti invece di eliminarli o sostituirli con "?". Questo è spesso utile, ad esempio per scopi di indicizzazione.


Ottengo: TypeError: la mappatura dei caratteri deve restituire un numero intero, Nessuno o Unicode
Ivelin

1
s.replace(u'Â ', '')              # u before string is important

e rendi il tuo .pyfile unicode.


1

Questo è un trucco sporco, ma potrebbe funzionare.

s2 = ""
for i in s:
    if ord(i) < 128:
        s2 += i

0

Per quel che valeva, il mio set di caratteri era utf-8e avevo incluso la classica # -*- coding: utf-8 -*-linea " ".

Tuttavia, ho scoperto che non avevo Newline universali durante la lettura di questi dati da una pagina web.

Il mio testo aveva due parole, separate da " \r\n". Stavo solo dividendo \ne sostituendo il "\n".

Dopo aver eseguito il looping e visto il set di caratteri in questione, mi sono reso conto dell'errore.

Quindi, potrebbe anche essere all'interno del set di caratteri ASCII , ma un carattere che non ti aspettavi.

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.