¿Cómo verificar si una cadena en Python está en ASCII?

212

Quiero verificar si una cadena está en ASCII o no.

Soy consciente ord(), sin embargo, cuando lo intento ord('é'), lo he hecho TypeError: ord() expected a character, but string of length 2 found. Comprendí que es causado por la forma en que construí Python (como se explica en ord()la documentación ).

¿Hay otra forma de verificar?

Nico
fuente
La codificación de cadenas difiere bastante entre Python 2 y Python 3, por lo que sería bueno saber a qué versión se dirige.
florisla

Respuestas:

188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)
Alexander Kojevnikov
fuente
95
Inútilmente ineficiente. Mucho mejor probar s.decode ('ascii') y atrapar UnicodeDecodeError, como lo sugiere Vincent Marchetti.
ddaa
20
No es ineficiente. all () cortocircuitará y devolverá False tan pronto como encuentre un byte no válido.
John Millikin el
10
Ineficiente o no, el método más pitónico es el try / except.
Jeremy Cantrell
43
Es ineficiente en comparación con el try / except. Aquí el bucle está en el intérprete. Con el formulario try / except, el bucle está en la implementación del códec C llamado por str.decode ('ascii'). Y estoy de acuerdo, la forma try / except también es más pitónica.
ddaa
25
@JohnMachin ord(c) < 128es infinitamente más legible e intuitivo quec <= "\x7F"
Slater Victoroff
253

Creo que no estás haciendo la pregunta correcta.

Una cadena en python no tiene ninguna propiedad correspondiente a 'ascii', utf-8 o cualquier otra codificación. La fuente de su cadena (ya sea que la lea de un archivo, la entrada de un teclado, etc.) puede haber codificado una cadena unicode en ascii para producir su cadena, pero ahí es donde debe buscar una respuesta.

Quizás la pregunta que puede hacer es: "¿Es esta cadena el resultado de codificar una cadena unicode en ascii?" - Esto puede responder intentando:

try:
    mystring.decode('ascii')
except UnicodeDecodeError:
    print "it was not a ascii-encoded unicode string"
else:
    print "It may have been an ascii-encoded unicode string"
Vincent Marchetti
fuente
28
usar codificar es mejor, porque el método de cadena sin decodificación en Python 3, ¿cuál es la diferencia entre codificar / decodificar? (python 2.x)
Jet Guo
@Sri: Eso es porque lo estás usando en una cadena no codificada ( stren Python 2, bytesen Python 3).
dotancohen
En Python 2, esta solución solo funciona para una cadena Unicode . A stren cualquier codificación ISO necesitaría codificarse primero a Unicode. La respuesta debería entrar en esto.
alexis
@JetGuo: debe usar ambos según el tipo de entrada: s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')en Python 3. La entrada de OP es una cadena de bytes 'é'(sintaxis de Python 2, Python 3 no se había lanzado en ese momento) y, por .decode()lo tanto, es correcta.
jfs
2
@alexis: mal. stren Python 2 es una cadena de bytes. Es correcto usarlo .decode('ascii')para averiguar si todos los bytes están en el rango ASCII.
jfs
153

Python 3 vías:

isascii = lambda s: len(s) == len(s.encode())

Para verificar, pase la cadena de prueba:

str1 = "♥O◘♦♥O◘♦"
str2 = "Python"

