it-swarm.dev

Escreva para o arquivo UTF-8 em Python

Estou muito confuso com o codecs.open function. Quando eu faço:

file = codecs.open("temp", "w", "utf-8")
file.write(codecs.BOM_UTF8)
file.close()

Isso me dá o erro

UnicodeDecodeError: O codec 'ascii' não pode decodificar o byte 0xef na posição 0: ordinal não está no intervalo (128)

Se eu fizer:

file = open("temp", "w")
file.write(codecs.BOM_UTF8)
file.close()

Funciona bem.

Pergunta é por que o primeiro método falha? E como eu insiro o bom?

Se o segundo método é a maneira correta de fazê-lo, qual o ponto de usar codecs.open(filename, "w", "utf-8")?

175
John Jiang

Eu acredito que o problema é que codecs.BOM_UTF8 é uma string de byte, não uma string Unicode. Eu suspeito que o manipulador de arquivos está tentando adivinhar o que você realmente quer dizer com base em "Eu estou destinado a escrever Unicode como texto codificado em UTF-8, mas você me deu uma string de byte!"

Tente escrever a string Unicode para a marca de ordem de byte (isto é, Unicode U + FEFF) diretamente, para que o arquivo apenas codifique como UTF-8:

import codecs

file = codecs.open("lol", "w", "utf-8")
file.write(u'\ufeff')
file.close()

(Isso parece dar a resposta certa - um arquivo com bytes EF BB BF.)

EDIT: S. Lott sugestão de usar "utf-8-sig" como a codificação é melhor do que explicitamente escrevendo o BOM você mesmo, mas eu ' Vou deixar esta resposta aqui, pois explica o que estava errado antes.

250
Jon Skeet

Leia o seguinte: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

Faça isso

with codecs.open("test_output", "w", "utf-8-sig") as temp:
    temp.write("hi mom\n")
    temp.write(u"This has ♭")

O arquivo resultante é UTF-8 com a lista de materiais esperada.

164
S.Lott

@ S-Lott fornece o procedimento correto, mas expandindo os problemas Unicode , o Python O interpretador pode fornecer mais insights.

Jon Skeet está certo (incomum) sobre o codecs module - contém strings de bytes:

>>> import codecs
>>> codecs.BOM
'\xff\xfe'
>>> codecs.BOM_UTF8
'\xef\xbb\xbf'
>>> 

Escolhendo outro nit, o BOM tem um nome padrão Unicode , e pode ser digitado como:

>>> bom= u"\N{ZERO WIDTH NO-BREAK SPACE}"
>>> bom
u'\ufeff'

Também é acessível via unicodedata :

>>> import unicodedata
>>> unicodedata.lookup('ZERO WIDTH NO-BREAK SPACE')
u'\ufeff'
>>> 
10
gimel

Eu uso o comando file * nix para converter um arquivo charset desconhecido em um arquivo utf-8

# -*- 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()
8
Ricardo