किसी भी फ़ाइल के एन्कोडिंग को खोजने का प्रभावी तरीका


115

हाँ एक सबसे लगातार सवाल है, और यह मामला मेरे लिए अस्पष्ट है और चूंकि मुझे इसके बारे में ज्यादा जानकारी नहीं है।

लेकिन मैं एनकोडिंग फ़ाइलों को खोजने के लिए एक बहुत ही सटीक तरीका चाहूंगा। तो नोटपैड ++ जितना सटीक है।



कौन सा एनकोडिंग? UTF-8 बनाम UTF-16, बड़ा बनाम छोटा एंडियन? या क्या आप पुराने MSDos कोडप्स, जैसे कि शिफ्ट-JIS या सिरिलिक आदि की बात कर रहे हैं?
dthorpe 20

एक और संभावित डुप्लिकेट: stackoverflow.com/questions/436220/…
Oded

@Oded: उद्धरण "getEncoding () विधि एन्कोडिंग को लौटाएगा जो स्ट्रीम के लिए स्थापित किया गया था (JavaDoc पढ़ें)। यह आपके लिए एन्कोडिंग का अनुमान नहीं लगाएगा।"
फाबियो एंट्यूस

2
कुछ पृष्ठभूमि पढ़ने के लिए, joelonsoftware.com/articles/Unicode.html एक अच्छा पढ़ा है। यदि पाठ के बारे में आपको एक बात पता होनी चाहिए, तो यह है कि सादे पाठ जैसी कोई चीज नहीं है।
मार्टिज़न

जवाबों:


155

StreamReader.CurrentEncodingसंपत्ति शायद ही कभी सही पाठ फ़ाइल मेरे लिए एन्कोडिंग देता है। मुझे एक फाइल के एंडियननेस को निर्धारित करने में अधिक सफलता मिली है, इसके बाइट ऑर्डर मार्क (BOM) का विश्लेषण करके। यदि फ़ाइल में BOM नहीं है, तो यह फ़ाइल के एन्कोडिंग को निर्धारित नहीं कर सकता है।

* UTF-32LE का पता लगाने और UTF-32BE के लिए सही एन्कोडिंग वापस करने के लिए 4/08/2020 अपडेट किया गया

/// <summary>
/// Determines a text file's encoding by analyzing its byte order mark (BOM).
/// Defaults to ASCII when detection of the text file's endianness fails.
/// </summary>
/// <param name="filename">The text file to analyze.</param>
/// <returns>The detected encoding.</returns>
public static Encoding GetEncoding(string filename)
{
    // Read the BOM
    var bom = new byte[4];
    using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
    {
        file.Read(bom, 0, 4);
    }

    // Analyze the BOM
    if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7;
    if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
    if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0 && bom[3] == 0) return Encoding.UTF32; //UTF-32LE
    if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
    if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
    if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return new UTF32Encoding(true, true);  //UTF-32BE

    // We actually have no idea what the encoding is if we reach this point, so
    // you may wish to return null instead of defaulting to ASCII
    return Encoding.ASCII;
}

3
+1। इसने मेरे लिए भी काम किया (जबकि डिटेक्ट एनकोडिंगफ्रोमबायट ऑडरमैकर्स ने नहीं किया था)। मैंने IOException से बचने के लिए "नई FileStream (फ़ाइल नाम, FileMode.Open, FileAccess.Read)" का उपयोग किया क्योंकि फ़ाइल केवल पढ़ी जाती है।
पॉलीफुन

56
UTF-8 फाइलें BOM के बिना हो सकती हैं, इस मामले में यह ASCII को गलत तरीके से लौटाएगी।
user626528

3
यह उत्तर गलत है। को देखते हुए संदर्भ स्रोत के लिए StreamReader, कि कार्यान्वयन अधिक लोग क्या चाहते हैं जाएगा। वे मौजूदा Encoding.Unicodeवस्तुओं का उपयोग करने के बजाय नए एनकोडिंग बनाते हैं, इसलिए समानता की जांच विफल हो जाएगी (जो शायद ही कभी भी हो सकता है क्योंकि, उदाहरण के लिए, Encoding.UTF8विभिन्न वस्तुओं को वापस कर सकता है), लेकिन यह (1) वास्तव में अजीब यूटीएफ -7 प्रारूप का उपयोग नहीं करता है, (2) कोई बीओएम नहीं मिलने पर यूटीएफ -8 में चूक, और (3) एक अलग डिफ़ॉल्ट एन्कोडिंग का उपयोग करने के लिए ओवरराइड किया जा सकता है।
हैंगर

