¿Escribir texto Unicode en un archivo de texto?


Estoy extrayendo datos de un documento de Google, procesándolos y escribiéndolos en un archivo (que eventualmente pegaré en una página de Wordpress).

Tiene algunos símbolos no ASCII. ¿Cómo puedo convertir estos símbolos de forma segura que se pueden utilizar en código HTML?

Actualmente estoy convirtiendo todo a Unicode en el camino, uniéndolo todo en una cadena de Python, luego haciendo:

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

Hay un error de codificación en la última línea:

UnicodeDecodeError: el códec 'ascii' no puede decodificar el byte 0xa0 en posición 12286: ordinal no en rango (128)

Solución Parcial:

Este Python se ejecuta sin un error:

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

Pero si abro el archivo de texto real, veo muchos símbolos como:

Qur’an 

¿Tal vez necesito escribir en algo que no sea un archivo de texto?

Author: Aaron Hall, 2011-05-18

7 answers

Trate exclusivamente con objetos unicode tanto como sea posible decodificando cosas a objetos unicode cuando los obtenga por primera vez y codificándolos según sea necesario al salir.

Si su cadena es realmente un objeto unicode, deberá convertirla en un objeto de cadena codificado en unicode antes de escribirla en un archivo:

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

Cuando vuelva a leer ese archivo, obtendrá una cadena codificada en unicode que puede decodificar en un objeto unicode:

f = file('test', 'r')
print f.read().decode('utf8')
 285
Author: quasistoic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 16:49:01

En Python 2.6+, podrías usar io.open() eso es predeterminado (builtin open()) en Python 3:

import io

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

Podría ser más conveniente si necesita escribir el texto de forma incremental (no necesita llamar a unicode_text.encode(character_encoding) varias veces). A diferencia del módulo codecs, el módulo io tiene un soporte universal adecuado para nuevas líneas.

 51
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 13:49:08

El archivo abierto por codecs.open es un archivo que toma unicode datos, los codifica en iso-8859-1 y los escribe en el archivo. Sin embargo, lo que intenta escribir no es unicode; coger unicode y codificarla en iso-8859-1 usted. Eso es lo que hace el método unicode.encode, y el resultado de codificar una cadena unicode es un bytestring (un tipo str.)

Debe usar normal open() y codificar el unicode usted mismo, o (generalmente una mejor idea) usar codecs.open() y no codificar los datos usted mismo.

 18
Author: Thomas Wouters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 16:55:20

Prefacio: ¿funcionará tu visor?

Asegúrese de que su visor/editor/terminal (sin embargo usted está interactuando con su archivo codificado utf-8) puede leer el archivo. Esto suele ser un problema en Windows , por ejemplo, el bloc de notas.

¿Escribir texto Unicode en un archivo de texto?

En Python 2, use open desde el módulo io (esto es lo mismo que el builtin open en Python 3):

import io

Las mejores prácticas, en general, utilizan UTF-8 para escribir en archivos (ni siquiera tenemos que preocuparnos por el orden de bytes con utf-8).

encoding = 'utf-8'

Utf-8 es la codificación más moderna y universalmente utilizable - funciona en todos los navegadores web, la mayoría de los editores de texto (consulte su configuración si tiene problemas) y la mayoría de los terminales/shells.

En Windows, puede probar utf-16le si está limitado a ver la salida en el bloc de notas (u otro visor limitado).

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

Y simplemente ábrelo con el administrador de contexto y escribe tus caracteres unicode fuera:

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

Ejemplo usando muchos caracteres Unicode

Aquí hay un ejemplo que intenta mapear cada carácter posible hasta tres bits de ancho (4 es el máximo, pero eso sería ir un poco lejos) desde la representación digital (en enteros) a una salida imprimible codificada, junto con su nombre, si es posible (ponga esto en un archivo llamado 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))

Esto debe ejecutarse en el orden de aproximadamente un minuto, y puede ver el archivo de datos, y si su visor de archivos puede mostrar unicode, lo verás. La información sobre las categorías se puede encontrar aquí. En base a los recuentos, probablemente podamos mejorar nuestros resultados excluyendo las categorías Cn y Co, que no tienen símbolos asociados con ellas.

$ python uni.py

Mostrará la asignación hexadecimal, categoría, símbolo (a menos que no pueda obtener el nombre, por lo que probablemente un carácter de control), y el nombre del símbolo. por ejemplo,

Recomiendo less en Unix o Cygwin (no imprimir / cat todo el archivo a su salida):

$ less unidata

Por ejemplo, se mostrará similar a las siguientes líneas que muestreé 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

Mi Python 3.5 de Anaconda tiene unicode 8.0, supongo que la mayoría de los 3 lo harían.

 13
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-27 12:21:54

El manejo de cadenas Unicode está estandarizado en Python 3.

  1. Los caracteres se almacenan en Unicode
  2. Solo necesita abrir el archivo en utf-8

    out1 = "(嘉南大圳 ㄐㄧㄚ ㄋㄢˊ ㄉㄚˋ ㄗㄨㄣˋ )"
    fobj = open("t1.txt", "w", encoding="utf-8")
    fobj.write(out1)
    fobj.close()
    
 8
Author: david m lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-15 01:20:47

Cómo imprimir caracteres unicode en un archivo:

Guarde esto en el archivo: 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: é')

Ejecútelo y canalice la salida al archivo:

python foo.py > tmp.txt

Abrir tmp.txt y mira dentro, ves esto:

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

Por lo tanto, ha guardado unicode e con una marca de ofuscación en un archivo.

 3
Author: Eric Leschinski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-27 18:36:02

Ese error surge cuando intenta codificar una cadena no unicode: intenta decodificarla, asumiendo que está en ASCII normal. Hay dos posibilidades:

  1. Lo estás codificando a bytestring, pero porque has usado códecs.abierto, el método write espera un objeto unicode. Así que lo codificas, y trata de decodificarlo de nuevo. Intenta: f.write(all_html) en su lugar.
  2. all_html no es, de hecho, un objeto unicode. Cuando haces .encode(...), primero intenta decodificarlo.
 1
Author: Thomas K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 16:45:01