Zwei Fragen hier. Ich habe eine Reihe von Dateien, die normalerweise UTF-8 mit Stückliste sind. Ich möchte sie (idealerweise vorhanden) ohne Stückliste in UTF-8 konvertieren. Es scheint, als codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)
würde man damit umgehen. Aber ich sehe keine wirklich guten Beispiele für die Verwendung. Wäre dies der beste Weg, um damit umzugehen?
source files:
Tue Jan 17$ file brh-m-157.json
brh-m-157.json: UTF-8 Unicode (with BOM) text
Es wäre auch ideal, wenn wir unterschiedliche Eingabecodierungen ohne explizites Wissen handhaben könnten (siehe ASCII und UTF-16). Es scheint, dass dies alles machbar sein sollte. Gibt es eine Lösung, die jede bekannte Python-Codierung und Ausgabe als UTF-8 ohne Stückliste verwenden kann?
1 vorgeschlagenen Sol'n von unten bearbeiten (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
Kurznachricht
In Kommentaren wird mir gesagt, dass der Fehler darin besteht, dass ich die Datei mit dem Modus 'rw' anstelle von 'r +' / 'r + b' öffne, sodass ich meine Frage eventuell erneut bearbeiten und den gelösten Teil entfernen sollte.
quelle
r+
Modus. Fügen Sieb
auch hinzu, damit es auch unter Windows funktioniert, ohne dass ein lustiges Line-Ending-Geschäft zustande kommt. Schließlich möchten Sie zum Anfang der Datei zurückkehren und sie am Ende abschneiden - siehe meine aktualisierte Antwort.Antworten:
Verwenden Sie einfach den Codec "utf-8-sig" :
fp = open("file.txt") s = fp.read() u = s.decode("utf-8-sig")
Das gibt Ihnen eine
unicode
Zeichenfolge ohne Stückliste. Sie können dann verwendens = u.encode("utf-8")
um einen normalen UTF-8-codierten String wieder zu erhalten
s
. Wenn Ihre Dateien groß sind, sollten Sie vermeiden, sie alle in den Speicher einzulesen. 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 Block und schreibt ihn 3 Byte früher als dort in die Datei. Die Datei wird direkt neu geschrieben. Eine einfachere Lösung besteht darin, die kürzere Datei wie die Antwort von newtover in eine neue Datei zu schreiben . Das wäre einfacher, würde aber für kurze Zeit den doppelten Speicherplatz belegen.
Wenn Sie die Codierung erraten möchten, 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 dekodiert, daher versuchen wir es zuerst mit UTF-8. Wenn dies fehlschlägt, versuchen wir es mit UTF-16. Schließlich verwenden wir Latin-1 - dies funktioniert immer, da alle 256 Bytes zulässige Werte in Latin-1 sind.
None
In diesem Fall möchten Sie möglicherweise stattdessen zurückkehren, da dies wirklich ein Fallback ist und Ihr Code dies möglicherweise sorgfältiger behandeln möchte (wenn dies möglich ist).quelle
In Python 3 ist es ganz einfach: Lesen Sie die Datei und schreiben Sie sie mit
utf-8
Codierung neu:s = open(bom_file, mode='r', encoding='utf-8-sig').read() open(bom_file, mode='w', encoding='utf-8').write(s)
quelle
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)
quelle
header = header[3:] if header[0:3] == codecs.BOM_UTF8 else header
Dies ist meine Implementierung, um jede Art von Codierung ohne Stückliste in UTF-8 zu konvertieren und Windows-Enlines durch ein universelles 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('\r\n', '\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
quelle
Ich habe diese Frage gefunden, weil ich Probleme
configparser.ConfigParser().read(fp)
beim Öffnen von Dateien mit dem UTF8-Stücklistenheader habe.Wenn Sie nach einer Lösung suchen, um den Header zu entfernen, damit ConfigPhaser die Konfigurationsdatei öffnen kann, anstatt einen Fehler von: zu melden
File contains no section headers
, öffnen Sie die Datei wie folgt:configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")
Dies kann Ihnen viel Aufwand ersparen, da das Entfernen des Stücklisten-Headers der Datei nicht erforderlich ist.
(Ich weiß, das klingt nicht verwandt, aber hoffentlich könnte dies Menschen helfen, die wie ich kämpfen.)
quelle
Sie können Codecs verwenden.
import codecs with open("test.txt",'r') as filehandle: content = filehandle.read() if content[:3] == codecs.BOM_UTF8: content = content[3:] print content.decode("utf-8")
quelle