2
मुझे नई स्ट्रीम-राइडर (फ़ाइल नाम, सत्य) के साथ बेहतर सफलता मिली थी। समांतर ईन्कोडिंग
बेनोइट

4
कोड में एक मौलिक त्रुटि है; जब आप बड़े-एंडियन UTF32 हस्ताक्षर ( 00 00 FE FF) का पता लगाते हैं , तो आप सिस्टम-प्रदान लौटाते हैं Encoding.UTF32, जो थोड़ा-सा एंडियन एन्कोडिंग है (जैसा कि यहां बताया गया है )। और यह भी, जैसा कि @Nyerguds द्वारा नोट किया गया है, आप अभी भी UTF32LE की तलाश नहीं कर रहे हैं, जिसमें हस्ताक्षर हैं FF FE 00 00( en.wikipedia.org/wiki/Byte_order_mark के अनुसार )। जैसा कि उस उपयोगकर्ता ने उल्लेख किया है, क्योंकि यह निर्वाह है, उस चेक को 2-बाइट चेक से पहले आना चाहिए।
ग्लेन स्लेडेन

44

StreamReaderकक्षा का उपयोग करते हुए, निम्नलिखित कोड मेरे लिए ठीक काम करता है :

  using (var reader = new StreamReader(fileName, defaultEncodingIfNoBom, true))
  {
      reader.Peek(); // you need this!
      var encoding = reader.CurrentEncoding;
  }

चाल Peekकॉल का उपयोग करने के लिए है , अन्यथा, .NET ने कुछ भी नहीं किया है (और यह प्रस्तावना, बीओएम नहीं पढ़ा है)। बेशक, अगर आप ReadXXXएन्कोडिंग की जांच करने से पहले किसी अन्य कॉल का उपयोग करते हैं, तो यह भी काम करता है।

यदि फ़ाइल में कोई BOM नहीं है, तो defaultEncodingIfNoBomएन्कोडिंग का उपयोग किया जाएगा। इस अधिभार विधि के बिना भी एक StreamReader है (इस मामले में, डिफ़ॉल्ट (ANSI) एन्कोडिंग को defaultEncodingIfNoBom के रूप में उपयोग किया जाएगा), लेकिन मैं आपको यह परिभाषित करने की सलाह देता हूं कि आप अपने संदर्भ में डिफ़ॉल्ट एन्कोडिंग को क्या मानते हैं।

मैंने इसे UTF8, UTF16 / Unicode (LE & BE) और UTF32 (LE & BE) के लिए BOM वाली फ़ाइलों के साथ सफलतापूर्वक परीक्षण किया है। यह UTF7 के लिए काम नहीं करता है।


मुझे वापस वही मिलता है जो डिफ़ॉल्ट एन्कोडिंग के रूप में सेट होता है। क्या मैं क्षणभंगुर याद आ रही है?
राम

1
@DRAM - यह अगर फाइल नहीं बीओएम है हो सकता है
साइमन Mourier

धन्यवाद @Simon Mourier। मुझे उम्मीद है कि मेरी पीडीएफ / किसी भी फ़ाइल में बम नहीं होगा। यह लिंक stackoverflow.com/questions/4520184/… बिना बम के पता लगाने की कोशिश करने वाले के लिए मददगार हो सकता है।
राम

1
पावरशेल में मुझे $ Reader.close () चलाना था, अन्यथा इसे लेखन से बंद कर दिया गया। foreach($filename in $args) { $reader = [System.IO.StreamReader]::new($filename, [System.Text.Encoding]::default,$true); $peek = $reader.Peek(); $reader.currentencoding | select bodyname,encodingname; $reader.close() }
js2010

1
@SimonMourier यह काम नहीं करता है यदि फ़ाइल की एन्कोडिंग हैUTF-8 without BOM
ओज़कान

11

मैं निम्नलिखित चरणों की कोशिश करूँगा:

1) बाइट ऑर्डर मार्क है या नहीं, इसकी जांच करें

2) जांचें कि क्या फ़ाइल वैध UTF8 है

3) स्थानीय "ANSI" कोडपेज का उपयोग करें (ANSI Microsoft इसे परिभाषित करता है)

चरण 2 काम करता है क्योंकि अन्य गैर- ASCII अनुक्रमिकों में अन्य UTF8 UTF8 मान्य नहीं हैं।


