Wie überprüfe ich, ob eine Zeichenfolge in Python in ASCII enthalten ist?

211

Ich möchte überprüfen, ob eine Zeichenfolge in ASCII vorliegt oder nicht.

Ich bin mir ord()jedoch bewusst , wenn ich es versuche ord('é'), habe ich TypeError: ord() expected a character, but string of length 2 found. Ich habe verstanden, dass dies durch die Art und Weise verursacht wird, wie ich Python erstellt habe (wie in ord()der Dokumentation erläutert ).

Gibt es eine andere Möglichkeit zu überprüfen?

Nico
quelle
Die Zeichenfolgencodierung unterscheidet sich erheblich zwischen Python 2 und Python 3, daher ist es gut zu wissen, auf welche Version Sie abzielen.
Florida

Antworten:

188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)
Alexander Kojevnikov
quelle
95
Sinnlos ineffizient. Es ist viel besser, s.decode ('ascii') zu versuchen und UnicodeDecodeError abzufangen, wie von Vincent Marchetti vorgeschlagen.
ddaa
20
Es ist nicht ineffizient. all () wird kurzschließen und False zurückgeben, sobald ein ungültiges Byte auftritt.
John Millikin
10
Ineffizient oder nicht, die pythonischere Methode ist das Ausprobieren / Ausnehmen.
Jeremy Cantrell
43
Es ist ineffizient im Vergleich zum Versuch / Ausnahme. Hier befindet sich die Schleife im Interpreter. Mit dem Try / Except-Formular befindet sich die Schleife in der C-Codec-Implementierung, die von str.decode ('ascii') aufgerufen wird. Und ich stimme zu, die Try / Except-Form ist auch pythonischer.
ddaa
25
@ JohnMachin ord(c) < 128ist unendlich lesbarer und intuitiver alsc <= "\x7F"
Slater Victoroff
252

Ich denke, Sie stellen nicht die richtige Frage ...

Eine Zeichenfolge in Python hat keine Eigenschaft, die 'ascii', utf-8 oder einer anderen Codierung entspricht. Die Quelle Ihrer Zeichenfolge (unabhängig davon, ob Sie sie aus einer Datei lesen, über eine Tastatur eingeben usw.) hat möglicherweise eine Unicode-Zeichenfolge in ASCII codiert, um Ihre Zeichenfolge zu erstellen. Hier müssen Sie jedoch eine Antwort finden.

Vielleicht können Sie die Frage stellen: "Ist diese Zeichenfolge das Ergebnis der Codierung einer Unicode-Zeichenfolge in ASCII?" - Dies können Sie beantworten, indem Sie versuchen:

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
quelle
28
Verwenden Sie Codierung ist besser, weil String keine Decodierungsmethode in Python 3, sehen, was ist der Unterschied zwischen Codierung / Decodierung? (Python 2.x)
Jet Guo
@Sri: Das liegt daran, dass Sie es für eine nicht codierte Zeichenfolge verwenden ( strin Python 2, bytesin Python 3).
Dotancohen
In Python 2 funktioniert diese Lösung nur für eine Unicode- Zeichenfolge. A strin jeder ISO-Codierung müsste zuerst in Unicode codiert werden. Die Antwort sollte darauf eingehen.
Alexis
@JetGuo: Sie sollten beide abhängig vom Eingabetyp verwenden: s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')in Python 3. Die Eingabe von OP ist ein Bytestring 'é'(Python 2-Syntax, Python 3 wurde zu diesem Zeitpunkt noch nicht veröffentlicht) und ist daher .decode()korrekt.
JFS
2
@alexis: falsch. strauf Python 2 ist ein Bytestring. Es ist richtig zu verwenden .decode('ascii'), um herauszufinden, ob alle Bytes im ASCII-Bereich liegen.
JFS
153

Python 3 Weg:

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

Übergeben Sie zur Überprüfung die Testzeichenfolge:

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

