Konvertieren Sie UTF-8 mit BOM in UTF-8 ohne BOM in Python
Lesezeit: 5 Minuten
Pauke
Zwei Fragen hier. Ich habe eine Reihe von Dateien, die normalerweise UTF-8 mit BOM sind. Ich möchte sie (idealerweise vorhanden) ohne BOM in UTF-8 konvertieren. Wie es scheint codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors) würde damit umgehen. Aber ich sehe keine wirklich guten Beispiele für die Verwendung. Wäre dies der beste Weg, damit umzugehen?
source files:
Tue Jan 17$ file brh-m-157.json
brh-m-157.json: UTF-8 Unicode (with BOM) text
Außerdem wäre es ideal, wenn wir ohne explizites Wissen mit unterschiedlichen Eingabecodierungen umgehen könnten (siehe ASCII und UTF-16). Das scheint alles machbar zu sein. Gibt es eine Lösung, die jede bekannte Python-Codierung verwenden und als UTF-8 ohne BOM ausgeben kann?
bearbeiten 1 vorgeschlagene Sol’n von unten (Danke!)
fp = open('brh-m-157.json','rw')
s = fp.read()
u = s.decode('utf-8-sig')
s = u.encode('utf-8')
print fp.encoding
fp.write(s)
Dies gibt mir den folgenden Fehler:
IOError: [Errno 9] Bad file descriptor
Newsflash
In Kommentaren wird mir gesagt, dass der Fehler darin besteht, dass ich die Datei mit dem Modus „rw“ anstelle von „r+“https://stackoverflow.com/“r+b“ öffne, also sollte ich meine Frage eventuell erneut bearbeiten und entfernen der gelöste Teil.
Sie müssen Ihre Datei zum Lesen plus Update öffnen, dh mit a r+ Modus. Hinzufügen b auch so, dass es auch unter Windows ohne irgendwelche komischen Zeilenenden funktioniert. Schließlich sollten Sie zum Anfang der Datei zurückkehren und sie am Ende abschneiden – siehe meine aktualisierte Antwort.
fp = open("file.txt")
s = fp.read()
u = s.decode("utf-8-sig")
Das gibt dir ein unicode Zeichenfolge ohne die Stückliste. Sie können dann verwenden
s = u.encode("utf-8")
um eine normale UTF-8-codierte Zeichenfolge wieder einzufügen s. Wenn Ihre Dateien groß sind, sollten Sie vermeiden, sie alle in den Speicher zu lesen. Die Stückliste besteht einfach aus drei Bytes am Anfang der Datei, sodass Sie diesen Code verwenden können, um sie aus der Datei zu entfernen:
import os, sys, codecs
BUFSIZE = 4096
BOMLEN = len(codecs.BOM_UTF8)
path = sys.argv[1]
with open(path, "r+b") as fp:
chunk = fp.read(BUFSIZE)
if chunk.startswith(codecs.BOM_UTF8):
i = 0
chunk = chunk[BOMLEN:]
while chunk:
fp.seek(i)
fp.write(chunk)
i += len(chunk)
fp.seek(BOMLEN, os.SEEK_CUR)
chunk = fp.read(BUFSIZE)
fp.seek(-BOMLEN, os.SEEK_CUR)
fp.truncate()
Es öffnet die Datei, liest einen Teil und schreibt es 3 Bytes früher als dort, wo es es gelesen hat, in die Datei. Die Datei wird an Ort und Stelle neu geschrieben. Als einfachere Lösung besteht darin, die kürzere Datei wie die Antwort von newtover in eine neue Datei zu schreiben. Das wäre einfacher, verbraucht aber kurzzeitig den doppelten Speicherplatz.
Um die Codierung zu erraten, können Sie die Codierung einfach von den meisten bis zu den am wenigsten spezifischen durchlaufen:
def decode(s):
for encoding in "utf-8-sig", "utf-16":
try:
return s.decode(encoding)
except UnicodeDecodeError:
continue
return s.decode("latin-1") # will always work
Eine UTF-16-codierte Datei wird nicht als UTF-8 decodiert, also versuchen wir es zuerst mit UTF-8. Wenn das fehlschlägt, versuchen wir es mit UTF-16. Schließlich verwenden wir Latin-1 – das wird immer funktionieren, da alle 256 Bytes gültige Werte in Latin-1 sind. Vielleicht möchten Sie zurückkehren None Stattdessen in diesem Fall, da es sich wirklich um ein Fallback handelt und Ihr Code dies möglicherweise sorgfältiger handhaben möchte (wenn dies möglich ist).
hmm, ich habe die Frage in Bearbeitung Nr. 1 mit Beispielcode aktualisiert, aber einen schlechten Dateideskriptor erhalten. thx für jede hilfe. Ich versuche das herauszufinden.
– Pauke
17. Januar 12 um 17:29 Uhr
scheint bekommen AttributeError: 'str' object has no attribute 'decode'. Also habe ich endlich den Code als verwendet with open(filename,encoding='utf-8-sig') as f_content:dann doc = f_content.read() und es hat bei mir funktioniert.
– Clemens116
20. April 21 um 19:21 Uhr
Geng Jiawen
In Python 3 ist es ganz einfach: Datei lesen und neu schreiben mit utf-8 Codierung:
s = open(bom_file, mode="r", encoding='utf-8-sig').read()
open(bom_file, mode="w", encoding='utf-8').write(s)
import codecs
import shutil
import sys
s = sys.stdin.read(3)
if s != codecs.BOM_UTF8:
sys.stdout.write(s)
shutil.copyfileobj(sys.stdin, sys.stdout)
Können Sie erklären, wie dieser Code funktioniert? $ remove_bom.py < input.txt > output.txt Habe ich recht?
– guneysus
2. November 13 um 12:38 Uhr
@guneysus, ja genau
– neu
2. November 13 um 18:55 Uhr
Dies ist meine Implementierung, um jede Art von Codierung ohne BOM in UTF-8 zu konvertieren und Windows-Enlines durch das universelle Format zu ersetzen:
def utf8_converter(file_path, universal_endline=True):
'''
Convert any type of file to UTF-8 without BOM
and using universal endline by default.
Parameters
----------
file_path : string, file path.
universal_endline : boolean (True),
by default convert endlines to universal format.
'''
# Fix file path
file_path = os.path.realpath(os.path.expanduser(file_path))
# Read from file
file_open = open(file_path)
raw = file_open.read()
file_open.close()
# Decode
raw = raw.decode(chardet.detect(raw)['encoding'])
# Remove windows end line
if universal_endline:
raw = raw.replace('rn', 'n')
# Encode to UTF-8
raw = raw.encode('utf8')
# Remove BOM
if raw.startswith(codecs.BOM_UTF8):
raw = raw.replace(codecs.BOM_UTF8, '', 1)
# Write to file
file_open = open(file_path, 'w')
file_open.write(raw)
file_open.close()
return 0
Alt.Schlüssel
Ich habe diese Frage gefunden, weil ich Probleme mit habe configparser.ConfigParser().read(fp) beim Öffnen von Dateien mit UTF8-BOM-Header.
Für diejenigen, die nach einer Lösung suchen, um den Header zu entfernen, damit ConfigPhaser die Konfigurationsdatei öffnen kann, anstatt einen Fehler zu melden von: File contains no section headersöffnen Sie bitte die Datei wie folgt:
Sie müssen Ihre Datei zum Lesen plus Update öffnen, dh mit a
r+
Modus. Hinzufügenb
auch so, dass es auch unter Windows ohne irgendwelche komischen Zeilenenden funktioniert. Schließlich sollten Sie zum Anfang der Datei zurückkehren und sie am Ende abschneiden – siehe meine aktualisierte Antwort.– Martin Geisler
17. Januar 12 um 21:58 Uhr