यह अधिक सही उत्तर की तरह लगता है, क्योंकि दूसरा उत्तर मेरे लिए काम नहीं करता है। एक File.OpenRead और .Read-ing को फ़ाइल के पहले कुछ बाइट्स के साथ कर सकता है।
user420667

1
चरण 2 बिट पैटर्न की जांच करने के लिए प्रोग्रामिंग कार्य का एक पूरा गुच्छा है, हालांकि।
Nyerguds

1
मुझे यकीन नहीं है कि डिकोडिंग वास्तव में अपवादों को फेंकता है, या अगर यह सिर्फ अपरिचित अनुक्रमों को '' के साथ बदलता है। मैं वैसे भी थोड़ा पैटर्न चेकिंग क्लास लिखने के साथ गया था।
Nyerguds

3
जब आप एक उदाहरण बनाते हैं, तो आप Utf8Encodingएक अतिरिक्त पैरामीटर में पारित कर सकते हैं जो यह निर्धारित करता है कि क्या अपवाद को फेंक दिया जाना चाहिए या यदि आप मूक डेटा भ्रष्टाचार पसंद करते हैं।
कोडइन्चौस

1
मुझे यह उत्तर पसंद है। अधिकांश एन्कोडिंग (संभवतः आपके उपयोग के मामलों में से 99% की तरह) या तो UTF-8 या ANSI (विंडोज कोडपेज 1252) होगी। यदि एन्कोडिंग विफल हो गया है, तो आप यह जांच सकते हैं कि स्ट्रिंग में प्रतिस्थापन चरित्र (0xFFFD) है या नहीं।
१ze

10

इसे देखो।

UDE

यह मोज़िला यूनिवर्सल चार्सेट डिटेक्टर का एक बंदरगाह है और आप इसे इस तरह से उपयोग कर सकते हैं ...

public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}

आपको पता होना चाहिए कि UDE GPL है
lindexi

ठीक है अगर आप लाइसेंस के बारे में चिंतित हैं तो आप इस का उपयोग कर सकते हैं। MIT के रूप में लाइसेंस प्राप्त है और आप इसे ओपन सोर्स और क्लोज्ड सोर्स सॉफ्टवेयर दोनों के लिए उपयोग कर सकते हैं। nuget.org/packages/SimpleHelpers.FileEncoding
एलेक्सी

लाइसेंस GPL विकल्प के साथ MPL है। The library is subject to the Mozilla Public License Version 1.1 (the "License"). Alternatively, it may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL").
jbtule

ऐसा प्रतीत होता है कि यह कांटा वर्तमान में सबसे अधिक सक्रिय है और इसमें एक यूजीनेट पैकेज UDE.Netstandard है। github.com/yinyue200/ude
jbtule

बहुत उपयोगी पुस्तकालय, बहुत सारे अलग-अलग और असामान्य एन्कोडिंग के साथ! टैंक!
mshakurov

6

@CodesInChaos द्वारा प्रस्तावित चरणों के लिए कार्यान्वयन विवरण प्रदान करना:

1) बाइट ऑर्डर मार्क है या नहीं, इसकी जांच करें

2) जांचें कि क्या फ़ाइल वैध UTF8 है

3) स्थानीय "ANSI" कोडपेज का उपयोग करें (ANSI Microsoft इसे परिभाषित करता है)

चरण 2 काम करता है क्योंकि अधिकांश गैर-ASCII अनुक्रमिकों में अन्य अनुक्रमित हैं जो UTF8 मान्य UTF8 नहीं हैं। https://stackoverflow.com/a/4522251/867248 अधिक विवरण में रणनीति की व्याख्या करता है।

using System; using System.IO; using System.Text;

// Using encoding from BOM or UTF8 if no BOM found,
// check if the file is valid, by reading all lines
// If decoding fails, use the local "ANSI" codepage

public string DetectFileEncoding(Stream fileStream)
{
    var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
    using (var reader = new StreamReader(fileStream, Utf8EncodingVerifier,
           detectEncodingFromByteOrderMarks: true, leaveOpen: true, bufferSize: 1024))
    {
        string detectedEncoding;
        try
        {
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
            }
            detectedEncoding = reader.CurrentEncoding.BodyName;
        }
        catch (Exception e)
        {
            // Failed to decode the file using the BOM/UT8. 
            // Assume it's local ANSI
            detectedEncoding = "ISO-8859-1";
        }
        // Rewind the stream
        fileStream.Seek(0, SeekOrigin.Begin);
        return detectedEncoding;
   }
}


