it-swarm.com.de

Unicode (UTF-8) zum Lesen und Schreiben von Dateien in Python

Ich habe Gehirnprobleme beim Lesen und Schreiben von Text in eine Datei (Python 2.4).

# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

("u'Capit\xe1n", "'Capit\xc3\xa1n'")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capit\xc3\xa1n\n'

Also tippe ich Capit\xc3\xa1n in meinen Lieblingseditor in der Datei f2.

Dann:

>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'

Was verstehe ich hier nicht? Es ist klar, dass ich ein wichtiges Stück Magie (oder einen gesunden Menschenverstand) vermisse. Was gibt man in Textdateien ein, um korrekte Konvertierungen zu erhalten?

Was ich hier wirklich nicht zu befürchten vermag, ist der Sinn der UTF-8-Darstellung, wenn Sie Python nicht dazu bringen können, sie zu erkennen, wenn sie von außen kommt. Vielleicht sollte ich einfach den String von JSON ausgeben und stattdessen diesen verwenden, da dieser eine unschätzbare Darstellung hat! Genauer gesagt, gibt es eine ASCII -Darstellung dieses Unicode-Objekts, die Python erkennt und dekodiert, wenn sie aus einer Datei eingeht? Wenn ja, wie bekomme ich es?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
298
Gregg Lind

In der Notation

u'Capit\xe1n\n'

das "\ xe1" repräsentiert nur ein Byte. "\ x" sagt Ihnen, dass "e1" hexadezimal ist. Wenn du schreibst

Capit\xc3\xa1n

in Ihrer Datei befindet sich "\ xc3". Das sind 4 Bytes und in Ihrem Code lesen Sie sie alle. Sie können dies sehen, wenn Sie sie anzeigen:

>>> open('f2').read()
'Capit\\xc3\\xa1n\n'

Sie können sehen, dass der Backslash durch einen Backslash maskiert wird. Sie haben also vier Bytes in Ihrer Zeichenfolge: "\", "x", "c" und "3".

Bearbeiten:

Wie andere in ihren Antworten betonten, sollten Sie nur die Zeichen im Editor eingeben und Ihr Editor sollte dann die Konvertierung in UTF-8 vornehmen und speichern.

Wenn Sie tatsächlich eine Zeichenfolge in diesem Format haben, können Sie den Codec string_escape verwenden, um sie in eine normale Zeichenfolge zu decodieren:

In [15]: print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
Capitán

Das Ergebnis ist eine Zeichenfolge, die in UTF-8 codiert ist, wobei das Zeichen mit Akzent durch die zwei Bytes dargestellt wird, die in der ursprünglichen Zeichenfolge \\xc3\\xa1 geschrieben wurden. Wenn Sie einen Unicode-String haben möchten, müssen Sie ihn mit UTF-8 erneut dekodieren.

Zu Ihrer Bearbeitung: Sie haben kein UTF-8 in Ihrer Datei. Um tatsächlich zu sehen, wie es aussehen würde:

s = u'Capit\xe1n\n'
sutf8 = s.encode('UTF-8')
open('utf-8.out', 'w').write(sutf8)

Vergleichen Sie den Inhalt der Datei utf-8.out mit dem Inhalt der Datei, die Sie mit Ihrem Editor gespeichert haben.

104
unbeknown

Anstatt mit den Codierungs- und Decodierungsmethoden herumzuspielen, finde ich es einfacher, die Codierung beim Öffnen der Datei anzugeben. Das io -Modul (hinzugefügt in Python 2.6) bietet eine io.open -Funktion mit einem Codierungsparameter.

Verwenden Sie die Methode open aus dem Modul io.

>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")

Nach dem Aufruf der Funktion read () von f wird ein codiertes Unicode-Objekt zurückgegeben.

>>>f.read()
u'Capit\xe1l\n\n'

Beachten Sie, dass in Python 3 die Funktion io.open ein Alias ​​für die integrierte Funktion open ist. Die integrierte Open-Funktion unterstützt nur das Codierungsargument in Python 3, nicht in Python 2.

Bearbeiten: Zuvor empfahl diese Antwort das Codecs Modul. Das Codecs-Modul kann beim Mischen von read() und readline() zu Problemen führen. Daher empfiehlt diese Antwort jetzt das io Modul.

Verwenden Sie die offene Methode aus dem Codec-Modul.

>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")

Nach dem Aufruf der Funktion read () von f wird ein codiertes Unicode-Objekt zurückgegeben.

>>>f.read()
u'Capit\xe1l\n\n'

Wenn Sie die Codierung einer Datei kennen, ist die Verwendung des Codec-Pakets weniger verwirrend.

