Wie kann der Python-Interpreter Nicht-ASCII-Zeichen in Zeichenfolgenoperationen korrekt verarbeiten?

104

Ich habe eine Zeichenfolge, die so aussieht:

6 918 417 712

Die eindeutige Methode zum Trimmen dieser Zeichenfolge (wie ich Python verstehe) besteht einfach darin, zu sagen, dass sich die Zeichenfolge in einer Variablen namens sbefindet. Wir erhalten:

s.replace('Â ', '')

Das sollte den Trick machen. Aber natürlich beschwert es sich, dass das Nicht-ASCII-Zeichen'\xc2' in der Datei blabla.py nicht codiert ist.

Ich konnte nie ganz verstehen, wie man zwischen verschiedenen Codierungen wechselt.

Hier ist der Code, er ist wirklich genauso wie oben, aber jetzt ist er im Kontext. Die Datei wird als UTF-8 im Editor gespeichert und hat den folgenden Header:

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

Der Code:

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)

Es geht nicht weiter als s.replace...

adergaard
quelle
1
Versuchte alle 4 Antworten bisher. No Go. Immer noch der UnicodeDecodeError: 'ascii'-Codec kann Byte 0xc2 in Position 1 nicht dekodieren: Ordnungszahl nicht im Bereich (128)
adergaard
Ihrer Unicode-Zeichenfolge mussu
SilentGhost
@ SilentGhost: Wie Sie sehen, gibt es keine Möglichkeit, sicher zu sein, dass es sich um eine Unicode-Zeichenfolge handelt. Ich erhalte eine Zeichenfolge mit dem oben gezeigten Inhalt, die jedoch keine ASCII-Zeichenfolgen enthält. Das ist das eigentliche Problem. Ich vermute, es ist Unicode, da es nicht in den ersten 128 ist.
Adergaard
Der Fehler hat nichts mit eingehender Zeichenfolge zu tun. Es ist eine Zeichenfolge in Ihrem Code, die diesen Fehler auslöst!
SilentGhost
2
Ich wette, aus diesem Grund ist Python 3 so streng in Bezug auf den Unterschied zwischen Zeichenfolgen und Byte-Sequenzen, nur um diese Art von Verwirrung zu vermeiden.
Mark Ransom

Antworten:

84

Python 2 verwendet asciials Standardcodierung für Quelldateien. Dies bedeutet, dass Sie oben in der Datei eine andere Codierung angeben müssen, um Nicht-ASCII-Unicode-Zeichen in Literalen zu verwenden. Python 3 verwendetutf-8 als Standardcodierung für Quelldateien, sodass dies weniger problematisch ist.

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

Um die utf-8-Quellcodierung zu aktivieren, wird dies in einer der beiden oberen Zeilen angegeben:

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

Das Obige ist in den Dokumenten, aber das funktioniert auch:

# coding: utf-8

Weitere Überlegungen:

  • Die Quelldatei muss auch in Ihrem Texteditor mit der richtigen Codierung gespeichert werden.

  • In Python 2 muss dem Unicode-Literal ein uvorangestellt sein, wie s.replace(u"Â ", u"")in Python 3. Verwenden Sie jedoch nur Anführungszeichen. In Python 2 können Sie from __future__ import unicode_literalsdas Python 3-Verhalten abrufen. Beachten Sie jedoch, dass dies das gesamte aktuelle Modul betrifft.

  • s.replace(u"Â ", u"")schlägt auch fehl, wenn ses sich nicht um eine Unicode-Zeichenfolge handelt.

  • string.replace Gibt eine neue Zeichenfolge zurück und wird nicht an Ort und Stelle bearbeitet. Stellen Sie daher sicher, dass Sie auch den Rückgabewert verwenden

Jason S.
quelle
4
Sie brauchen eigentlich nur # coding: utf-8. -*-ist nicht für die Dekoration, aber Sie werden es wahrscheinlich nie brauchen. Ich denke, es war für alte Muscheln da.
Fmalina
157
def removeNonAscii(s): return "".join(filter(lambda x: ord(x)<128, s))

edit: Mein erster Impuls ist immer, einen Filter zu verwenden, aber der Generatorausdruck ist speichereffizienter (und kürzer) ...

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

Beachten Sie, dass dies garantiert mit der UTF-8-Codierung funktioniert (da für alle Bytes in Mehrbyte-Zeichen das höchste Bit auf 1 gesetzt ist).

fortran
quelle
1
Ich bekomme: TypeError: ord () erwartete ein Zeichen, aber Zeichenfolge der Länge 2 gefunden
Ivelin
@Ivelin, weil das "Zeichen" nicht als richtiger Unicode interpretiert wird ... Überprüfen Sie, ob Ihrer Quellzeichenfolge uein Literal vorangestellt ist .
Fortan
35
>>> unicode_string = u"hello aåbäcö"
>>> unicode_string.encode("ascii", "ignore")
'hello abc'
Truppo
quelle
4
Ich sehe die Stimmen, die Sie bekommen, aber wenn ich es versuche, heißt es: Nein. UnicodeDecodeError: Der Codec 'ascii' kann das Byte 0xc2 an Position 1 nicht dekodieren: Ordnungszahl nicht im Bereich (128). Könnte es sein, dass meine ursprüngliche Zeichenfolge nicht im Unicode ist? Na auf jeden Fall. es braucht
adergaard
2
Nett, danke. Darf ich vorschlagen, .decode () für das Ergebnis zu verwenden, um es in der ursprünglichen Codierung zu erhalten?
AkiRoss
Wenn Sie UnicodeDecodeError: 'ascii' erhalten, versuchen Sie, die Zeichenfolge in das UTF-8-Format zu konvertieren, bevor Sie die Codierungsfunktion anwenden.
Sateesh
16

