¿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 convertirlos de forma segura en símbolos que se pueden usar en una fuente HTML?

Actualmente estoy convirtiendo todo a Unicode en el camino, uniéndolo en una cadena de Python, y 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 encoding en la última línea:

UnicodeDecodeError: el codec ‘ascii’ no puede decodificar el byte 0xa0 en la posición 12286: ordinal no está dentro del 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] + "
" + row[1] f = open('out.txt', 'w') f.write(all_html.encode("utf-8")

Pero luego, 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?

Trate exclusivamente con objetos Unicode tanto como sea posible mediante la deencoding de cosas a objetos Unicode cuando los obtiene por primera vez y codifíquelos cuando sea necesario al salir.

Si su cadena es en realidad un objeto Unicode, deberá convertirla en un objeto de cadena codificada en Unicode antes de escribirlo en un archivo:

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

Cuando vuelvas a leer ese archivo, obtendrás una cadena codificada en Unicode que puedes decodificar en un objeto Unicode:

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

En Python 2.6+, puedes usar io.open() que es predeterminado ( incorporado en 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 manera incremental (no necesita llamar a unicode_text.encode(character_encoding) varias veces). A diferencia del módulo de codecs , el módulo io tiene un adecuado soporte para nuevas líneas universales.

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

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

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

  1. Char se almacenan en Unicode
  2. Solo necesitas abrir el archivo en utf-8.

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

Prefacio: ¿tu visor funcionará?

Asegúrese de que su visor / editor / terminal (sin embargo, está interactuando con su archivo codificado en 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 incorporado 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 encoding más moderna y de uso universal; funciona en todos los navegadores web, en la mayoría de los editores de texto (consulte su configuración si tiene problemas) y en la mayoría de los terminales / shells.

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

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

Y solo ábrelo con el administrador de contexto y escribe tus caracteres Unicode:

 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 de hasta tres bits de ancho (4 es el máximo, pero iría un poco más lejos) de la representación digital (en enteros) a una salida imprimible codificada, junto con su nombre, si posible (poner 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 debería 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á. La información sobre las categorías se puede encontrar aquí . Con base en los conteos, probablemente podamos mejorar nuestros resultados al excluir las categorías Cn y Co, que no tienen símbolos asociados con ellos.

 $ python uni.py 

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

Recomiendo less en Unix o Cygwin (no imprima / envíe 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 sería.

Cómo imprimir caracteres Unicode en un archivo:

Guarda 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: é') 

Ejecutarlo y canalizar la salida al archivo:

 python foo.py > tmp.txt 

Abre 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.

Ese error surge cuando intentas codificar una cadena que no es Unicode: intenta decodificarla, asumiendo que está en ASCII simple. Hay dos posibilidades:

  1. Lo estás codificando en una serie de bytestring, pero debido a que has usado codecs.open, el método de escritura espera un objeto Unicode. Así que lo codificas, y trata de decodificarlo de nuevo. Intente: f.write(all_html) lugar.
  2. all_html no es, de hecho, un objeto Unicode. Cuando haces .encode(...) , primero intenta decodificarlo.