모든 파일의 인코딩을 찾는 효과적인 방법


115

예는 가장 빈번한 질문입니다.이 문제는 저에게 모호하며 그것에 대해 많이 알지 못합니다.

하지만 파일 인코딩을 찾는 매우 정확한 방법을 원합니다. Notepad ++만큼 정확합니다.


1
Oded

어떤 인코딩? UTF-8 vs UTF-16, 빅 vs 리틀 엔디안? 아니면 shift-JIS 또는 Cyrillic 등과 같은 이전 MSDos 코드 페이지를 언급하고 있습니까?
dthorpe

또 다른 중복 가능성 : stackoverflow.com/questions/436220/…
Oded

@Oded : Quote "getEncoding () 메소드는 스트림에 대해 설정된 인코딩 (JavaDoc 읽기)을 반환합니다. 인코딩을 추측하지 않습니다."
Fábio Antunes

2
배경 자료를 읽으 려면 joelonsoftware.com/articles/Unicode.html을 읽어보십시오. 텍스트에 대해 알아야 할 것이 하나 있다면 일반 텍스트와 같은 것이 없다는 것입니다.
마티

답변:


155

StreamReader.CurrentEncoding속성은 나를 위해 올바른 텍스트 파일 인코딩을 거의 반환하지 않습니다. 파일의 바이트 순서 표시 (BOM)를 분석하여 파일의 엔디안을 결정하는 데 더 큰 성공을 거두었습니다. 파일에 BOM이 없으면 파일의 인코딩을 결정할 수 없습니다.

* UTF-32LE 감지를 포함하고 UTF-32BE에 대한 올바른 인코딩을 반환하도록 2020 년 4 월 8 일 업데이트 됨

/// <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. 이것은 나에게도 효과적이었습니다 (detectEncodingFromByteOrderMarks는 그렇지 않았습니다). 파일이 읽기 전용이기 때문에 IOException을 피하기 위해 "new FileStream (filename, FileMode.Open, FileAccess.Read)"를 사용했습니다.
Polyfun 2014-04-07

56
UTF-8 파일에는 BOM이 없을 수 있습니다.이 경우 ASCII가 잘못 반환됩니다.
user626528

3
이 대답은 틀 렸습니다. 에 대한 참조 소스 를 보면 StreamReader더 많은 사람들이 원하는 구현이 될 것입니다. 그들은 기존 Encoding.Unicode객체를 사용하는 대신 새로운 인코딩을 만들기 때문에 동등성 검사가 실패합니다 (예를 들어, Encoding.UTF8다른 객체를 반환 할 수 있기 때문에 어차피 거의 발생하지 않을 수 있음).하지만 (1) 정말 이상한 UTF-7 형식을 사용하지 않습니다. (2) BOM이없는 경우 기본값은 UTF-8이고 (3) 다른 기본 인코딩을 사용하도록 재정의 할 수 있습니다.
격납고

2
나는 .CurrentEncoding 새에서는 StreamReader (파일 이름, 진정한) 더 나은 성공을 거두었
벤와

4
코드에 근본적인 오류가 있습니다. 당신이 감지 될 때 빅 엔디안 UTF32의 서명을 ( 00 00 FE FF), 시스템 제공 반환 Encoding.UTF32하는 것입니다 리틀 엔디안 (언급 한 바와 같이 인코딩 여기를 ). 또한 @Nyerguds에서 언급했듯이 서명이있는 UTF32LE를 찾고 있지 않습니다 FF FE 00 00( en.wikipedia.org/wiki/Byte_order_mark 에 따라 ). 그 사용자가 언급했듯이, 그것이 포함되기 때문에 그 검사는 2 바이트 검사 전에 와야합니다.
Glenn Slayden 2018

44

다음 코드는 StreamReader클래스를 사용하여 잘 작동합니다 .

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

트릭은 Peek호출 을 사용하는 것입니다 . 그렇지 않으면 .NET이 아무 작업도 수행하지 않고 서문 인 BOM을 읽지 않았습니다. 물론 ReadXXX인코딩을 확인하기 전에 다른 호출 을 사용하는 경우 에도 작동합니다.

파일에 BOM이 없으면 defaultEncodingIfNoBom인코딩이 사용됩니다. 이 오버로드 메서드가없는 StreamReader도 있지만 (이 경우 기본 (ANSI) 인코딩이 defaultEncodingIfNoBom으로 사용됨) 컨텍스트에서 기본 인코딩을 고려하는 사항을 정의하는 것이 좋습니다.

UTF8, UTF16 / Unicode (LE & BE) 및 UTF32 (LE & BE) 용 BOM이있는 파일로 이것을 성공적으로 테스트했습니다. UTF7에서는 작동하지 않습니다.


기본 인코딩으로 설정된 것을 되돌립니다. 모 메팅을 놓칠 수 있을까요?

1
@DRAM - 파일이 더 BOM이없는 경우에 발생할 수 있습니다
사이먼 Mourier

