Sim é a pergunta mais frequente, e este assunto é vago para mim e já que não sei muito sobre ele.
Mas eu gostaria de uma maneira muito precisa de encontrar a codificação de arquivos. Tão preciso quanto o Notepad ++ é.
Sim é a pergunta mais frequente, e este assunto é vago para mim e já que não sei muito sobre ele.
Mas eu gostaria de uma maneira muito precisa de encontrar a codificação de arquivos. Tão preciso quanto o Notepad ++ é.
Respostas:
A StreamReader.CurrentEncoding
propriedade raramente retorna a codificação de arquivo de texto correta para mim. Tive maior sucesso ao determinar o endianness de um arquivo, analisando sua marca de ordem de byte (BOM). Se o arquivo não tiver um BOM, isso não poderá determinar a codificação do arquivo.
* ATUALIZADO em 08/04/2020 para incluir detecção de UTF-32LE e retornar a codificação correta para UTF-32BE
/// <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
, que a implementação é o que mais pessoas vão querer. Eles fazem novas codificações em vez de usar os Encoding.Unicode
objetos existentes , então as verificações de igualdade irão falhar (o que pode acontecer raramente porque, por exemplo, Encoding.UTF8
pode retornar objetos diferentes), mas (1) não usa o formato UTF-7 realmente estranho, (2) o padrão é UTF-8 se nenhum BOM for encontrado e (3) pode ser substituído para usar uma codificação padrão diferente.
00 00 FE FF
Encoding.UTF32
FF FE 00 00
O código a seguir funciona bem para mim, usando a StreamReader
classe:
using (var reader = new StreamReader(fileName, defaultEncodingIfNoBom, true))
{
reader.Peek(); // you need this!
var encoding = reader.CurrentEncoding;
}
O truque é usar a Peek
chamada, caso contrário, o .NET não fez nada (e não leu o preâmbulo, o BOM). Claro, se você usar qualquer outra ReadXXX
chamada antes de verificar a codificação, ela também funcionará.
Se o arquivo não tiver BOM, a defaultEncodingIfNoBom
codificação será usada. Há também um StreamReader sem esse método de sobrecarga (neste caso, a codificação Padrão (ANSI) será usada como defaultEncodingIfNoBom), mas recomendo definir o que você considera a codificação padrão em seu contexto.
Eu testei isso com sucesso com arquivos com BOM para UTF8, UTF16 / Unicode (LE & BE) e UTF32 (LE & BE). Não funciona para 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
Eu tentaria as seguintes etapas:
1) Verifique se há uma Marca de Ordem de Byte
2) Verifique se o arquivo é UTF8 válido
3) Use a página de código "ANSI" local (ANSI como a Microsoft define)
A etapa 2 funciona porque a maioria das sequências não ASCII em páginas de código diferentes de UTF8 não são UTF8 válidas.
Utf8Encoding
você pode passar um parâmetro extra que determina se uma exceção deve ser lançada ou se você prefere corrupção de dados silenciosa.
Verifique isso.
Este é um porte do Mozilla Universal Charset Detector e você pode usá-lo assim ...
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").
Fornecendo os detalhes de implementação para as etapas propostas por @CodesInChaos:
1) Verifique se há uma Marca de Ordem de Byte
2) Verifique se o arquivo é UTF8 válido
3) Use a página de código "ANSI" local (ANSI como a Microsoft define)
A etapa 2 funciona porque a maioria das sequências não ASCII em páginas de código diferentes de UTF8 não são UTF8 válidas. https://stackoverflow.com/a/4522251/867248 explica a tática em mais detalhes.
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()
vez de while (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
não lê o fluxo inteiro. Descobri que, com riachos maiores, Peek()
era inadequado. Eu usei em seu reader.ReadToEndAsync()
lugar.
var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
é usado no try
bloco ao ler uma linha. Se o codificador falhar em analisar o texto fornecido (o texto não está codificado com utf8), Utf8EncodingVerifier irá lançar. A exceção é detectada e então sabemos que o texto não é utf8 e o padrão é ISO-8859-1
Os códigos a seguir são meus códigos Powershell para determinar se alguns arquivos cpp ou h ou ml estão codificados com ISO-8859-1 (Latin-1) ou UTF-8 sem BOM, se nenhum deles supor que seja GB18030. Eu sou um chinês trabalhando na França e o MSVC salva como Latin-1 no computador francês e como GB no computador chinês, então isso me ajuda a evitar problemas de codificação quando faço trocas de arquivos de origem entre meu sistema e meus colegas.
O caminho é simples, se todos os caracteres estiverem entre x00-x7E, ASCII, UTF-8 e Latin-1 são todos iguais, mas se eu ler um arquivo não ASCII por UTF-8, encontraremos o caractere especial mostrado , então tente ler com Latin-1. Em Latin-1, entre \ x7F e \ xAF está vazio, enquanto GB usa full entre x00-xFF, então se eu tiver algum entre os dois, não é Latin-1
O código é escrito em PowerShell, mas usa .net, por isso é fácil de ser traduzido para C # ou 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 não é muito útil, mas você pode tentar o seguinte algoritmo:
Aqui está a chamada:
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");
Aqui está o código:
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; //
}
}
Procure aqui por 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();
}