print(isascii(str1)) -> will return False
print(isascii(str2)) -> will return True
weit
quelle
7
Dies ist ein netter kleiner Trick, um Nicht-ASCII-Zeichen in Unicode-Zeichenfolgen zu erkennen, die in Python3 so ziemlich alle Zeichenfolgen sind. Da ASCII-Zeichen mit nur 1 Byte codiert werden können, entspricht jede Länge von ASCII-Zeichen ihrer Größe, nachdem sie in Bytes codiert wurde. Andere Nicht-ASCII-Zeichen werden entsprechend in 2 Bytes oder 3 Bytes codiert, wodurch sich ihre Größe erhöht.
Devy
Mit @far die beste Antwort, aber nicht, dass einige Zeichen wie ... und - wie ASCII aussehen könnten. Wenn Sie dies verwenden möchten, um englischen Text zu erkennen, müssen Sie diese Zeichen ersetzen, bevor Sie sie überprüfen
Christophe Roussy
1
In Python2 wird jedoch ein UnicodeEncodeError ausgelöst. Ich muss eine Lösung für Py2 und Py3 finden
alvas
2
Für diejenigen, die mit der Verwendung von Lambda nicht vertraut sind (wie ich es war, als ich zum ersten Mal auf diese Antwort stieß), isasciiist jetzt eine Funktion, bei der Sie eine Zeichenfolge übergeben: isascii('somestring')== Trueund isascii('àéç')==False
rabidang3ls
8
Das ist einfach nur verschwenderisch. Es codiert eine Zeichenfolge in UTF-8 und erstellt einen ganz anderen Bytestring. Echte Python 3-Methode ist try: s.encode('ascii'); return True except UnicodeEncodeError: return False(wie oben, aber Codierung, da Zeichenfolgen in Python 3 Unicode sind). Diese Antwort löst auch einen Fehler in Python 3 aus, wenn Sie isascii('\uD800')False
Ersatz
71

Neu in Python 3.7 ( bpo32677 )

Keine lästig / ineffizient ascii Kontrollen auf Strings, neuen Einbau in str/ bytes/ bytearrayMethode - .isascii()wird überprüfen , ob die Saiten sind ascii.

print("is this ascii?".isascii())
# True
abccd
quelle
Dieser verdient es, an der Spitze zu sein!
Salek
"\x03".isascii()ist auch wahr. In der Dokumentation wird lediglich überprüft, ob alle Zeichen unter dem Codepunkt 128 (0-127) liegen. Wenn Sie auch Steuerzeichen vermeiden möchten, benötigen Sie : text.isascii() and text.isprintable(). Die Verwendung isprintableallein reicht ebenfalls nicht aus, da ein Zeichen wie ¿als (korrekt) druckbar angesehen wird, sich jedoch nicht im druckbaren Bereich von ASCII befindet. Sie müssen also beide überprüfen, wenn Sie beide möchten. Noch ein Problem: Leerzeichen gelten als druckbar, Tabulatoren und Zeilenumbrüche nicht.
Luc
19

Bin kürzlich auf so etwas gestoßen - zum späteren Nachschlagen

import chardet

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

die Sie verwenden könnten mit:

string_ascii = string.decode(encoding['encoding']).encode('ascii')
Alvin
quelle
7
Dies erfordert natürlich die Chardet- Bibliothek.
StackExchange Saddens Dancek 30.
1
Ja, obwohl Chardet in den meisten Installationen standardmäßig verfügbar ist
Alvin
7
Chardet errät die Codierung nur mit einer bestimmten Wahrscheinlichkeit wie folgt : {'confidence': 0.99, 'encoding': 'EUC-JP'}(was in diesem Fall völlig falsch war)
Suzana
19

Vincent Marchetti hat die richtige Idee, wurde jedoch str.decodein Python 3 nicht mehr unterstützt. In Python 3 können Sie denselben Test durchführen mit str.encode:

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

Beachten Sie, dass sich die Ausnahme, die Sie abfangen möchten, ebenfalls von UnicodeDecodeErrorauf geändert hat UnicodeEncodeError.

drs
quelle
Die Eingabe von OP ist ein Bytestring (geben Sie bytesPython 3 ohne .encode()Methode ein). .decode()in @Vincent Marchettis Antwort ist richtig .
JFS
@JFSebastian Das OP fragt "Wie kann überprüft werden, ob eine Zeichenfolge in Python in ASCII enthalten ist?" und gibt keine Bytes gegenüber Unicode-Zeichenfolgen an. Warum sagen Sie, dass seine / ihre Eingabe ein Bytestring ist?
Dr.
1
Schauen Sie sich das Datum der Frage an: 'é'war zu der Zeit ein Test.
JFS
1
@JFSebastian, ok, wenn man bedenkt, dass diese Antwort diese Frage beantwortet, als ob sie heute gestellt worden wäre, denke ich, dass sie immer noch gültig und hilfreich ist. Immer weniger Leute werden hierher kommen, um nach Antworten zu suchen, als würden sie 2008 Python
ausführen
2
Ich fand diese Frage, als ich nach einer Lösung für Python3 suchte und das schnelle Lesen der Frage ließ mich nicht vermuten, dass dies Python 2-spezifisch war. Aber diese Antwort war wirklich hilfreich - Upvoting!
Josch
17