print(isascii(str1)) -> will return False
print(isascii(str2)) -> will return True
lejos
fuente
77
Este es un pequeño y agradable truco para detectar caracteres no ascii en cadenas Unicode, que en python3 son prácticamente todas las cadenas. Dado que los caracteres ascii pueden codificarse utilizando solo 1 byte, la longitud de los caracteres ascii será fiel a su tamaño después de codificarse en bytes; mientras que otros caracteres no ASCII se codificarán en 2 bytes o 3 bytes en consecuencia, lo que aumentará sus tamaños.
Devy
Por @far la mejor respuesta, pero no es que algunos caracteres como ... y - pueden parecer ascii, así que en caso de que quiera usar esto para detectar texto en inglés, haga que reemplace dichos caracteres antes de verificar
Christophe Roussy
1
Pero en Python2 arrojará un UnicodeEncodeError. Tengo que encontrar una solución para Py2 y Py3
alvas
2
Para aquellos que no están familiarizados con el uso de lambda (como lo estaba cuando encontré esta respuesta por primera vez), isasciiahora es una función que pase una cadena: isascii('somestring')== Truey isascii('àéç')==False
rabidang3ls el
8
Esto es simplemente un desperdicio. Codifica una cadena en UTF-8, creando una cadena de bytes completamente diferente. La verdadera forma de Python 3 es try: s.encode('ascii'); return True except UnicodeEncodeError: return False(como arriba, pero codificación, ya que las cadenas son Unicode en Python 3). Esta respuesta también genera un error en Python 3 cuando tiene sustitutos (por ejemplo, isascii('\uD800')genera un error en lugar de regresar False)
Artyer
73

Nuevo en Python 3.7 ( bpo32677 )

No más verificaciones ascii aburridas / ineficientes en las cadenas, nuevo método / str/ incorporado : verificará si las cadenas son ascii.bytesbytearray.isascii()

print("is this ascii?".isascii())
# True
abccd
fuente
¡Este merece estar en la cima!
Salek
"\x03".isascii()También es cierto. La documentación dice que esto solo verifica que todos los caracteres estén por debajo del punto de código 128 (0-127). Si también quiere evitar los caracteres de control, se necesita: text.isascii() and text.isprintable(). Solo usarlo isprintablepor sí solo tampoco es suficiente, ya que considerará que un carácter como ¿se puede imprimir (correctamente), pero no está dentro de la sección de impresión ASCII, por lo que debe verificar ambos si desea ambos. Otro problema: los espacios se consideran imprimibles, las pestañas y las nuevas líneas no.
Luc
19

Me encontré con algo como esto recientemente, para referencia futura

import chardet

encoding = chardet.detect(string)
if encoding['encoding'] == 'ascii':
    print 'string is in ascii'

que podrías usar con:

string_ascii = string.decode(encoding['encoding']).encode('ascii')
Alvin
fuente
77
Por supuesto, esto requiere la biblioteca de chardet .
StackExchange entristece al dancek el
1
sí, aunque chardet está disponible por defecto en la mayoría de las instalaciones
Alvin
77
Chardet solo adivina la codificación con una cierta probabilidad como esta: {'confidence': 0.99, 'encoding': 'EUC-JP'}(que en este caso estaba completamente equivocado)
Suzana
19

Vincent Marchetti tiene la idea correcta, pero str.decodeha quedado en desuso en Python 3. En Python 3 puede hacer la misma prueba con str.encode:

try:
    mystring.encode('ascii')
except UnicodeEncodeError:
    pass  # string is not ascii
else:
    pass  # string is ascii

Tenga en cuenta que la excepción que desea capturar también ha cambiado de UnicodeDecodeErrora UnicodeEncodeError.

drs
fuente
La entrada de OP es una cadena de bytes ( bytesescriba Python 3 que no tiene .encode()método). .decode()en @Vincent Marchetti la respuesta es correcta .
jfs
@JFSebastian El OP pregunta "¿Cómo verificar si una cadena en Python está en ASCII?" y no especifica bytes vs cadenas unicode. ¿Por qué dice que su entrada es una cadena de bytes?
drs
1
mira la fecha de la pregunta: 'é'era una cadena de bytes en ese momento.
jfs
1
@JFSebastian, está bien, teniendo en cuenta que esta respuesta responde a esta pregunta como si se hiciera hoy, creo que sigue siendo válida y útil. Cada vez menos personas vendrán aquí en busca de respuestas como si estuvieran ejecutando Python en 2008
drs
2
Encontré esta pregunta cuando estaba buscando una solución para python3 y leer rápidamente la pregunta no me hizo sospechar que se trataba de python 2 specfic. Pero esta respuesta fue realmente útil: ¡votación positiva!
josch
17

