¿Cómo hacer que el intérprete de Python maneje correctamente los caracteres no ASCII en las operaciones de cadena?


104

Tengo una cadena que se parece a eso:

6 918 417 712

La forma clara de recortar esta cadena (como entiendo Python) es simplemente decir que la cadena está en una variable llamada s, obtenemos:

s.replace('Â ', '')

Eso debería hacer el truco. Pero, por supuesto, se queja de que el carácter no ASCII del '\xc2'archivo blabla.py no está codificado.

Nunca pude entender cómo cambiar entre diferentes codificaciones.

Aquí está el código, realmente es el mismo que el anterior, pero ahora está en contexto. El archivo se guarda como UTF-8 en el bloc de notas y tiene el siguiente encabezado:

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

El código:

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)

No llega más allá de s.replace...


1
Probé todas las 4 respuestas hasta ahora. No vayas. Aún obteniendo el UnicodeDecodeError: el códec 'ascii' no puede decodificar el byte 0xc2 en la posición 1: ordinal no está en el rango (128)
adergaard

su cadena Unicode debe estar precedida poru
SilentGhost

@SilentGhost: como puede ver, no hay forma de estar seguro de que sea una cadena Unicode. Obtengo una cadena que tiene el contenido que se muestra arriba, pero contiene cadenas no ascii. Ese es el verdadero problema. Supongo que es unicode ya que no está en los primeros 128.
adergaard

El error no tiene nada que ver con la cadena entrante. ¡Es una cadena en su código la que genera este error!
SilentGhost

2
Apuesto a que es por eso que Python 3 es tan estricto con la diferencia entre cadenas y secuencias de bytes, solo para evitar este tipo de confusión.
Mark Ransom

Respuestas:


84

Python 2 utiliza asciicomo codificación predeterminada para los archivos de origen, lo que significa que debe especificar otra codificación en la parte superior del archivo para usar caracteres Unicode que no sean ASCII en literales. Python 3 se utiliza utf-8como codificación predeterminada para los archivos de origen, por lo que esto es un problema menor.

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

Para habilitar la codificación de origen utf-8, esto iría en una de las dos líneas superiores:

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

Lo anterior está en los documentos, pero esto también funciona:

# coding: utf-8

Consideraciones adicionales:

  • El archivo de origen también debe guardarse utilizando la codificación correcta en su editor de texto.

  • En Python 2, el literal Unicode debe tener un uantes, como en s.replace(u"Â ", u"")Pero en Python 3, solo use comillas. En Python 2, puede from __future__ import unicode_literalsobtener el comportamiento de Python 3, pero tenga en cuenta que esto afecta a todo el módulo actual.

  • s.replace(u"Â ", u"")también fallará si sno es una cadena Unicode.

  • string.replace devuelve una nueva cadena y no se edita en su lugar, así que asegúrese de usar el valor de retorno también


4
En realidad, solo necesitas # coding: utf-8. -*-no es para decoración, pero es poco probable que lo necesite. Creo que estaba ahí para viejas conchas.
fmalina

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

editar: mi primer impulso es siempre usar un filtro, pero la expresión del generador es más eficiente en memoria (y más corta) ...

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

Tenga en cuenta que se garantiza que esto funciona con la codificación UTF-8 (porque todos los bytes en caracteres multibyte tienen el bit más alto establecido en 1).


1
Recibo: TypeError: ord () esperaba un carácter, pero se encontró una cadena de longitud 2
Ivelin

@Ivelin eso se debe a que el "carácter" no se está interpretando como unicode adecuado ... verifique que su cadena de origen tenga el prefijo usi es un literal.
fortran

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

4
Veo los votos que obtienes, pero cuando lo intento, dice: No. UnicodeDecodeError: el códec 'ascii' no puede decodificar el byte 0xc2 en la posición 1: ordinal no está en el rango (128). ¿Podría ser que mi cadena original no esté en Unicode? Bueno, en cualquier caso. necesita
adergaard

