Unicode-Text in eine Textdatei schreiben?


225

Ich ziehe Daten aus einem Google-Dokument, verarbeite sie und schreibe sie in eine Datei (die ich schließlich in eine Wordpress-Seite einfügen werde).

Es hat einige Nicht-ASCII-Symbole. Wie kann ich diese sicher in Symbole konvertieren, die in HTML-Quellen verwendet werden können?

Momentan konvertiere ich alles auf dem Weg nach Unicode, füge alles in einem Python-String zusammen und mache dann:

import codecs
f = codecs.open('out.txt', mode="w", encoding="iso-8859-1")
f.write(all_html.encode("iso-8859-1", "replace"))

In der letzten Zeile ist ein Codierungsfehler aufgetreten:

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

Teillösung:

Dieser Python läuft ohne Fehler:

row = [unicode(x.strip()) if x is not None else u'' for x in row]
all_html = row[0] + "<br/>" + row[1]
f = open('out.txt', 'w')
f.write(all_html.encode("utf-8"))

Aber wenn ich dann die eigentliche Textdatei öffne, sehe ich viele Symbole wie:

Qur’an 

Vielleicht muss ich in etwas anderes als eine Textdatei schreiben?


1
Das Programm, mit dem Sie es öffnen, interpretiert den UTF-8-Text nicht richtig. Es sollte eine Option zum Öffnen der Datei als UTF-8 geben.
Thomas K

Antworten:


322

Beschäftigen Sie sich so weit wie möglich ausschließlich mit Unicode-Objekten, indem Sie Dinge beim ersten Abrufen in Unicode-Objekte dekodieren und sie auf dem Weg nach draußen nach Bedarf codieren.

Wenn Ihre Zeichenfolge tatsächlich ein Unicode-Objekt ist, müssen Sie sie in ein Unicode-codiertes Zeichenfolgenobjekt konvertieren, bevor Sie sie in eine Datei schreiben:

foo = u'Δ, Й, ק, ‎ م, ๗, あ, 叶, 葉, and 말.'
f = open('test', 'w')
f.write(foo.encode('utf8'))
f.close()

Wenn Sie diese Datei erneut lesen, erhalten Sie eine Unicode-codierte Zeichenfolge, die Sie in ein Unicode-Objekt dekodieren können:

f = file('test', 'r')
print f.read().decode('utf8')

Vielen Dank. Dies läuft ohne Fehler, aber wenn ich die Textdatei öffne, sehe ich eine Reihe seltsamer Symbole :) Ich muss den Text kopieren und in eine Wordpress-Seite einfügen (nicht fragen). Gibt es eine Möglichkeit, die dort vorhandenen Symbole tatsächlich zu drucken? Ich denke nicht zu einer txt-Datei, richtig, aber vielleicht zu etwas anderem?
Simon

1
Womit öffnen Sie die Textdatei? Ich vermute, Sie arbeiten unter Windows und öffnen es im Editor, der mit Codierungen nicht allzu intelligent ist. Was passiert, wenn Sie es in Wordpad öffnen?
Quasistoic

@quasistoic wo sich die Datei Methode kommen Form?
Omar Cusma Fait

Ich musste den Binärmodus aktivieren , dh f = open ('test', 'wb'), wie in stackoverflow.com/a/5513856/6580199 beschrieben - andernfalls würde ich das Argument "TypeError: write () muss str sein, keine Bytes "
Benji

72

In Python 2.6+, könnten Sie verwendenio.open() das ist Standard ( builtinopen() ) auf Python 3:

import io

with io.open(filename, 'w', encoding=character_encoding) as file:
    file.write(unicode_text)

Es ist möglicherweise praktischer, wenn Sie den Text schrittweise schreiben müssen (Sie müssen nicht unicode_text.encode(character_encoding)mehrmals anrufen ). Im Gegensatz zum codecsModul verfügt das ioModul über eine ordnungsgemäße Unterstützung für universelle Zeilenumbrüche.


1
Mann, ich habe so viel Zeit damit verbracht, das zu finden! Danke dir!
Georgy Gobozov

2
Dies funktioniert auch für Python 3 (offensichtlich, aber dennoch erwähnenswert).
Hippo

37