Siehe http://docs.python.org/library/codecs.html#codecs.open

666
Tim Swast

Jetzt brauchen Sie in Python3 nur noch open(Filename, 'r', encoding='utf-8')

[Am 10.02.2016 zur gewünschten Klärung bearbeiten]

Python3 hat den Kodierungsparameter zu seiner Open-Funktion hinzugefügt. Die folgenden Informationen zur Öffnungsfunktion finden Sie hier: https://docs.python.org/3/library/functions.html#open

open(file, mode='r', buffering=-1, 
      encoding=None, errors=None, newline=None, 
      closefd=True, opener=None)

Codierung ist der Name der Codierung, die zum Decodieren oder Codieren der Datei verwendet wird. Dies sollte nur im Textmodus verwendet werden. Die Standardkodierung ist plattformabhängig (unabhängig davon, was locale.getpreferredencoding () zurückgibt), es kann jedoch jede Textkodierung verwendet werden, die von Python unterstützt wird. Eine Liste der unterstützten Codierungen finden Sie im Modul Codecs .

Wenn Sie also encoding='utf-8' als Parameter zur Funktion open hinzufügen, erfolgt das Lesen und Schreiben der Datei als utf8 (dies ist jetzt auch die Standardcodierung für alles, was in Python ausgeführt wird).

32
Dakusan

Also habe ich eine Lösung für das gefunden, wonach ich suche:

print open('f2').read().decode('string-escape').decode("utf-8")

Es gibt einige ungewöhnliche Codecs, die hier nützlich sind. Diese spezielle Lesung ermöglicht es, UTF-8-Darstellungen aus Python zu übernehmen, sie in eine ASCII -Datei zu kopieren und sie in Unicode einlesen zu lassen. Bei der "String-Escape" -Dekodierung werden die Schrägstriche nicht verdoppelt.

Dies ermöglicht die Art von Rundreise, die ich mir vorgestellt habe.

17
Gregg Lind
# -*- encoding: utf-8 -*-

# converting a unknown formatting file in utf-8

import codecs
import commands

file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)

file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')

for l in file_stream:
    file_output.write(l)

file_stream.close()
file_output.close()
14
Ricardo

Tatsächlich funktionierte dies für mich zum Lesen einer Datei mit UTF-8-Codierung in Python 3.2:

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)
14
Sina

Um eine Unicode-Zeichenfolge einzulesen und dann an HTML zu senden, habe ich Folgendes ausgeführt:

fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')

Nützlich für python betriebene http-Server.

6
praj

mit Ausnahme von codecs.open() kann io.open() verwendet werden, um mit Python2 oder Python3 Unicode-Dateien zu lesen/schreiben

Beispiel

import io

text = u'á'
encoding = 'utf8'

with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout:
    fout.write(text)

with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin:
    text2 = fin.read()

assert text == text2
6
Ryan

Sie sind über das allgemeine Problem mit Codierungen gestolpert: Wie kann ich feststellen, in welcher Codierung sich eine Datei befindet?

Antwort: Sie können nicht , wenn nicht das Dateiformat dies vorsieht. XML beginnt beispielsweise mit:

<?xml encoding="utf-8"?>

Dieser Header wurde sorgfältig ausgewählt, damit er unabhängig von der Codierung gelesen werden kann. In Ihrem Fall gibt es keinen solchen Hinweis, daher haben weder Ihr Editor noch Python eine Ahnung, was los ist. Daher müssen Sie das Modul codecs und codecs.open(path,mode,encoding) verwenden, das das fehlende Bit in Python bereitstellt.

Was Ihren Editor betrifft, müssen Sie prüfen, ob er eine Möglichkeit bietet, die Codierung einer Datei festzulegen.

Der Sinn von UTF-8 ist es, 21-Bit-Zeichen (Unicode) als 8-Bit-Datenstrom zu codieren (weil dies das einzige ist, was alle Computer auf der Welt verarbeiten können). Da die meisten Betriebssysteme jedoch älter sind als Unicode, verfügen sie nicht über geeignete Tools, um die Codierungsinformationen an Dateien auf der Festplatte anzuhängen.

Die nächste Ausgabe ist die Darstellung in Python. Dies ist im Kommentar von heikogerlach perfekt erklärt. Sie müssen verstehen, dass Ihre Konsole nur ASCII anzeigen kann. Um Unicode oder irgendetwas> = charcode 128 anzuzeigen, muss ein Escape-Befehl verwendet werden. In Ihrem Editor müssen Sie nicht die mit Escape-Zeichenfolge versehene Anzeige eingeben, sondern die Bedeutung der Zeichenfolge (in diesem Fall müssen Sie den Umlaut eingeben und die Datei speichern).