2
Genial gracias. ¿Puedo sugerir usar .decode () en el resultado para obtenerlo en la codificación original?
AkiRoss

Si obtiene UnicodeDecodeError: 'ascii', intente convertir la cadena al formato '' UTF-8 'antes de aplicar la función de codificación.
Sateesh

16

El siguiente código reemplazará todos los caracteres no ASCII con signos de interrogación.

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

Por curiosidad, quería saber eso, ¿hay alguna razón específica para reemplazarlo con el signo de interrogación?
Mohsin

6

Usando Regex:

import re

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

5

Demasiado tarde para una respuesta, pero la cadena original estaba en UTF-8 y '\ xc2 \ xa0' es UTF-8 para NO-BREAK SPACE. Simplemente decodifique la cadena original como s.decode('utf-8')(\ xa0 se muestra como un espacio cuando se decodifica incorrectamente como Windows-1252 o latin-1:

Ejemplo (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

Salida

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

Esto se imprimirá 6 918 417 712


No UnicodeDecodeError: el códec 'ascii' no puede decodificar el byte 0xc2 en la posición 1: ordinal no está en el rango (128). ¿Podría ser que mi cadena original no esté en Unicode? Bueno, en cualquier caso. Probablemente estoy haciendo algo mal.
adergaard

@adergaard, ¿agregó # - - coding: utf-8 - - en la parte superior del archivo fuente?
Nadia Alramli

Sí, vea la parte superior de esta página nuevamente, he editado la pregunta e ingresado el código y los comentarios del encabezado. Gracias por tu ayuda.
adergaard

Creo que tendrá que averiguar cómo obtener las cadenas del documento html o xml en unicode. Más información sobre eso aquí: diveintopython.org/xml_processing/unicode.html
Isaías

2

Sé que es un hilo antiguo, pero me sentí obligado a mencionar el método de traducción, que siempre es una buena manera de reemplazar todos los códigos de caracteres por encima de 128 (u otro si es necesario).

Uso : str. traducir ( tabla [, eliminar caracteres] )

>>> 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 partir de Python 2.6 , también puede establecer la tabla en Ninguno y usar deletechars para eliminar los caracteres que no desea, como en los ejemplos que se muestran en los documentos estándar en http://docs.python.org/library/stdtypes. html .

Con cadenas Unicode, la tabla de traducción no es una cadena de 256 caracteres sino un dict con el ord () de los caracteres relevantes como claves. Pero de todos modos, obtener una cadena ascii adecuada de una cadena unicode es bastante simple, usando el método mencionado por truppo anteriormente, a saber: unicode_string.encode ("ascii", "ignore")

Como resumen, si por alguna razón es absolutamente necesario obtener una cadena ascii (por ejemplo, cuando genera una excepción estándar con raise Exception, ascii_message), puede usar la siguiente función:

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)

Lo bueno de translate es que puedes convertir caracteres acentuados en caracteres ascii relevantes sin acentos en lugar de simplemente eliminarlos o reemplazarlos por '?'. Esto suele ser útil, por ejemplo, para fines de indexación.


Recibo: TypeError: el mapeo de caracteres debe devolver un entero, Ninguno o Unicode
Ivelin

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

y haga que su .pyarchivo sea unicode.


1

Este es un truco sucio, pero puede funcionar.

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

0

Por lo que valía, mi juego de caracteres era utf-8y había incluido la # -*- coding: utf-8 -*-línea clásica " ".

Sin embargo, descubrí que no tenía Universal Newlines al leer estos datos de una página web.

Mi texto tenía dos palabras, separadas por " \r\n". Solo estaba dividiendo \ny reemplazando el "\n".

Una vez que recorrí y vi el juego de caracteres en cuestión, me di cuenta del error.

Entonces, también podría estar dentro del conjunto de caracteres ASCII , pero un carácter que no esperabas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.