예는 가장 빈번한 질문입니다.이 문제는 저에게 모호하며 그것에 대해 많이 알지 못합니다.
하지만 파일 인코딩을 찾는 매우 정확한 방법을 원합니다. Notepad ++만큼 정확합니다.
예는 가장 빈번한 질문입니다.이 문제는 저에게 모호하며 그것에 대해 많이 알지 못합니다.
하지만 파일 인코딩을 찾는 매우 정확한 방법을 원합니다. Notepad ++만큼 정확합니다.
답변:
이 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;
}
StreamReader
더 많은 사람들이 원하는 구현이 될 것입니다. 그들은 기존 Encoding.Unicode
객체를 사용하는 대신 새로운 인코딩을 만들기 때문에 동등성 검사가 실패합니다 (예를 들어, Encoding.UTF8
다른 객체를 반환 할 수 있기 때문에 어차피 거의 발생하지 않을 수 있음).하지만 (1) 정말 이상한 UTF-7 형식을 사용하지 않습니다. (2) BOM이없는 경우 기본값은 UTF-8이고 (3) 다른 기본 인코딩을 사용하도록 재정의 할 수 있습니다.
00 00 FE FF
), 시스템 제공 반환 Encoding.UTF32
하는 것입니다 리틀 엔디안 (언급 한 바와 같이 인코딩 여기를 ). 또한 @Nyerguds에서 언급했듯이 서명이있는 UTF32LE를 찾고 있지 않습니다 FF FE 00 00
( en.wikipedia.org/wiki/Byte_order_mark 에 따라 ). 그 사용자가 언급했듯이, 그것이 포함되기 때문에 그 검사는 2 바이트 검사 전에 와야합니다.
다음 코드는 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에서는 작동하지 않습니다.
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() }
UTF-8 without BOM
다음 단계를 시도해 보겠습니다.
1) Byte Order Mark가 있는지 확인
2) 파일이 유효한 UTF8인지 확인
3) 로컬 "ANSI"코드 페이지 사용 (Microsoft에서 정의한 ANSI)
2 단계는 UTF8 이외의 코드 페이지에서 대부분의 비 ASCII 시퀀스가 유효한 UTF8이 아니기 때문에 작동합니다.
Utf8Encoding
예외를 throw해야하는지 또는 자동 데이터 손상을 선호 하는지를 결정하는 추가 매개 변수를 전달할 수 있습니다.
이것을 확인하십시오.
이것은 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.");
}
}
}
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").
@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(); }
reader.Peek()
전체 스트림을 읽지 않습니다. 나는 큰 개울에서는 Peek()
부적절하다는 것을 알았습니다 . reader.ReadToEndAsync()
대신 사용 했습니다.
var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
에서 사용됩니다 try
. 인코더가 제공된 텍스트를 구문 분석하지 못하면 (텍스트가 utf8로 인코딩되지 않음) Utf8EncodingVerifier가 발생합니다. 예외가 포착되고 텍스트가 utf8이 아니며 기본값은 ISO-8859-1
다음 코드는 일부 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');
.NET은 그다지 도움이되지 않지만 다음 알고리즘을 시도해 볼 수 있습니다.
전화는 다음과 같습니다.
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; //
}
}
여기에서 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();
}