Come verificare se una stringa in Python è in ASCII?


212

Voglio verificare se una stringa è in ASCII o no.

Sono consapevole ord(), tuttavia, quando provo ord('é'), ho TypeError: ord() expected a character, but string of length 2 found. Ho capito che è causato dal modo in cui ho creato Python (come spiegato nella ord()documentazione di ).

C'è un altro modo per verificare?


La codifica delle stringhe differisce abbastanza tra Python 2 e Python 3, quindi sarebbe bene sapere quale versione stai prendendo di mira.
florisla,

Risposte:


188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)

95
Inutilmente inefficiente. Molto meglio provare s.decode ('ascii') e catturare UnicodeDecodeError, come suggerito da Vincent Marchetti.
dada

20
Non è inefficiente. all () metterà in corto circuito e restituirà False non appena incontra un byte non valido.
John Millikin,

10
Inefficiente o no, il metodo più pitonico è il tentativo / tranne.
Jeremy Cantrell,

43
È inefficiente rispetto al tentativo / tranne. Qui il loop è nell'interprete. Con il modulo try / tranne, il ciclo è nell'implementazione del codec C chiamato da str.decode ('ascii'). E sono d'accordo, anche la forma try / tranne è più pitonica.
dada

25
@JohnMachin ord(c) < 128è infinitamente più leggibile e intuitivo dic <= "\x7F"
Slater Victoroff il

253

Penso che non stai facendo la domanda giusta ...

