यूटीएफ -8 को बीओएम के साथ यूटीएफ -8 में अजगर के बिना बीओएम में परिवर्तित करें


82

दो सवाल यहां। मेरे पास फाइलों का एक सेट है जो आमतौर पर बीओएम के साथ यूटीएफ -8 हैं। मैं उन्हें (आदर्श रूप में जगह में) UTF-8 में बिना BOM के परिवर्तित करना चाहूंगा। ऐसा लगता है कि codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)यह संभाल लेंगे। लेकिन मैं वास्तव में उपयोग पर कोई अच्छा उदाहरण नहीं देखता। क्या इससे निपटने का सबसे अच्छा तरीका होगा?

source files:
Tue Jan 17$ file brh-m-157.json 
brh-m-157.json: UTF-8 Unicode (with BOM) text

इसके अलावा, यह आदर्श होगा यदि हम स्पष्ट रूप से जानने वाले विभिन्न इनपुट एन्कोडिंग को संभाल सकते हैं (एएससीआईआई और यूटीएफ -16 देखें)। ऐसा लगता है जैसे यह सब संभव होना चाहिए। क्या कोई ऐसा समाधान है जो किसी भी ज्ञात पायथन एन्कोडिंग और आउटपुट को बिना BOM के UTF-8 के रूप में ले सकता है?

नीचे से 1 प्रस्तावित sol'n संपादित करें (धन्यवाद!)

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)

यह मुझे निम्नलिखित त्रुटि देता है:

IOError: [Errno 9] Bad file descriptor

न्यूजफ्लैश

मुझे टिप्पणियों में कहा जा रहा है कि गलती मैं 'r +' / 'r + b' के बजाय मोड 'rw' के साथ फाइल को खोलने की है, इसलिए मुझे अंततः अपने प्रश्न को फिर से संपादित करना चाहिए और हल किए गए भाग को निकालना चाहिए।


2
आपको एक r+मोड के साथ, प्लस अपडेट पढ़ने के लिए अपनी फ़ाइल खोलने की आवश्यकता है। bइतना भी जोड़ दें कि यह विंडोज पर काम करेगा और साथ ही बिना किसी मजेदार लाइन एंड बिजनेस के। अंत में, आप फ़ाइल की शुरुआत में वापस जाना चाहते हैं और इसे अंत में काट देंगे - कृपया मेरा अद्यतन उत्तर देखें।
मार्टिन गेइस्लर

जवाबों:


125

बस "utf-8-sig" कोडेक का उपयोग करें :

fp = open("file.txt")
s = fp.read()
u = s.decode("utf-8-sig")

यह आपको unicodeBOM के बिना एक स्ट्रिंग देता है । तब आप उपयोग कर सकते हैं

s = u.encode("utf-8")

एक सामान्य UTF-8 एनकोडेड स्ट्रिंग वापस पाने के लिए s। यदि आपकी फाइलें बड़ी हैं, तो आपको उन सभी को मेमोरी में पढ़ने से बचना चाहिए। BOM फ़ाइल की शुरुआत में केवल तीन बाइट्स है, इसलिए आप इस कोड का उपयोग करके उन्हें फ़ाइल से निकाल सकते हैं:

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()

यह फ़ाइल को खोलता है, एक चंक पढ़ता है, और इसे फ़ाइल 3 बाइट्स से पहले लिखता है जहां उसने इसे पढ़ा था। फ़ाइल को फिर से लिखा गया है। जैसा कि आसान समाधान नई फ़ाइल के जवाब की तरह छोटी फ़ाइल को लिखना है । यह सरल होगा, लेकिन छोटी अवधि के लिए डिस्क स्थान का दोगुना उपयोग करें।

एन्कोडिंग का अनुमान लगाने के लिए, फिर आप एन्कोडिंग के माध्यम से सबसे कम से कम विशिष्ट तक लूप कर सकते हैं:

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

एक UTF-16 एन्कोडेड फ़ाइल को UTF-8 के रूप में डिकोड नहीं किया जाएगा, इसलिए हम पहले UTF-8 के साथ प्रयास करें। यदि वह विफल रहता है, तो हम UTF-16 के साथ प्रयास करते हैं। अंत में, हम लैटिन -1 का उपयोग करते हैं - यह हमेशा काम करेगा क्योंकि सभी 256 बाइट्स लैटिन -1 में कानूनी मूल्य हैं। आप Noneइस मामले में इसके बजाय वापस आना चाह सकते हैं क्योंकि यह वास्तव में गिरावट है और आपका कोड इसे अधिक सावधानी से संभालना चाहता है (यदि यह हो सकता है)।


हम्म, मैंने नमूना कोड के साथ # 1 को संपादित करने में प्रश्न को अपडेट किया लेकिन एक खराब फाइल डिस्क्रिप्टर मिला। किसी भी मदद के लिए thx। इसका पता लगाने की कोशिश की जा रही है।
टिमपोन

64

पायथन 3 में यह काफी आसान है: फ़ाइल को पढ़ें और utf-8एन्कोडिंग के साथ इसे फिर से लिखें:

s = open(bom_file, mode='r', encoding='utf-8-sig').read()
open(bom_file, mode='w', encoding='utf-8').write(s)

3
इस विषय पर वेब में सर्वश्रेष्ठ उत्तर। बस utf-8-sig का उपयोग करें।
QtRoS

6
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)

क्या आप बता सकते हैं कि यह कोड कैसे काम करता है? $ remove_bom.py <input.txt> output.txt क्या मैं सही हूं?
गनयूस

@ गुगनयूस, हाँ, बिल्कुल
newtover

1
मैंने अभी जोड़ाheader = header[3:] if header[0:3] == codecs.BOM_UTF8 else header
चिनमय

5

यह बिना बीओएम के किसी भी प्रकार के एन्कोडिंग को यूटीएफ -8 में बदलने के लिए मेरा कार्यान्वयन है और सार्वभौमिक प्रारूप द्वारा खिड़कियों की सुर्खियों को बदलना है:

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

3

मुझे यह सवाल इसलिए मिला क्योंकि configparser.ConfigParser().read(fp)UTF8 BOM हैडर के साथ फाइल खोलने में परेशानी हो रही है ।

उन लोगों के लिए जो हेडर को हटाने के लिए एक समाधान की तलाश कर रहे हैं ताकि कॉन्फ़िग्रेसर की त्रुटि की रिपोर्ट करने के बजाय कॉन्फ़िग फ़ाइल को खोल सके: File contains no section headersकृपया निम्न की तरह फ़ाइल खोलें:

configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")

यह आपको फ़ाइल के BOM हेडर को हटाकर अनावश्यक प्रयास से बचा सकता है।

(मुझे पता है कि यह असंबंधित लगता है, लेकिन उम्मीद है कि यह मेरे जैसे संघर्षरत लोगों की मदद कर सकता है।)


1
जैसा कि मैं पहली कोशिश के साथ काम कर रहा था - सिवाय इसके -> यह भी UTF-8 को खोलता है "BOM नहीं" समस्याओं के बिना एन्कोडेड फाइलें
फ्लिपकार्ट

2

आप कोडेक्स का उपयोग कर सकते हैं।

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")

बिल्कुल नहीं प्रयोग करने योग्य snipplet (filehandle भी codecs.BOM_UTF8 में सिंटेक्स त्रुटि वापसी?)
मैक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.