Tu pregunta es incorrecta; el error que ves no es el resultado de cómo construiste python, sino de una confusión entre cadenas de bytes y cadenas unicode.

Las cadenas de bytes (por ejemplo, "foo", o 'bar', en sintaxis de python) son secuencias de octetos; números del 0-255. Las cadenas Unicode (por ejemplo, u "foo" o u'bar ') son secuencias de puntos de código unicode; números del 0-1112064. Pero parece estar interesado en el carácter é, que (en su terminal) es una secuencia de varios bytes que representa un solo carácter.

En lugar de ord(u'é'), intente esto:

>>> [ord(x) for x in u'é']

Eso te dice qué secuencia de puntos de código representa "é". Puede darle [233], o puede darle [101, 770].

En lugar de chr()revertir esto, hay unichr():

>>> unichr(233)
u'\xe9'

Este personaje puede representarse en realidad como "puntos de código" unicode simples o múltiples, que a su vez representan grafemas o caracteres. Es "e con un acento agudo (es decir, punto de código 233)" o "e" (punto de código 101), seguido de "un acento agudo en el carácter anterior" (punto de código 770). Por lo que este mismo carácter exacto puede ser presentada como la estructura de datos de Python u'e\u0301'o u'\u00e9'.

La mayoría de las veces no debería preocuparse por esto, pero puede convertirse en un problema si está iterando sobre una cadena Unicode, ya que la iteración funciona por punto de código, no por caracteres descomponibles. En otras palabras, len(u'e\u0301') == 2y len(u'\u00e9') == 1. Si esto es importante para usted, puede convertir entre formas compuestas y descompuestas utilizando unicodedata.normalize.

El Glosario Unicode puede ser una guía útil para comprender algunos de estos problemas, al señalar cómo cada término específico se refiere a una parte diferente de la representación del texto, que es mucho más complicado de lo que muchos programadores se dan cuenta.

Glifo
fuente
3
'é' no representa necesariamente un único punto de código. Pueden ser dos puntos de código (U + 0065 + U + 0301).
jfs el
2
Cada carácter abstracto siempre está representado por un único punto de código. Sin embargo, los puntos de código pueden codificarse en múltiples bytes, dependiendo del esquema de codificación. es decir, 'é' es dos bytes en UTF-8 y UTF-16, y cuatro bytes en UTF-32, pero en cada caso sigue siendo un único punto de código: U + 00E9.
Ben Blank
55
En blanco @Ben: U + 0065 y U + 0301 son puntos de código y se hacen representar 'E', que puede también ser representado por U + 00E9. Google "combina acento agudo".
jfs el
JF tiene razón al combinar U + 0065 y U + 0301 para formar 'é', pero este no es un functino reversible. Obtendrá U + 00E9. De acuerdo con Wikipedia , estos puntos de código compuesto son útiles para la compatibilidad con versiones anteriores
Martin Konecny
1
@teehoo: es una función reversible en el sentido de que puede volver a normalizar el punto de código que representa el carácter compuesto en una secuencia de puntos de código que representan el mismo carácter compuesto. En Python puede hacer esto así: unicodedata.normalize ('NFD', u '\ xe9').
Glifo
10

¿Qué tal hacer esto?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True
miya
fuente
55
Esto falla si su cadena contiene caracteres ASCII que no son letras. Para sus ejemplos de código, eso incluye nueva línea, espacio, punto, coma, guión bajo y paréntesis.
florisla
9

Encontré esta pregunta al intentar determinar cómo usar / codificar / decodificar una cadena de cuya codificación no estaba seguro (y cómo escapar / convertir caracteres especiales en esa cadena).