Ihre Frage ist falsch; Der Fehler, den Sie sehen, ist nicht das Ergebnis der Erstellung von Python, sondern eine Verwechslung zwischen Byte-Strings und Unicode-Strings.

Byte-Strings (z. B. "foo" oder "bar" in der Python-Syntax) sind Sequenzen von Oktetten. Zahlen von 0-255. Unicode-Zeichenfolgen (z. B. u "foo" oder u'bar ') sind Sequenzen von Unicode-Codepunkten. Zahlen von 0-1112064. Sie scheinen jedoch an dem Zeichen é interessiert zu sein, das (in Ihrem Terminal) eine Mehrbyte-Sequenz ist, die ein einzelnes Zeichen darstellt.

ord(u'é')Versuchen Sie stattdessen Folgendes:

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

Das sagt Ihnen, welche Folge von Codepunkten "é" darstellt. Es kann Ihnen [233] geben, oder es kann Ihnen [101, 770] geben.

Anstatt chr()dies umzukehren, gibt es unichr():

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

Dieses Zeichen kann tatsächlich entweder ein einzelner oder mehrere Unicode- "Codepunkte" sein, die selbst entweder Grapheme oder Zeichen darstellen. Es ist entweder "e mit einem akuten Akzent (dh Codepunkt 233)" oder "e" (Codepunkt 101), gefolgt von "einem akuten Akzent auf dem vorherigen Zeichen" (Codepunkt 770). So kann genau dieses Zeichen als Python-Datenstruktur u'e\u0301'oder dargestellt werden u'\u00e9'.

Die meiste Zeit sollten Sie sich nicht darum kümmern müssen, aber es kann zu einem Problem werden, wenn Sie über eine Unicode-Zeichenfolge iterieren, da die Iteration nach Codepunkt und nicht nach zerlegbaren Zeichen funktioniert. Mit anderen Worten len(u'e\u0301') == 2und len(u'\u00e9') == 1. Wenn dies für Sie wichtig ist, können Sie mithilfe von zwischen zusammengesetzten und zerlegten Formularen konvertieren unicodedata.normalize.

Das Unicode-Glossar kann eine hilfreiche Anleitung zum Verständnis einiger dieser Probleme sein, indem aufgezeigt wird, wie sich die einzelnen Begriffe auf einen anderen Teil der Textdarstellung beziehen, was weitaus komplizierter ist, als viele Programmierer erkennen.

Glyphe
quelle
3
'é' repräsentiert nicht unbedingt einen einzelnen Codepunkt. Es können zwei Codepunkte sein (U + 0065 + U + 0301).
JFS
2
Jedes abstrakte Zeichen wird immer durch einen einzelnen Codepunkt dargestellt. Codepunkte können jedoch abhängig vom Codierungsschema in mehrere Bytes codiert werden. Das heißt, 'é' ist zwei Bytes in UTF-8 und UTF-16 und vier Bytes in UTF-32, aber es ist jeweils immer noch ein einzelner Codepunkt - U + 00E9.
Ben Blank
5
@ Ben Blank: U + 0065 und U + 0301 sind Codepunkte und sie tun repräsentieren ‚é‘ , das kann auch durch U + 00E9 dargestellt werden. Google "Akutakzent kombinieren".
JFS
JF hat Recht, U + 0065 und U + 0301 zu 'é' zu kombinieren, aber dies ist keine reversible Funktion. Sie erhalten U + 00E9. Laut Wikipedia sind diese zusammengesetzten Codepunkte für die Abwärtskompatibilität nützlich
Martin Konecny
1
@teehoo - Dies ist eine umkehrbare Funktion in dem Sinne, dass Sie den Codepunkt, der das zusammengesetzte Zeichen darstellt, in eine Folge von Codepunkten normalisieren können, die dasselbe zusammengesetzte Zeichen darstellen. In Python können Sie dies folgendermaßen tun: unicodedata.normalize ('NFD', u '\ xe9').
Glyphe
10

Wie wäre es damit?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True
miya
quelle
5
Dies schlägt fehl, wenn Ihre Zeichenfolge ASCII-Zeichen enthält, die keine Buchstaben sind. Für Sie Codebeispiele, einschließlich Zeilenumbruch, Leerzeichen, Punkt, Komma, Unterstrich und Klammern.
Florida
9

Ich habe diese Frage gefunden, als ich versucht habe zu bestimmen, wie eine Zeichenfolge verwendet / codiert / decodiert wird, deren Codierung ich nicht sicher war (und wie Sonderzeichen in dieser Zeichenfolge maskiert / konvertiert werden).

