Tutaj dwa pytania. Mam zestaw plików, które zwykle są w formacie UTF-8 z BOM. Chciałbym je przekonwertować (najlepiej na miejscu) do UTF-8 bez BOM. Wygląda na to, codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)
że poradzi sobie z tym. Ale tak naprawdę nie widzę dobrych przykładów użycia. Czy byłby to najlepszy sposób na rozwiązanie tego problemu?
source files:
Tue Jan 17$ file brh-m-157.json
brh-m-157.json: UTF-8 Unicode (with BOM) text
Ponadto byłoby idealnie, gdybyśmy mogli obsłużyć różne kodowanie danych wejściowych bez wyraźnej wiedzy (patrz ASCII i UTF-16). Wydaje się, że to wszystko powinno być wykonalne. Czy istnieje rozwiązanie, które może przyjąć dowolne znane kodowanie w Pythonie i wyprowadzać jako UTF-8 bez BOM?
edytuj 1 proponowane rozwiązanie od dołu (dzięki!)
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)
To daje mi następujący błąd:
IOError: [Errno 9] Bad file descriptor
wiadomosci
W komentarzach powiedziano mi, że błąd polega na tym, że otwieram plik w trybie „rw” zamiast „r +” / „r + b”, więc powinienem w końcu ponownie edytować moje pytanie i usunąć rozwiązaną część.
źródło
r+
. W trybie. Dodajb
też, aby działał również w systemie Windows bez żadnych zabawnych zakończeń linii. Na koniec zechcesz wrócić do początku pliku i skrócić go na końcu - zobacz moją zaktualizowaną odpowiedź.Odpowiedzi:
Po prostu użyj kodeka „utf-8-sig” :
fp = open("file.txt") s = fp.read() u = s.decode("utf-8-sig")
To daje ci
unicode
ciąg bez BOM. Następnie możesz użyćs = u.encode("utf-8")
aby odzyskać normalny łańcuch zakodowany w UTF-8
s
. Jeśli twoje pliki są duże, powinieneś unikać wczytywania ich wszystkich do pamięci. BOM ma po prostu trzy bajty na początku pliku, więc możesz użyć tego kodu, aby usunąć je z pliku: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()
Otwiera plik, odczytuje fragment i zapisuje go do pliku 3 bajty wcześniej niż miejsce, w którym go odczytuje. Plik jest przepisywany w miejscu. Łatwiejszym rozwiązaniem jest zapisanie krótszego pliku do nowego pliku, takiego jak odpowiedź newtover . Byłoby to prostsze, ale przez krótki czas zajmowałoby dwukrotnie więcej miejsca na dysku.
Jeśli chodzi o zgadywanie kodowania, możesz po prostu zapętlić kodowanie od najbardziej do najmniej konkretnego:
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
Plik zakodowany w UTF-16 nie zostanie zdekodowany jako UTF-8, więc najpierw spróbujemy z UTF-8. Jeśli to się nie powiedzie, próbujemy z UTF-16. Na koniec używamy Latin-1 - to zawsze będzie działać, ponieważ wszystkie 256 bajtów to legalne wartości w Latin-1. W
None
tym przypadku możesz chcieć zwrócić zamiast tego, ponieważ jest to naprawdę rezerwa i Twój kod może chcieć obsługiwać to ostrożniej (jeśli to możliwe).źródło
W Pythonie 3 jest to całkiem proste: przeczytaj plik i przepisz go z
utf-8
kodowaniem:s = open(bom_file, mode='r', encoding='utf-8-sig').read() open(bom_file, mode='w', encoding='utf-8').write(s)
źródło
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)
źródło
header = header[3:] if header[0:3] == codecs.BOM_UTF8 else header
Oto moja implementacja do konwersji dowolnego rodzaju kodowania do UTF-8 bez BOM i zastąpienia powiększania okien przez format uniwersalny:
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
źródło
Znalazłem to pytanie, ponieważ mam problem z
configparser.ConfigParser().read(fp)
otwieraniem plików z nagłówkiem BOM UTF8.Dla tych, którzy szukają rozwiązania w celu usunięcia nagłówka, aby ConfigPhaser mógł otworzyć plik konfiguracyjny zamiast zgłaszać błąd:,
File contains no section headers
proszę otworzyć plik w następujący sposób:configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")
Może to zaoszczędzić mnóstwo wysiłku, sprawiając, że usunięcie nagłówka BOM pliku będzie niepotrzebne.
(Wiem, że to brzmi niepowiązane, ale mam nadzieję, że może to pomóc ludziom walczącym tak jak ja).
źródło
Możesz używać kodeków.
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")
źródło