Scrivere testo Unicode in un file di testo?


225

Sto estraendo i dati da un documento Google, elaborandoli e scrivendoli in un file (che alla fine incollerò in una pagina di Wordpress).

Ha alcuni simboli non ASCII. Come posso convertirli in modo sicuro in simboli che possono essere utilizzati nel sorgente HTML?

Attualmente sto convertendo tutto in Unicode mentre ci arrivo, unendo tutto in una stringa Python, quindi facendo:

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

C'è un errore di codifica sull'ultima riga:

UnicodeDecodeError: il codec 'ascii' non può decodificare il byte 0xa0 nella posizione 12286: ordinale non compreso nell'intervallo (128)

Soluzione parziale:

Questo Python funziona senza errori:

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"))

Ma se apro il file di testo effettivo, vedo molti simboli come:

Qur’an 

Forse devo scrivere su qualcosa di diverso da un file di testo?


1
Il programma che stai utilizzando per aprirlo non sta interpretando correttamente il testo UTF-8. Dovrebbe avere un'opzione per aprire il file come UTF-8.
Thomas K,

Risposte:


322

Gestisci il più possibile esclusivamente gli oggetti unicode decodificando le cose in unicode quando le ottieni per la prima volta e codificandole se necessario all'uscita.

Se la tua stringa è in realtà un oggetto Unicode, dovrai convertirla in un oggetto String codificato Unicode prima di scriverla in un file:

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

Quando rileggi quel file, otterrai una stringa con codifica Unicode che puoi decodificare in un oggetto Unicode:

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

Grazie. Questo funziona senza errori, ma se apro il file di testo, vedo un mucchio di simboli strani :) Devo copiare e incollare il testo in una pagina di Wordpress (non chiedere). C'è un modo in cui posso effettivamente stampare i simboli che ci sono? Immagino non in un file txt, giusto, ma forse in qualcos'altro?
simon,

1
Cosa stai usando per aprire il file di testo? Immagino che tu sia su Windows e lo apri in Blocco note, che non è troppo intelligente con le codifiche. Cosa succede quando lo apri in Wordpad?
quasistoic

@quasistoic da dove viene il metodo del file ?
Omar Cusma Fait,

Avevo bisogno di attivare la modalità binaria, cioè f = open ('test', 'wb'), come descritto in stackoverflow.com/a/5513856/6580199 - altrimenti otterrei "TypeError: l'argomento write () deve essere str, non byte "
Benji,

72

In Python 2.6+, è possibile utilizzareio.open() quello predefinito ( incorporatoopen() ) su Python 3:

import io

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

Potrebbe essere più conveniente se è necessario scrivere il testo in modo incrementale (non è necessario chiamare unicode_text.encode(character_encoding)più volte). A differenza del codecsmodulo, il iomodulo dispone di un supporto newlines universale adeguato.


1
Amico, ho passato così tanto tempo a trovarlo! Grazie!
Georgy Gobozov,

2
Questo funziona anche per Python 3 (ovvio, ma comunque degno di nota).
Ippopotamo,

37

La gestione delle stringhe Unicode è già standardizzata in Python 3.

  1. i caratteri sono già memorizzati in Unicode (32 bit) in memoria
  2. Devi solo aprire il file in utf-8
    (la conversione da Unicode a 32 bit in utf-8 a lunghezza di byte variabile viene eseguita automaticamente dalla memoria al file.)

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

Ma questo non funziona su Python 2, giusto? (Dovrei dire, su questo codice Python 3, sembra così conciso e ragionevole)
Liwen Zhao,

non dovrebbe funzionare su Python 2. Restiamo su Python 3. 3 è molto meglio.
david m lee,

18

Il file aperto da codecs.openè un file che prende i unicodedati, li codifica iso-8859-1e li scrive nel file. Tuttavia, ciò che si tenta di scrivere non lo è unicode; lo prendi unicodee lo codifichi in iso-8859-1 te stesso . Questo è ciò che fa il unicode.encodemetodo e il risultato della codifica di una stringa unicode è un bytestring (un strtipo).