Der folgende Code ersetzt alle Nicht-ASCII-Zeichen durch Fragezeichen.

"".join([x if ord(x) < 128 else '?' for x in s])
Vision
quelle
Aus Neugier wollte ich wissen: Gibt es einen bestimmten Grund, es durch das Fragezeichen zu ersetzen?
Mohsin
6

Regex verwenden:

import re

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

Viel zu spät für eine Antwort, aber die ursprüngliche Zeichenfolge war in UTF-8 und '\ xc2 \ xa0' ist UTF-8 für NO-BREAK SPACE. Dekodieren Sie einfach die ursprüngliche Zeichenfolge alss.decode('utf-8') (\ xa0 wird bei falscher Dekodierung als Windows-1252 oder Latin-1 als Leerzeichen angezeigt:

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

Ausgabe

6 918 417 712
6 918 417 712
6_918_417_712
6-918-417-712
Mark Tolonen
quelle
3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

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

Dies wird ausgedruckt 6 918 417 712

Jesaja
quelle
Nee. UnicodeDecodeError: Der Codec 'ascii' kann das Byte 0xc2 an Position 1 nicht dekodieren: Ordnungszahl nicht im Bereich (128). Könnte es sein, dass meine ursprüngliche Zeichenfolge nicht im Unicode ist? Na auf jeden Fall. Ich mache wahrscheinlich etwas falsch.
Adergaard
@adergaard, hast du # - - Kodierung: utf-8 - - oben in der Quelldatei hinzugefügt?
Nadia Alramli
Ja, siehe oben auf dieser Seite. Ich habe das Questoin bearbeitet und den Code und die Header-Kommentare eingegeben. Danke für deine Hilfe.
Adergaard
Ich denke, Sie müssen herausfinden, wie Sie die Zeichenfolgen aus dem HTML- oder XML-Dokument in Unicode erhalten. Mehr Infos dazu hier: diveintopython.org/xml_processing/unicode.html
Jesaja
2

Ich weiß, dass es ein alter Thread ist, aber ich fühlte mich gezwungen, die Übersetzungsmethode zu erwähnen, die immer eine gute Möglichkeit ist, alle Zeichencodes über 128 (oder andere, falls erforderlich) zu ersetzen.

Verwendung : str. übersetzen ( Tabelle [, Löschzeichen] )

>>> 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'

Ab Python 2.6 können Sie die Tabelle auch auf Keine setzen und mit deletechars die nicht gewünschten Zeichen löschen, wie in den Beispielen in den Standarddokumenten unter http://docs.python.org/library/stdtypes gezeigt. html .

Bei Unicode-Zeichenfolgen ist die Übersetzungstabelle keine Zeichenfolge mit 256 Zeichen, sondern ein Diktat mit der Ord () der relevanten Zeichen als Schlüssel. Trotzdem ist es einfach genug, eine richtige ASCII-Zeichenfolge aus einer Unicode-Zeichenfolge zu erhalten, indem die oben von truppo erwähnte Methode verwendet wird: unicode_string.encode ("ascii", "ignore")

Zusammenfassend raise Exception, ascii_messagekönnen Sie die folgende Funktion verwenden , wenn Sie aus irgendeinem Grund unbedingt eine ASCII-Zeichenfolge benötigen (z. B. wenn Sie eine Standardausnahme mit auslösen ):

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)

Das Gute an translate ist, dass Sie Zeichen mit Akzent tatsächlich in relevante ASCII-Zeichen ohne Akzent konvertieren können, anstatt sie einfach zu löschen oder durch '?' Zu ersetzen. Dies ist häufig nützlich, beispielsweise für Indizierungszwecke.

Louis LC
quelle
Ich bekomme: TypeError: Zeichenzuordnung muss Ganzzahl, Keine oder Unicode zurückgeben
Ivelin
1
s.replace(u'Â ', '')              # u before string is important

und machen Sie Ihre .pyDatei Unicode.

SilentGhost
quelle
1

Dies ist ein schmutziger Hack, kann aber funktionieren.

s2 = ""
for i in s:
    if ord(i) < 128:
        s2 += i
Corey D.
quelle
0

Für das, was es wert war, war mein Zeichensatz utf-8und ich hatte die klassische " # -*- coding: utf-8 -*-" Linie eingefügt.

Ich stellte jedoch fest, dass ich beim Lesen dieser Daten von einer Webseite keine Universal Newlines hatte.

Mein Text hatte zwei Wörter, die durch " \r\n" getrennt waren. Ich habe mich nur auf die geteilt \nund die ersetzt "\n".

Als ich mich durchschleifte und den fraglichen Zeichensatz sah, erkannte ich den Fehler.

Es könnte sich also auch innerhalb des ASCII- Zeichensatzes befinden, aber ein Zeichen, das Sie nicht erwartet haben.

Tal
quelle