Scrivi nel file UTF-8 in Python


204

Sono davvero confuso con il codecs.open function. Quando io faccio:

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

Mi dà l'errore

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

Se lo faccio:

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

Funziona bene

La domanda è: perché il primo metodo fallisce? E come inserisco la bom?

Se il secondo metodo è il modo corretto di farlo, che senso ha usare codecs.open(filename, "w", "utf-8")?


54
Non utilizzare una DBA in UTF-8. Per favore.
tchrist,

7
@tchrist Huh? Perchè no?
Salman von Abbas,

8
@SalmanPK BOM non è necessario in UTF-8 e aggiunge solo complessità (ad es. Non puoi semplicemente concatenare i file BOM e ottenere un testo valido). Vedi queste domande e risposte ; non perdere il grande commento sotto Q
Alois Mahdal,

Risposte:


271

Credo che il problema sia che si codecs.BOM_UTF8tratta di una stringa di byte, non di una stringa Unicode. Ho il sospetto che il gestore di file stia cercando di indovinare cosa intendi veramente basandomi su "Sono destinato a scrivere Unicode come testo con codifica UTF-8, ma mi hai dato una stringa di byte!"

Prova a scrivere direttamente la stringa Unicode per il segno dell'ordine dei byte (cioè Unicode U + FEFF), in modo che il file lo codifichi come UTF-8:

import codecs

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

(Sembra dare la risposta giusta: un file con byte EF BB BF.)

EDIT: il suggerimento di S. Lott di usare "utf-8-sig" come codifica è migliore che scrivere esplicitamente la DBA, ma lascio qui questa risposta perché spiega cosa non andava.


Attenzione: aprire e aprire non è lo stesso. Se fai "from codecs import open", NON sarà lo stesso che digiteresti "open".
Apache,

2
puoi anche usare codecs.open ('test.txt', 'w', 'utf-8-sig') invece
beta-closed

1
Ricevo "TypeError: è richiesto un numero intero (ottenuto il tipo str)". Non capisco cosa stiamo facendo qui. Qualcuno può aiutarmi per favore? Devo aggiungere una stringa (paragrafo) a un file di testo. Devo convertirlo in un numero intero prima di scrivere?
Mugen,

@Mugen: Il codice esatto che ho scritto funziona bene per quanto posso vedere. Ti suggerisco di porre una nuova domanda che mostra esattamente quale codice hai e dove si verifica l'errore.
Jon Skeet,

@Mugen devi chiamare codecs.openinvece di soloopen
northben

179

Leggi quanto segue: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

Fai questo

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

Il file risultante è UTF-8 con la distinta base prevista.


2
Grazie. Funzionava (Windows 7 x64, Python 2.7.5 x64). Questa soluzione funziona bene quando si apre il file in modalità "a" (append).
Mohamad Fakih,

Questo non ha funzionato per me, Python 3 su Windows. Ho dovuto farlo invece con open (file_name, 'wb') come bomfile: bomfile.write (codecs.BOM_UTF8), quindi riaprire il file per aggiungere.
Dustin Andrews,

Forse aggiungere temp.close()?
user2905353

2
@ user2905353: non richiesto; questo è gestito dalla gestione del contesto di open.
matheburg,

11

@ S-Lott fornisce la procedura corretta, ma espandendo i problemi Unicode , l' interprete Python può fornire ulteriori approfondimenti.

Jon Skeet ha ragione (inusuale) riguardo al codecsmodulo - contiene stringhe di byte:

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

Scegliendo un altro nit, BOMha un nome Unicode standard e può essere inserito come:

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

È inoltre accessibile tramite unicodedata:

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

8

Uso il comando file * nix per convertire un file charset sconosciuto in un file 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()

1
Utilizzare # coding: utf8invece del # -*- coding: utf-8 -*-quale è molto più facile da ricordare.
show0k
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.