[Test]
public void Test1()
{
    Stream fs = File.OpenRead(@".\TestData\TextFile_ansi.csv");
    var detectedEncoding = DetectFileEncoding(fs);

    using (var reader = new StreamReader(fs, Encoding.GetEncoding(detectedEncoding)))
    {
       // Consume your file
        var line = reader.ReadLine();
        ...

धन्यवाद! यह मेरे लिए हल हो गया। लेकिन मैं reader.Peek() इसके बजाय का उपयोग करना पसंद करूंगा while (!reader.EndOfStream) { var line = reader.ReadLine(); }
हरिसन सिल्वा

reader.Peek()पूरी धारा नहीं पढ़ता। मैंने पाया कि बड़ी धाराओं के साथ, Peek()अपर्याप्त था। मैं reader.ReadToEndAsync()इसके बजाय इस्तेमाल किया।
गैरी पेंडलेबरी

और Utf8EncodingVerifier क्या है?
पीटर मूर

1
@PeterMoore utf8 के लिए इसकी एन्कोडिंग, var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());यह tryएक लाइन को पढ़ते समय ब्लॉक में उपयोग की जाती है । यदि एनकोडर प्रदान किए गए पाठ को पार्स करने में विफल रहता है (पाठ utf8 के साथ एन्कोडेड नहीं है), Utf8EncodingVerifier फेंक देगा। अपवाद पकड़ा गया है और हम तब जानते हैं कि पाठ utf8 नहीं है, और ISO-8859-1 के लिए डिफ़ॉल्ट है
ब्युमियर लेमीक्स

2

निम्नलिखित कोड मेरे पॉवर्सशेल कोड हैं, जो यह निर्धारित करते हैं कि कुछ सीपीपी या एच या एमएल फाइलें बिना बीओएम के आईएसओ-8859-1 (लैटिन -1) या यूटीएफ -8 के साथ एनकोडिंग हैं, अगर नहीं तो मान लें कि यह जीबी 1 बी 30 है। मैं फ्रांस में काम कर रहा एक चीनी हूं और MSVC फ्रेंच कंप्यूटर पर लैटिन -1 के रूप में बचाता है और चीनी कंप्यूटर पर जीबी के रूप में बचाता है, इसलिए यह मेरे सिस्टम और मेरे सहयोगियों के बीच स्रोत फ़ाइल का आदान-प्रदान करते समय मुझे एन्कोडिंग समस्या से बचने में मदद करता है।

रास्ता सरल है, यदि सभी वर्ण x00-x7E, ASCII, UTF-8 और लैटिन -1 के बीच हैं, तो सभी समान हैं, लेकिन यदि मैं UTF-8 द्वारा एक गैर ASCII फ़ाइल पढ़ता हूं, तो हम विशेष वर्ण शो करेंगे , इसलिए लैटिन -1 के साथ पढ़ने की कोशिश करें। लैटिन -1 में, \ x7F और \ xAF के बीच खाली है, जबकि GB x00-xFF के बीच पूर्ण का उपयोग करता है, अगर मुझे दोनों के बीच कोई मिला, तो यह लैटिन -1 नहीं है

कोड PowerShell में लिखा गया है, लेकिन .net का उपयोग करता है इसलिए इसे C # या F # में अनुवादित किया जाना आसान है

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in Get-ChildItem .\ -Recurse -include *.cpp,*.h, *.ml) {
    $openUTF = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::UTF8)
    $contentUTF = $openUTF.ReadToEnd()
    [regex]$regex = '�'
    $c=$regex.Matches($contentUTF).count
    $openUTF.Close()
    if ($c -ne 0) {
        $openLatin1 = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('ISO-8859-1'))
        $contentLatin1 = $openLatin1.ReadToEnd()
        $openLatin1.Close()
        [regex]$regex = '[\x7F-\xAF]'
        $c=$regex.Matches($contentLatin1).count
        if ($c -eq 0) {
            [System.IO.File]::WriteAllLines($i, $contentLatin1, $Utf8NoBomEncoding)
            $i.FullName
        } 
        else {
            $openGB = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('GB18030'))
            $contentGB = $openGB.ReadToEnd()
            $openGB.Close()
            [System.IO.File]::WriteAllLines($i, $contentGB, $Utf8NoBomEncoding)
            $i.FullName
        }
    }
}
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');