Die Behandlung von Unicode-Zeichenfolgen ist in Python 3 bereits standardisiert.

  1. Zeichen sind bereits in Unicode (32-Bit) im Speicher gespeichert
  2. Sie müssen die Datei nur in utf-8 öffnen
    (die 32-Bit-Konvertierung von utf-8 in Unicode in variable Byte-Länge wird automatisch vom Speicher in die Datei durchgeführt.)

    out1 = "(嘉南大圳 ㄐㄧㄚ ㄋㄢˊ ㄉㄚˋ ㄗㄨㄣˋ )"
    fobj = open("t1.txt", "w", encoding="utf-8")
    fobj.write(out1)
    fobj.close()

Aber das funktioniert unter Python 2 nicht, oder? (Ich sollte sagen, auf diesem Python 3-Code sieht es so prägnant und vernünftig aus)
Liwen Zhao

es sollte nicht auf Python 2 funktionieren. Wir bleiben auf Python 3. 3 ist so viel besser.
David M Lee

18

Die von geöffnete Datei codecs.openist eine Datei, die unicodeDaten aufnimmt , codiert iso-8859-1und in die Datei schreibt. Was Sie jedoch zu schreiben versuchen, ist nicht unicode; du nimmst unicodeund verschlüsselst es in iso-8859-1 dir . Das ist, was die unicode.encodeMethode tut, und das Ergebnis der Codierung einer Unicode-Zeichenfolge ist ein Bytestring (astr Typ).

Sie sollten entweder normal verwenden open()und den Unicode selbst codieren oder (normalerweise eine bessere Idee) die Daten selbst verwenden codecs.open()und nicht codieren.


17

Vorwort: Funktioniert Ihr Viewer?

Stellen Sie sicher, dass Ihr Viewer / Editor / Terminal (wie auch immer Sie mit Ihrer utf-8-codierten Datei interagieren) die Datei lesen kann. Dies ist häufig ein Problem unter Windows , z. B. Notepad.

Unicode-Text in eine Textdatei schreiben?

Verwenden Sie in Python 2 opendas ioModul (dies entspricht dem openin Python 3 integrierten):

import io

Best Practice wird im Allgemeinen UTF-8zum Schreiben in Dateien verwendet (wir müssen uns mit utf-8 nicht einmal um die Bytereihenfolge kümmern).

encoding = 'utf-8'

utf-8 ist die modernste und universell verwendbare Codierung - es funktioniert in allen Webbrowsern, den meisten Texteditoren (siehe Ihre Einstellungen, wenn Sie Probleme haben) und den meisten Terminals / Shells.

Unter Windows können Sie versuchen utf-16le, die Ausgabe nur im Editor (oder einem anderen eingeschränkten Viewer) anzuzeigen.

encoding = 'utf-16le' # sorry, Windows users... :(

Öffnen Sie es einfach mit dem Kontextmanager und schreiben Sie Ihre Unicode-Zeichen auf:

with io.open(filename, 'w', encoding=encoding) as f:
    f.write(unicode_object)

Beispiel mit vielen Unicode-Zeichen

Hier ist ein Beispiel, das versucht, jedes mögliche Zeichen mit einer Breite von bis zu drei Bit (4 ist das Maximum, aber das würde ein bisschen weit gehen) von der digitalen Darstellung (in ganzen Zahlen) zusammen mit seinem Namen auf eine codierte druckbare Ausgabe abzubilden, wenn möglich (in eine Datei mit dem Namen uni.py):

from __future__ import print_function
import io
from unicodedata import name, category
from curses.ascii import controlnames
from collections import Counter

try: # use these if Python 2
    unicode_chr, range = unichr, xrange
except NameError: # Python 3
    unicode_chr = chr

exclude_categories = set(('Co', 'Cn'))
counts = Counter()
control_names = dict(enumerate(controlnames))
with io.open('unidata', 'w', encoding='utf-8') as f:
    for x in range((2**8)**3): 
        try:
            char = unicode_chr(x)
        except ValueError:
            continue # can't map to unicode, try next x
        cat = category(char)
        counts.update((cat,))
        if cat in exclude_categories:
            continue # get rid of noise & greatly shorten result file
        try:
            uname = name(char)
        except ValueError: # probably control character, don't use actual
            uname = control_names.get(x, '')
            f.write(u'{0:>6x} {1}    {2}\n'.format(x, cat, uname))
        else:
            f.write(u'{0:>6x} {1}  {2}  {3}\n'.format(x, cat, char, uname))