Una stringa in Python non ha proprietà corrispondenti a 'ascii', utf-8 o qualsiasi altra codifica. La fonte della tua stringa (sia che tu la legga da un file, l'input da una tastiera, ecc.) Potrebbe aver codificato una stringa unicode in ascii per produrre la tua stringa, ma è lì che devi cercare una risposta.

Forse la domanda che puoi porre è: "Questa stringa è il risultato della codifica di una stringa unicode in ascii?" - A questo puoi rispondere provando:

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"

28
usare la codifica è meglio, perché stringa senza metodo di decodifica in Python 3, vedi qual è la differenza tra codifica / decodifica? (python 2.x)
Jet Guo

@Sri: Questo perché lo stai usando su una stringa non codificata ( strin Python 2, bytesin Python 3).
dotancohen,

In Python 2, questa soluzione funziona solo per una stringa unicode . A strin qualsiasi codifica ISO dovrebbe essere prima codificato in Unicode. La risposta dovrebbe andare in questo.
alexis,

@JetGuo: dovresti usarli entrambi a seconda del tipo di input: s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')in Python 3. L'ingresso di OP è un bytestring 'é'(la sintassi di Python 2, Python 3 non era stata rilasciata al momento) e quindi .decode()è corretta.
jfs,

2
@alexis: sbagliato. strsu Python 2 è un bytestring. È corretto utilizzare .decode('ascii')per scoprire se tutti i byte sono nell'intervallo ASCII.
jfs,

153

Python 3 vie:

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

Per verificare, passare la stringa di prova:

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

print(isascii(str1)) -> will return False
print(isascii(str2)) -> will return True

7
Questo è un piccolo trucco per rilevare i caratteri non ascii nelle stringhe Unicode, che in python3 è praticamente tutte le stringhe. Poiché i caratteri ASCII possono essere codificati usando solo 1 byte, quindi qualsiasi lunghezza dei caratteri ASCII sarà fedele alle sue dimensioni dopo essere stata codificata in byte; mentre altri caratteri non ascii verranno codificati in 2 byte o 3 byte di conseguenza, aumentandone le dimensioni.
Devy,

Con @far la risposta migliore, ma non che alcuni caratteri come ... e - possano sembrare ascii, quindi nel caso in cui tu voglia utilizzarlo per rilevare il testo in inglese ti fanno sostituire tali caratteri prima di controllare
Christophe Roussy,

1
Ma in Python2 genererà un UnicodeEncodeError. Devo trovare una soluzione per Py2 e Py3
alvas

2
Per coloro che non hanno familiarità con l'uso di lambda (come lo ero quando ho incontrato per la prima volta questa risposta) isasciiè ora una funzione che si passa a una stringa: isascii('somestring')== Truee isascii('àéç')==False
rabidang3ls

8
Questo è semplicemente uno spreco. Codifica una stringa in UTF-8, creando un altro bytestring. Il vero modo di Python 3 è try: s.encode('ascii'); return True except UnicodeEncodeError: return False(Come sopra, ma codifica, poiché le stringhe sono Unicode in Python 3). Questa risposta genera anche un errore in Python 3 quando hai surrogati (ad esempio isascii('\uD800')genera un errore invece di tornare False)
Artyer,

73

Nuovo in Python 3.7 ( bpo32677 )

Niente più controlli ascii noiosi / inefficienti sulle stringhe, nuovo metodo / str/ incorporato - controllerà se le stringhe sono ascii.bytesbytearray.isascii()

print("is this ascii?".isascii())
# True

Questo merita di essere al top!
Salek,

"\x03".isascii()è anche vero. La documentazione dice che questo controlla solo che tutti i caratteri sono al di sotto del punto di codice 128 (0-127). Se vuoi anche per evitare i caratteri di controllo, è necessario: text.isascii() and text.isprintable(). Anche il solo utilizzo isprintableda solo non è sufficiente, poiché considererà un carattere come ¿stampabile (correttamente), ma non è all'interno della sezione stampabile ASCII, quindi è necessario controllare entrambi se si desidera entrambi. Ancora un altro gotcha: gli spazi sono considerati stampabili, le schede e le nuove righe no.
Luc,

19

Mi sono imbattuto in qualcosa di simile di recente - per riferimento futuro

import chardet

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

che potresti usare con:

string_ascii = string.decode(encoding['encoding']).encode('ascii')

7
Naturalmente, questo richiede la libreria chardet .
StackExchange saddens dancek,

1
sì, sebbene chardet sia disponibile di default nella maggior parte delle installazioni
Alvin,

7
chardet indovina la codifica solo con una certa probabilità come questa: {'confidence': 0.99, 'encoding': 'EUC-JP'}(che in questo caso era completamente sbagliato)
Suzana

19

Vincent Marchetti ha l'idea giusta, ma str.decodeè stato deprecato in Python 3. In Python 3 puoi fare lo stesso test con str.encode:

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

Nota che anche l'eccezione che vuoi catturare è cambiata da UnicodeDecodeErrora UnicodeEncodeError.


L'input di OP è un bytestring ( bytesdigitare in Python 3 che non ha .encode()metodo). .decode()nella risposta di @Vincent Marchetti è corretta .
jfs,

@JFSebastian L'OP chiede "Come verificare se una stringa in Python è in ASCII?" e non specifica byte vs stringhe unicode. Perché dici che il suo contributo è una distrazione?
drs

1
guarda la data della domanda: 'é'era una distrazione al momento.
jfs,

1
@JFSebastian, ok, considerando bene questa risposta risponde a questa domanda come se fosse stata posta oggi, penso che sia ancora valida e utile. Sempre meno persone verranno qui in cerca di risposte come se stessero gestendo Python nel 2008
drs

2
Ho trovato questa domanda quando stavo cercando una soluzione per python3 e la lettura rapida della domanda non mi ha fatto sospettare che si trattasse di uno specifico python 2. Ma questa risposta è stata davvero utile: votazione!
Jos

17

La tua domanda non è corretta; l'errore che vedi non è il risultato di come hai creato Python, ma di una confusione tra stringhe di byte e stringhe unicode.

Le stringhe di byte (ad es. "Pippo", o "bar", nella sintassi di Python) sono sequenze di ottetti; numeri da 0 a 255. Le stringhe Unicode (ad es. U "foo" o u'bar ') sono sequenze di punti di codice unicode; numeri da 0-1112064. Ma sembra che tu sia interessato al carattere é, che (nel tuo terminale) è una sequenza multi-byte che rappresenta un singolo carattere.

Invece di ord(u'é'), prova questo:

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

Ciò indica quale sequenza di punti di codice "é" rappresenta. Potrebbe darti [233] o potrebbe darti [101, 770].

Invece di chr()invertire questo, c'è unichr():

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

Questo personaggio può effettivamente essere rappresentato da un singolo o multiplo "punti di codice" unicode, che rappresentano essi stessi grafi o caratteri. È "e con un accento acuto (cioè il punto di codice 233)" o "e" (punto di codice 101), seguito da "un accento acuto sul carattere precedente" (punto di codice 770). Quindi questo stesso identico personaggio può essere presentato come la struttura dati di Python u'e\u0301'o u'\u00e9'.

Il più delle volte non dovresti preoccuparti di questo, ma può diventare un problema se stai iterando su una stringa unicode, poiché l'iterazione funziona per punto di codice, non per carattere scomponibile. In altre parole, len(u'e\u0301') == 2e len(u'\u00e9') == 1. Se questo è importante per te, puoi convertire tra forme composte e decomposte utilizzando unicodedata.normalize.

Il Glossario Unicode può essere una guida utile per comprendere alcuni di questi problemi, indicando come ogni termine specifico si riferisce a una parte diversa della rappresentazione del testo, che è molto più complicata di quanto molti programmatori realizzino.


3
'é' non non rappresenta necessariamente un unico punto di codice. Potrebbe essere due punti di codice (U + 0065 + U + 0301).
jfs,

2
Ogni carattere astratto è sempre rappresentato da un singolo punto di codice. Tuttavia, i punti di codice possono essere codificati in più byte, a seconda dello schema di codifica. cioè, 'é' è due byte in UTF-8 e UTF-16 e quattro byte in UTF-32, ma in ogni caso è ancora un singolo punto di codice - U + 00E9.
Ben Blank

5
Vuoto @ Ben: U + 0065 e U + 0301 sono punti di codice e fanno rappresentare 'é' che può anche essere rappresentato da U + 00E9. Google "combinando accento acuto".
jfs,

JF ha ragione nel combinare U + 0065 e U + 0301 per formare 'é' ma questo non è un functino reversibile. Otterrai U + 00E9. Secondo Wikipedia , questi punti di codice composito sono utili per la retrocompatibilità
Martin Konecny ​​il

1
@teehoo - È una funzione reversibile nel senso che puoi ri-normalizzare il punto di codice che rappresenta il carattere composto in una sequenza di punti di codice che rappresenta lo stesso carattere composto. In Python puoi farlo in questo modo: unicodedata.normalize ('NFD', u '\ xe9').
Glifo

10

Che ne dici di fare questo?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True

5
Ciò non riesce se la stringa contiene caratteri ASCII che non sono lettere. Per te esempi di codice, che include newline, spazio, punto, virgola, trattino basso e parentesi.
florisla,

9

Ho trovato questa domanda mentre provavo a determinare come usare / codificare / decodificare una stringa di cui non ero sicuro della codifica (e come sfuggire / convertire caratteri speciali in quella stringa).

Il mio primo passo avrebbe dovuto essere quello di controllare il tipo di stringa: non mi ero reso conto che avrei potuto ottenere buoni dati sulla sua formattazione dai tipi. Questa risposta è stata molto utile e ha raggiunto la vera radice dei miei problemi.

Se stai diventando scortese e persistente

UnicodeDecodeError: il codec 'ascii' non può decodificare il byte 0xc3 in posizione 263: ordinale non compreso nell'intervallo (128)

in particolare quando stai ENCODING, assicurati di non provare a unicode () una stringa che è già unicode - per qualche motivo terribile, ricevi errori di codec ASCII. (Vedi anche la ricetta di Python Kitchen e i documenti di Python esercitazioni sui per una migliore comprensione di quanto possa essere terribile.)

Alla fine ho deciso che quello che volevo fare era questo:

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

Utile anche nel debug è stato impostare la codifica predefinita nel mio file su utf-8 (mettilo all'inizio del tuo file python):

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

Ciò ti consente di testare caratteri speciali ('àéç') senza dover usare le loro escape unicode (u '\ xe0 \ xe9 \ xe7').

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


2

È possibile utilizzare la libreria di espressioni regolari che accetta la definizione standard [[: ASCII:]] di Posix.


2

Una puntura ( strtipo) in Python è una serie di byte. Non c'è modo di dire semplicemente guardando la stringa se questa serie di byte rappresenta una stringa ASCII, una stringa in un set di caratteri a 8 bit come ISO-8859-1 o una stringa codificata con UTF-8 o UTF-16 o qualsiasi altra cosa .

Tuttavia, se si conosce la codifica utilizzata, è possibile inserire decodestr in una stringa unicode e quindi utilizzare un'espressione regolare (o un ciclo) per verificare se contiene caratteri al di fuori dell'intervallo di cui si è preoccupati.


1

Come la risposta di @ RogerDahl, ma è più efficiente cortocircuitare negando la classe di caratteri e usando la ricerca anziché 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

Immagino che un'espressione regolare sia ottimizzata per questo.


0
import re

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

Per includere una stringa vuota come ASCII, modificare +in *.


-1

Per evitare arresti anomali del codice, potresti voler usare a try-exceptper catturareTypeErrors

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

Per esempio

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

Questo tryinvolucro è completamente inutile. Se "¶"è una stringa Unicode, allora ord("¶")funzionerà e se non lo è (Python 2), for c in sla decomporrà in byte, quindi ordcontinuerà a funzionare.
Ry-

-5

Uso quanto segue per determinare se la stringa è ASCII o Unicode:

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

Quindi basta usare un blocco condizionale per definire la funzione:

def is_ascii(input):
    if input.__class__.__name__ == "str":
        return True
    return False

4
-1 AARRGGHH sta trattando tutti i personaggi con ord (c) nel range (128, 256) come ASCII !!!
John Machin,

Non funziona Prova a chiamare il seguente: is_ascii(u'i am ascii'). Anche se le lettere e gli spazi sono sicuramente ASCII, questo ritorna comunque Falseperché abbiamo forzato la stringa unicode.
jpmc26,
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.