Mi primer paso debería haber sido verificar el tipo de la cadena: no me di cuenta de que podía obtener buenos datos sobre su formato de tipo (s). Esta respuesta fue muy útil y llegó a la raíz real de mis problemas.

Si te estás volviendo grosero y persistente

UnicodeDecodeError: el códec 'ascii' no puede decodificar el byte 0xc3 en la posición 263: el ordinal no está en el rango (128)

particularmente cuando está CODIFICANDO, asegúrese de que no está tratando de unicode () una cadena que ya ES unicode- por alguna razón terrible, obtiene errores de códec ASCII. (Consulte también la receta de Python Kitchen y los tutoriales de documentación de Python para comprender mejor lo terrible que puede ser).

Finalmente, determiné que lo que quería hacer era esto:

escaped_string = unicode(original_string.encode('ascii','xmlcharrefreplace'))

También fue útil para depurar la configuración de la codificación predeterminada en mi archivo a utf-8 (ponga esto al comienzo de su archivo python):

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

Eso le permite probar caracteres especiales ('àéç') sin tener que usar sus escapes unicode (u '\ xe0 \ xe9 \ xe7').

>>> specials='àéç'
>>> specials.decode('latin-1').encode('ascii','xmlcharrefreplace')
'&#224;&#233;&#231;'
Max P Magee
fuente
2

Puede usar la biblioteca de expresiones regulares que acepta la definición estándar [[: ASCII:]] de Posix.

Steve Moyer
fuente
2

Una picadura ( strtipo) en Python es una serie de bytes. No hay forma de saber con solo mirar la cadena si esta serie de bytes representa una cadena ascii, una cadena en un juego de caracteres de 8 bits como ISO-8859-1 o una cadena codificada con UTF-8 o UTF-16 o lo que sea .

Sin embargo, si conoce la codificación utilizada, puede decodeconvertir la cadena en una cadena unicode y luego usar una expresión regular (o un bucle) para verificar si contiene caracteres fuera del rango que le preocupa.

JacquesB
fuente
1

Me gusta la respuesta de @ RogerDahl, pero es más eficiente hacer un cortocircuito al negar la clase de caracteres y usar la búsqueda en lugar de find_allo match.

>>> import re
>>> re.search('[^\x00-\x7F]', 'Did you catch that \x00?') is not None
False
>>> re.search('[^\x00-\x7F]', 'Did you catch that \xFF?') is not None
True

Me imagino que una expresión regular está bien optimizada para esto.

placas
fuente
0
import re

def is_ascii(s):
    return bool(re.match(r'[\x00-\x7F]+$', s))

Para incluir una cadena vacía como ASCII, cambie el +a *.

Roger Dahl
fuente
-1

Para evitar que su código se bloquee, es posible que desee utilizar un try-exceptpara atraparTypeErrors

>>> ord("¶")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 2 found

Por ejemplo

def is_ascii(s):
    try:
        return all(ord(c) < 128 for c in s)
    except TypeError:
        return False

fuente
Este tryenvoltorio es completamente inútil. Si "¶"es una cadena Unicode, entonces ord("¶")funcionará, y si no lo es (Python 2), la for c in sdescompondrá en bytes para ordque continúe funcionando.
Ry-
-5

Utilizo lo siguiente para determinar si la cadena es ascii o unicode:

>> print 'test string'.__class__.__name__
str
>>> print u'test string'.__class__.__name__
unicode
>>> 

Luego, use un bloque condicional para definir la función:

def is_ascii(input):
    if input.__class__.__name__ == "str":
        return True
    return False
mvknowles
fuente
44
-1 AARRGGHH esto trata a todos los caracteres con ord (c) en el rango (128, 256) como ASCII !!!
John Machin
No funciona Trate de llamar a la siguiente: is_ascii(u'i am ascii'). Aunque las letras y los espacios son definitivamente ASCII, esto todavía regresa Falseporque forzamos la cadena a ser unicode.
jpmc26