@Simon Mourier에게 감사드립니다. 내 pdf / 모든 파일에 bom이 없을 것으로 예상합니다. 이 링크 stackoverflow.com/questions/4520184/… 는 bom없이 탐지하려는 사람에게 도움이 될 수 있습니다.

1
powershell에서 $ 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 21:53에

1
파일의 인코딩하는 경우 @SimonMourier이없는 작업을 수행UTF-8 without BOM
오즈 칸

11

다음 단계를 시도해 보겠습니다.

1) Byte Order Mark가 있는지 확인

2) 파일이 유효한 UTF8인지 확인

3) 로컬 "ANSI"코드 페이지 사용 (Microsoft에서 정의한 ANSI)

2 단계는 UTF8 이외의 코드 페이지에서 대부분의 비 ASCII 시퀀스가 ​​유효한 UTF8이 아니기 때문에 작동합니다.


다른 답변이 저에게 효과가 없기 때문에 이것은 더 정답 인 것 같습니다. File.OpenRead 및 .Read-ing으로 파일의 처음 몇 바이트를 읽을 수 있습니다.
user420667 2013-08-12

1
2 단계는 비트 패턴을 확인하기위한 프로그래밍 작업입니다.
Nyerguds

1
디코딩이 실제로 예외를 throw하는지 또는 인식되지 않는 시퀀스를 '?'로 대체하는지 확실하지 않습니다. 어쨌든 비트 패턴 검사 클래스를 작성했습니다.
Nyerguds

3
의 인스턴스를 만들 때 Utf8Encoding예외를 throw해야하는지 또는 자동 데이터 손상을 선호 하는지를 결정하는 추가 매개 변수를 전달할 수 있습니다.
CodesInChaos

1
나는이 대답을 좋아한다. 대부분의 인코딩 (예 : 99 %의 사용 사례)은 UTF-8 또는 ANSI (Windows 코드 페이지 1252)입니다. 문자열에 대체 문자 (0xFFFD)가 포함되어 있는지 확인하여 인코딩이 실패했는지 확인할 수 있습니다.
marsze

10

이것을 확인하십시오.

UDE

이것은 Mozilla Universal Charset Detector의 포트이며 다음과 같이 사용할 수 있습니다.

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 2017

라이센스가 걱정된다면이 라이센스를 사용할 수 있습니다. 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 19

이 포크는 현재 가장 활동적이고 너겟 패키지 UDE.Netstandard를 가지고있는 것으로 보입니다. github.com/yinyue200/ude
jbtule

다양하고 특이한 인코딩에 대처하는 매우 유용한 라이브러리! 탱크!
mshakurov

6

@CodesInChaos에서 제안한 단계에 대한 구현 세부 정보 제공 :

1) Byte Order Mark가 있는지 확인

2) 파일이 유효한 UTF8인지 확인

3) 로컬 "ANSI"코드 페이지 사용 (Microsoft에서 정의한 ANSI)

2 단계는 UTF8 이외의 코드 페이지에서 대부분의 비 ASCII 시퀀스가 ​​유효한 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(); }
Harison 실바

reader.Peek()전체 스트림을 읽지 않습니다. 나는 큰 개울에서는 Peek()부적절하다는 것을 알았습니다 . reader.ReadToEndAsync()대신 사용 했습니다.
Gary Pendlebury

그리고 Utf8EncodingVerifier는 무엇입니까?
Peter Moore

1
@PeterMoore utf8에 대한 인코딩으로, 라인을 읽을 때 블록 var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());에서 사용됩니다 try. 인코더가 제공된 텍스트를 구문 분석하지 못하면 (텍스트가 utf8로 인코딩되지 않음) Utf8EncodingVerifier가 발생합니다. 예외가 포착되고 텍스트가 utf8이 아니며 기본값은 ISO-8859-1
Berthier Lemieux

2

다음 코드는 일부 cpp 또는 h 또는 ml 파일이 BOM없이 ISO-8859-1 (Latin-1) 또는 UTF-8로 인코딩되는지 확인하는 Powershell 코드입니다. 둘 다 GB18030이라고 가정하지 않습니다. 저는 프랑스에서 일하는 중국인이고 MSVC는 프랑스 컴퓨터에서 Latin-1로 저장하고 중국 컴퓨터에서 GB로 저장하므로 시스템과 동료간에 소스 파일을 교환 할 때 인코딩 문제를 방지하는 데 도움이됩니다.

방법은 간단합니다. 모든 문자가 x00-x7E 사이에 있고 ASCII, UTF-8 및 Latin-1이 모두 동일하지만 ASCII가 아닌 파일을 UTF-8로 읽으면 특수 문자가 표시됩니다. , 그러니 Latin-1로 읽으십시오. Latin-1에서 \ x7F와 \ xAF 사이는 비어 있고 GB는 x00-xFF 사이에서 전체를 사용하므로 둘 사이에 있으면 Latin-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 (byte order mark)으로 인코딩을 찾으십시오.
  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

여기에서 C #을 찾으십시오.

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);                        
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.