2

.NET बहुत उपयोगी नहीं है, लेकिन आप निम्नलिखित एल्गोरिथ्म की कोशिश कर सकते हैं:

  1. BOM (बाइट ऑर्डर मार्क) द्वारा एन्कोडिंग को खोजने का प्रयास करें ... बहुत संभव नहीं है कि पाया जाए
  2. विभिन्न एन्कोडिंग में पार्स करने का प्रयास करें

यहाँ कॉल है:

var encoding = FileHelper.GetEncoding(filePath);
if (encoding == null)
    throw new Exception("The file encoding is not supported. Please choose one of the following encodings: UTF8/UTF7/iso-8859-1");

यहाँ कोड है:

public class FileHelper
{
    /// <summary>
    /// Determines a text file's encoding by analyzing its byte order mark (BOM) and if not found try parsing into diferent encodings       
    /// Defaults to UTF8 when detection of the text file's endianness fails.
    /// </summary>
    /// <param name="filename">The text file to analyze.</param>
    /// <returns>The detected encoding or null.</returns>
    public static Encoding GetEncoding(string filename)
    {
        var encodingByBOM = GetEncodingByBOM(filename);
        if (encodingByBOM != null)
            return encodingByBOM;

        // BOM not found :(, so try to parse characters into several encodings
        var encodingByParsingUTF8 = GetEncodingByParsing(filename, Encoding.UTF8);
        if (encodingByParsingUTF8 != null)
            return encodingByParsingUTF8;

        var encodingByParsingLatin1 = GetEncodingByParsing(filename, Encoding.GetEncoding("iso-8859-1"));
        if (encodingByParsingLatin1 != null)
            return encodingByParsingLatin1;

        var encodingByParsingUTF7 = GetEncodingByParsing(filename, Encoding.UTF7);
        if (encodingByParsingUTF7 != null)
            return encodingByParsingUTF7;

        return null;   // no encoding found
    }

    /// <summary>
    /// Determines a text file's encoding by analyzing its byte order mark (BOM)  
    /// </summary>
    /// <param name="filename">The text file to analyze.</param>
    /// <returns>The detected encoding.</returns>
    private static Encoding GetEncodingByBOM(string filename)
    {
        // Read the BOM
        var byteOrderMark = new byte[4];
        using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
        {
            file.Read(byteOrderMark, 0, 4);
        }

        // Analyze the BOM
        if (byteOrderMark[0] == 0x2b && byteOrderMark[1] == 0x2f && byteOrderMark[2] == 0x76) return Encoding.UTF7;
        if (byteOrderMark[0] == 0xef && byteOrderMark[1] == 0xbb && byteOrderMark[2] == 0xbf) return Encoding.UTF8;
        if (byteOrderMark[0] == 0xff && byteOrderMark[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
        if (byteOrderMark[0] == 0xfe && byteOrderMark[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
        if (byteOrderMark[0] == 0 && byteOrderMark[1] == 0 && byteOrderMark[2] == 0xfe && byteOrderMark[3] == 0xff) return Encoding.UTF32;

        return null;    // no BOM found
    }

    private static Encoding GetEncodingByParsing(string filename, Encoding encoding)
    {            
        var encodingVerifier = Encoding.GetEncoding(encoding.BodyName, new EncoderExceptionFallback(), new DecoderExceptionFallback());

        try
        {
            using (var textReader = new StreamReader(filename, encodingVerifier, detectEncodingFromByteOrderMarks: true))
            {
                while (!textReader.EndOfStream)
                {                        
                    textReader.ReadLine();   // in order to increment the stream position
                }

                // all text parsed ok
                return textReader.CurrentEncoding;
            }
        }
        catch (Exception ex) { }

        return null;    // 
    }
}

1

सी # के लिए यहाँ देखो

https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx

string path = @"path\to\your\file.ext";

using (StreamReader sr = new StreamReader(path, true))
{
    while (sr.Peek() >= 0)
    {
        Console.Write((char)sr.Read());
    }

    //Test for the encoding after reading, or at least
    //after the first read.
    Console.WriteLine("The encoding used was {0}.", sr.CurrentEncoding);
    Console.ReadLine();
    Console.WriteLine();
}

0

यह उपयोगी हो सकता है

string path = @"address/to/the/file.extension";

using (StreamReader sr = new StreamReader(path))
{ 
    Console.WriteLine(sr.CurrentEncoding);                        
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.