Dovresti usare normale open()e codificare tu stesso l'unicode o (di solito un'idea migliore) usare codecs.open()e non codificare tu stesso i dati.


17

Prefazione: il tuo spettatore funzionerà?

Assicurati che il tuo visualizzatore / editor / terminale (comunque tu stia interagendo con il tuo file codificato utf-8) possa leggere il file. Questo è spesso un problema su Windows , ad esempio Blocco note.

Scrivere testo Unicode in un file di testo?

In Python 2, usa opendal iomodulo (questo è lo stesso del built- openin in Python 3):

import io

Le migliori pratiche, in generale, usano UTF-8per scrivere su file (non dobbiamo nemmeno preoccuparci di byte-order con utf-8).

encoding = 'utf-8'

utf-8 è la codifica più moderna e universalmente utilizzabile: funziona in tutti i browser Web, nella maggior parte degli editor di testo (vedere le impostazioni in caso di problemi) e nella maggior parte dei terminali / shell.

Su Windows, potresti provare utf-16lese sei limitato a visualizzare l'output in Blocco note (o in un altro visualizzatore limitato).

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

E basta aprirlo con il gestore di contesto e scrivere i caratteri unicode:

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

Esempio usando molti caratteri Unicode

Ecco un esempio che tenta di mappare ogni possibile carattere largo fino a tre bit (4 è il massimo, ma che andrebbe un po 'lontano) dalla rappresentazione digitale (in numeri interi) a un output stampabile codificato, insieme al suo nome, se possibile (inseriscilo in un file chiamato 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))

Dovrebbe essere eseguito nell'ordine di circa un minuto e puoi visualizzare il file di dati e se il tuo visualizzatore di file può visualizzare unicode, lo vedrai. Informazioni sulle categorie sono disponibili qui . Sulla base dei conteggi, possiamo probabilmente migliorare i nostri risultati escludendo le categorie Cn e Co, a cui non sono associati simboli.

$ python uni.py

Visualizzerà la mappatura esadecimale, la categoria , il simbolo (a meno che non sia possibile ottenere il nome, quindi probabilmente un carattere di controllo) e il nome del simbolo. per esempio

Raccomando lesssu Unix o Cygwin (non stampare / cat l'intero file sul tuo output):

$ less unidata

ad esempio, verrà visualizzato in modo simile alle seguenti righe che ho campionato da esso usando Python 2 (unicode 5.2):

     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

Il mio Python 3.5 di Anaconda ha Unicode 8.0, presumo che la maggior parte dei 3 lo farebbe.


3

Come stampare caratteri unicode in un file:

Salvalo nel file: 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: é')

Eseguilo e invia l'output al file:

python foo.py > tmp.txt

Apri tmp.txt e guarda dentro, vedi questo:

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

Così hai salvato unicode e con un segno di offuscamento su di esso in un file.


2
Ero piuttosto entusiasta di questa risposta, ma dà un errore sulla mia macchina. Quando copio / incollo il codice, viene visualizzato un messaggio di errore: "TypeError: must be str, not bytes"
Richard Rast

1

Tale errore si presenta quando si tenta di codificare una stringa non unicode: tenta di decodificarla, presupponendo che sia in chiaro ASCII. Vi sono due possibilità:

  1. Lo stai codificando in un bytestring, ma poiché hai usato codecs.open, il metodo write prevede un oggetto unicode. Quindi lo codifichi e prova a decodificarlo di nuovo. Prova: f.write(all_html)invece.
  2. all_html non è, in effetti, un oggetto unicode. Quando lo fai .encode(...), cerca prima di decodificarlo.

0

In caso di scrittura 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à'

In caso di scrittura 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)

Per evitare questo errore dovresti codificarlo in byte usando i codec "utf-8" in questo modo:

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

e decodifica i dati durante la lettura usando i codec "utf-8":

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

E anche se provi ad eseguire la stampa su questa stringa, decodificherà automaticamente usando i codec "utf-8" come questo

>>> print a
batsà
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.