Das heißt, Sie können die Python -Funktion eval () verwenden, um einen maskierten String in einen String umzuwandeln:

>>> x = eval("'Capit\\xc3\\xa1n\\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1

Wie Sie sehen, wurde die Zeichenfolge "\ xc3" in ein einzelnes Zeichen umgewandelt. Dies ist jetzt eine 8-Bit-Zeichenfolge, UTF-8-codiert. So erhalten Sie Unicode:

>>> x.decode('utf-8')
u'Capit\xe1n\n'

Gregg Lind fragte: Ich denke, hier fehlen einige Teile: Die Datei f2 enthält: hex:

0000000: 4361 7069 745c 7863 335c 7861 316e  Capit\xc3\xa1n

codecs.open('f2','rb', 'utf-8') liest sie beispielsweise alle in separaten Zeichen (erwartet) Gibt es eine Möglichkeit, in ASCII in eine Datei zu schreiben, die funktionieren würde?

Antwort: Das hängt davon ab, was Sie meinen. ASCII kann keine Zeichen> 127 darstellen. Sie müssen also irgendwie sagen, dass "die nächsten Zeichen etwas Besonderes bedeuten", was die Sequenz "\ x" bewirkt. Es heißt: Die nächsten beiden Zeichen sind der Code eines einzelnen Zeichens. "\ u" verwendet vier Zeichen, um Unicode bis zu 0xFFFF (65535) zu codieren.

Sie können also Unicode nicht direkt in ASCII schreiben (da ASCII einfach nicht dieselben Zeichen enthält). Sie können es als String-Escape schreiben (wie in f2); In diesem Fall kann die Datei als ASCII dargestellt werden. Sie können es auch als UTF-8 schreiben. In diesem Fall benötigen Sie einen sicheren 8-Bit-Stream.

Ihre Lösung mit decode('string-escape') funktioniert, aber Sie müssen wissen, wie viel Speicher Sie verwenden: Dreimal so viel wie mit codecs.open().

Denken Sie daran, dass eine Datei nur eine Folge von Bytes mit 8 Bits ist. Weder die Bits noch die Bytes haben eine Bedeutung. Sie sagen "65 bedeutet 'A'". Da \xc3\xa1 zu "à" werden soll, der Computer jedoch keine Möglichkeit hat, dies zu wissen, müssen Sie die beim Schreiben der Datei verwendete Codierung angeben.

6
Aaron Digulla

Nun, Ihr bevorzugter Texteditor erkennt nicht, dass \xc3\xa1 Zeichenliterale sein sollen, interpretiert sie jedoch als Text. Deshalb erhalten Sie in der letzten Zeile die doppelten Backslashes - es ist jetzt ein echter Backslash + xc3 usw. in Ihrer Datei.

Wenn Sie in Python codierte Dateien lesen und schreiben möchten, verwenden Sie am besten das Modul Codecs .

Das Einfügen von Text zwischen Terminal und Anwendungen ist schwierig, da Sie nicht wissen, welches Programm Ihren Text mit welcher Codierung interpretiert. Sie könnten Folgendes versuchen:

>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán

Fügen Sie dann diesen String in Ihren Editor ein und stellen Sie sicher, dass er mit Latin-1 gespeichert wird. Unter der Annahme, dass die Zwischenablage die Zeichenfolge nicht verfälscht, sollte der Roundtrip funktionieren.

5
Torsten Marek

Die\x .. -Sequenz ist spezifisch für Python. Es ist keine universelle Byte-Escape-Sequenz.

Wie Sie UTF-8-codierte Nicht-ASCII-Zeichen eingeben, hängt von Ihrem Betriebssystem und/oder Ihrem Editor ab. So geht's in Windows . Damit OS X ein mit einem akuten Akzent eingibt, können Sie einfach drücken option + E, dann Aund fast alle Texteditoren in OS X unterstützen UTF-8.

4
ʞɔıu

Sie können auch die ursprüngliche Funktion open() für die Arbeit mit Unicode-Dateien verbessern, indem Sie sie mit der Funktion partial ersetzen. Das Schöne an dieser Lösung ist, dass Sie keinen alten Code ändern müssen. Es ist transparent.

import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')
3
hipertracker

Ich habe versucht, iCal mit Python 2.7.9 zu analysieren:

aus icalendar importieren Kalender

Aber ich bekam:

 Traceback (most recent call last):
 File "ical.py", line 92, in parse
    print "{}".format(e[attr])
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128)

und es wurde behoben mit nur:

print "{}".format(e[attr].encode("utf-8"))

(Jetzt kann es wie ein Schloss gedruckt werden.)

1
Alexx Roche