Mein erster Schritt sollte darin bestehen, den Typ der Zeichenfolge zu überprüfen. Ich wusste dort nicht, dass ich von Typ (en) gute Daten über die Formatierung erhalten kann. Diese Antwort war sehr hilfreich und brachte meine Probleme auf den Punkt.

Wenn Sie unhöflich und hartnäckig werden

UnicodeDecodeError: Der Codec 'ascii' kann das Byte 0xc3 an Position 263 nicht dekodieren: Ordnungszahl nicht im Bereich (128)

Stellen Sie insbesondere beim ENCODIEREN sicher, dass Sie nicht versuchen, eine Zeichenfolge zu unicodeieren (), die bereits Unicode ist. Aus irgendeinem schrecklichen Grund treten ASCII-Codec-Fehler auf. (Siehe auch das Python Kitchen-Rezept und die Python-Dokumentations- Tutorials, um besser zu verstehen, wie schrecklich dies sein kann.)

Schließlich entschied ich, dass ich Folgendes tun wollte:

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

Ebenfalls hilfreich beim Debuggen war das Setzen der Standardcodierung in meiner Datei auf utf-8 (setzen Sie diese an den Anfang Ihrer Python-Datei):

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

Auf diese Weise können Sie Sonderzeichen ('àéç') testen, ohne ihre Unicode-Escapezeichen (u '\ xe0 \ xe9 \ xe7') verwenden zu müssen.

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

Um Alexanders Lösung aus Python 2.6 (und in Python 3.x) zu verbessern, können Sie das Hilfsmodul curses.ascii und die Funktion curses.ascii.isascii () oder verschiedene andere verwenden: https://docs.python.org/2.6/ library / curses.ascii.html

from curses import ascii

def isascii(s):
    return all(ascii.isascii(c) for c in s)
Sergey Nevmerzhitsky
quelle
2

Sie können die Bibliothek für reguläre Ausdrücke verwenden, die die Posix-Standarddefinition [[: ASCII:]] akzeptiert.

Steve Moyer
quelle
2

Ein Stich ( str-Typ) in Python besteht aus einer Reihe von Bytes. Es gibt keinen Weg anhand der Zeichenfolge zu erkennen, ob diese Reihe von Bytes eine ASCII-Zeichenfolge, eine Zeichenfolge in einem 8-Bit-Zeichensatz wie ISO-8859-1 oder eine mit UTF-8 oder UTF-16 oder was auch immer codierte Zeichenfolge darstellt .

Wenn Sie jedoch die verwendete Codierung kennen, können Sie decodeden str in einen Unicode-String umwandeln und dann mit einem regulären Ausdruck (oder einer Schleife) prüfen, ob er Zeichen außerhalb des Bereichs enthält, um den Sie sich Sorgen machen.

JacquesB
quelle
1

Wie die Antwort von @ RogerDahl, aber es ist effizienter, einen Kurzschluss zu machen, indem die Zeichenklasse negiert und die Suche anstelle von find_alloder verwendet wird 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

Ich stelle mir vor, dass ein regulärer Ausdruck dafür gut optimiert ist.

Kochfelder
quelle
0
import re

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

Um eine leere Zeichenfolge als ASCII einzuschließen, ändern Sie die +in *.

Roger Dahl
quelle
-1

Um zu verhindern, dass Ihr Code abstürzt, möchten Sie möglicherweise ein try-exceptzum Abfangen verwendenTypeErrors

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

Beispielsweise

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

quelle
Dieser tryWrapper ist völlig sinnlos. Wenn "¶"es sich um eine Unicode-Zeichenfolge handelt, ord("¶")funktioniert sie, und wenn dies nicht der Fall ist (Python 2), for c in swird sie in Bytes zerlegt, sodass ordsie weiterhin funktioniert.
Ry-
-5

Ich benutze das Folgende, um festzustellen, ob der String ASCII oder Unicode ist:

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

Verwenden Sie dann einfach einen bedingten Block, um die Funktion zu definieren:

def is_ascii(input):
    if input.__class__.__name__ == "str":
        return True
    return False
mvknowles
quelle
4
-1 AARRGGHH behandelt alle Zeichen mit ord (c) im Bereich (128, 256) als ASCII !!!
John Machin
Funktioniert nicht Versuchen Sie Folgendes anzurufen : is_ascii(u'i am ascii'). Obwohl die Buchstaben und Leerzeichen definitiv ASCII sind, kehrt dies immer noch zurück, Falseweil wir die Zeichenfolge dazu gezwungen haben unicode.
jpmc26