# may as well describe the types we logged.
for cat, count in counts.items():
    print('{0} chars of category, {1}'.format(count, cat))

Dies sollte in der Größenordnung von ungefähr einer Minute ausgeführt werden, und Sie können die Datendatei anzeigen. Wenn Ihr Datei-Viewer Unicode anzeigen kann, wird dies angezeigt. Informationen zu den Kategorien finden Sie hier . Basierend auf den Zählungen können wir unsere Ergebnisse wahrscheinlich verbessern, indem wir die Kategorien Cn und Co ausschließen, denen keine Symbole zugeordnet sind.

$ python uni.py

Es wird die hexadezimale Zuordnung Kategorie angezeigt , das Symbol (es sei denn, der Name kann nicht abgerufen werden, also wahrscheinlich ein Steuerzeichen) und der Name des Symbols . z.B

Ich empfehle lessunter Unix oder Cygwin (drucke / cat nicht die gesamte Datei für deine Ausgabe):

$ less unidata

zB wird ähnlich wie in den folgenden Zeilen angezeigt, die ich mit Python 2 (Unicode 5.2) daraus abgetastet habe:

     0 Cc NUL
    20 Zs     SPACE
    21 Po  !  EXCLAMATION MARK
    b6 So    PILCROW SIGN
    d0 Lu  Ð  LATIN CAPITAL LETTER ETH
   e59 Nd    THAI DIGIT NINE
  2887 So    BRAILLE PATTERN DOTS-1238
  bc13 Lo    HANGUL SYLLABLE MIH
  ffeb Sm    HALFWIDTH RIGHTWARDS ARROW

Mein Python 3.5 von Anaconda hat Unicode 8.0, ich würde davon ausgehen, dass die meisten 3er dies tun würden.


3

So drucken Sie Unicode-Zeichen in eine Datei:

Speichern Sie dies in der Datei: foo.py:

#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
import codecs
import sys 
UTF8Writer = codecs.getwriter('utf8')
sys.stdout = UTF8Writer(sys.stdout)
print(u'e with obfuscation: é')

Führen Sie es aus und leiten Sie die Ausgabe an die Datei weiter:

python foo.py > tmp.txt

Öffnen Sie tmp.txt und schauen Sie hinein. Sie sehen Folgendes:

el@apollo:~$ cat tmp.txt 
e with obfuscation: é

Sie haben also Unicode e mit einem Verschleierungszeichen in einer Datei gespeichert.


2
Ich war ziemlich aufgeregt über diese Antwort, aber es gibt einen Fehler auf meinem Computer. Wenn ich Ihren Code kopiere / einfüge, erhalte ich eine Fehlermeldung: "TypeError: muss str sein, keine Bytes"
Richard Rast

1

Dieser Fehler tritt auf, wenn Sie versuchen, eine Nicht-Unicode-Zeichenfolge zu codieren: Sie versucht, sie zu decodieren, vorausgesetzt, sie ist in einfachem ASCII-Format. Es gibt zwei Möglichkeiten:

  1. Sie codieren es in einen Bytestring, aber da Sie codecs.open verwendet haben, erwartet die Schreibmethode ein Unicode-Objekt. Sie codieren es also und es versucht erneut, es zu decodieren. Versuchen Sie: f.write(all_html)stattdessen.
  2. all_html ist in der Tat kein Unicode-Objekt. Wenn Sie dies tun .encode(...), wird zuerst versucht, es zu dekodieren.

0

Beim Schreiben in Python3

>>> a = u'bats\u00E0'
>>> print a
batsà
>>> f = open("/tmp/test", "w")
>>> f.write(a)
>>> f.close()
>>> data = open("/tmp/test").read()
>>> data
'batsà'

Beim Schreiben in Python2:

>>> a = u'bats\u00E0'
>>> f = open("/tmp/test", "w")
>>> f.write(a)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)

Um diesen Fehler zu vermeiden, müssten Sie ihn mit den folgenden Codecs "utf-8" in Bytes codieren:

>>> f.write(a.encode("utf-8"))
>>> f.close()

und dekodieren Sie die Daten beim Lesen mit den Codecs "utf-8":

>>> data = open("/tmp/test").read()
>>> data.decode("utf-8")
u'bats\xe0'

Und auch wenn Sie versuchen, diese Zeichenfolge zu drucken, wird sie automatisch mit den folgenden Codecs "utf-8" dekodiert

>>> print a
batsà
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.