Quais codificações? UTF-8 vs UTF-16, big vs little endian? Ou você está se referindo às páginas de código do MSDos antigas, como shift-JIS ou cirílico etc?
@Oded: Quote "O método getEncoding () retornará a codificação que foi configurada (leia o JavaDoc) para o fluxo. Ele não adivinhará a codificação para você.".
A StreamReader.CurrentEncodingpropriedade 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>publicstaticEncodingGetEncoding(string filename){// Read the BOMvar bom =newbyte[4];
using (var file =newFileStream(filename,FileMode.Open,FileAccess.Read)){
file.Read(bom,0,4);}// Analyze the BOMif(bom[0]==0x2b&& bom[1]==0x2f&& bom[2]==0x76)returnEncoding.UTF7;if(bom[0]==0xef&& bom[1]==0xbb&& bom[2]==0xbf)returnEncoding.UTF8;if(bom[0]==0xff&& bom[1]==0xfe&& bom[2]==0&& bom[3]==0)returnEncoding.UTF32;//UTF-32LEif(bom[0]==0xff&& bom[1]==0xfe)returnEncoding.Unicode;//UTF-16LEif(bom[0]==0xfe&& bom[1]==0xff)returnEncoding.BigEndianUnicode;//UTF-16BEif(bom[0]==0&& bom[1]==0&& bom[2]==0xfe&& bom[3]==0xff)returnnew 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 ASCIIreturnEncoding.ASCII;}
+1. Isso funcionou para mim também (enquanto detectEncodingFromByteOrderMarks não). Usei "new FileStream (filename, FileMode.Open, FileAccess.Read)" para evitar uma IOException porque o arquivo é somente leitura.
Polyfun
56
Arquivos UTF-8 podem estar sem BOM, neste caso ele retornará ASCII incorretamente.
user626528
3
Esta resposta está errada. Olhando para o fonte de referência para StreamReader, que a implementação é o que mais pessoas vão querer. Eles fazem novas codificações em vez de usar os Encoding.Unicodeobjetos existentes , então as verificações de igualdade irão falhar (o que pode acontecer raramente porque, por exemplo, Encoding.UTF8pode 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.
hangar
2
Tive mais sucesso com o novo StreamReader (nome do arquivo, verdadeiro) .CurrentEncoding
Benoit
4
Existe um erro fundamental no código; ao detectar a assinatura UTF32 big-endian ( ), você retorna a fornecida pelo sistema , que é uma codificação little-endian (conforme observado aqui ). E também, como notado por @Nyerguds, você ainda não está procurando pelo UTF32LE, que tem assinatura (de acordo com en.wikipedia.org/wiki/Byte_order_mark ). Como o usuário observou, por ser subsumida, essa verificação deve vir antes das verificações de 2 bytes. 00 00 FE FFEncoding.UTF32FF FE 00 00
Glenn Slayden
44
O código a seguir funciona bem para mim, usando a StreamReaderclasse:
using (var reader =newStreamReader(fileName, defaultEncodingIfNoBom,true)){
reader.Peek();// you need this!var encoding = reader.CurrentEncoding;}
O truque é usar a Peekchamada, caso contrário, o .NET não fez nada (e não leu o preâmbulo, o BOM). Claro, se você usar qualquer outra ReadXXXchamada antes de verificar a codificação, ela também funcionará.
Se o arquivo não tiver BOM, a defaultEncodingIfNoBomcodificaçã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.
Eu recebo de volta o que foi definido como codificação padrão. Eu poderia estar faltando alguma coisa?
Ram
1
@DRAM - isso pode acontecer se o arquivo não tiver BOM
Simon Mourier
Obrigado @Simon Mourier. Não esperava que meu pdf / qualquer arquivo não tivesse nascido. Este link stackoverflow.com/questions/4520184/… pode ser útil para quem tenta detectar sem BOM.
Ram
1
No PowerShell, tive que executar $ reader.close (), ou então não foi possível gravar. 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 Isso não funciona se a codificação do arquivo forUTF-8 without BOM
Ozkan
11
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.
Esta parece ser a resposta mais correta, pois a outra resposta não funciona para mim. Pode-se fazer isso com File.OpenRead e .Read-ing os primeiros bytes do arquivo.
user420667
1
O passo 2 é um monte de trabalho de programação para verificar os padrões de bits, no entanto.
Nyerguds,
1
Não tenho certeza se a decodificação realmente lança exceções, ou se apenas substitui as sequências não reconhecidas por '?' Eu continuei escrevendo uma classe de verificação de padrões de bits, de qualquer maneira.
Nyerguds,
3
Ao criar uma instância de, Utf8Encodingvocê 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.
CodesInChaos
1
Eu gosto dessa resposta. A maioria das codificações (como provavelmente 99% dos casos de uso) será UTF-8 ou ANSI (página de códigos 1252 do Windows). Você pode verificar se a string contém o caractere de substituição (0xFFFD) para determinar se a codificação falhou.
Ok, se você está preocupado com a licença, pode usar esta. Licenciado como MIT e você pode usá-lo para software de código aberto e fechado. nuget.org/packages/SimpleHelpers.FileEncoding
Alexei Agüero Alba
A licença é MPL com opção GPL. 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 de
Parece que esta bifurcação é atualmente a mais ativa e tem um pacote nuget UDE.Netstandard. github.com/yinyue200/ude
jbtule de
biblioteca muito útil, com muitas codificações diferentes e incomuns! tanques!
mshakurov
6
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" codepagepublicstringDetectFileEncoding(Stream fileStream){varUtf8EncodingVerifier=Encoding.GetEncoding("utf-8",newEncoderExceptionFallback(),newDecoderExceptionFallback());
using (var reader =newStreamReader(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]publicvoidTest1(){Stream fs =File.OpenRead(@".\TestData\TextFile_ansi.csv");var detectedEncoding =DetectFileEncoding(fs);
using (var reader =newStreamReader(fs,Encoding.GetEncoding(detectedEncoding))){// Consume your filevar line = reader.ReadLine();...
Obrigado! Isso está resolvido para mim. Mas eu prefiro usar apenas em reader.Peek() vez de while (!reader.EndOfStream) { var line = reader.ReadLine(); }
Harison Silva
reader.Peek()não lê o fluxo inteiro. Descobri que, com riachos maiores, Peek()era inadequado. Eu usei em seu reader.ReadToEndAsync()lugar.
Gary Pendlebury
E o que é Utf8EncodingVerifier?
Peter Moore
1
@PeterMoore É uma codificação para utf8, var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());é usado no trybloco 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
Berthier Lemieux
2
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 #
.NET não é muito útil, mas você pode tentar o seguinte algoritmo:
tente encontrar a codificação por BOM (marca de ordem de byte) ... muito provavelmente não será encontrado
tente analisar em codificações diferentes
Aqui está a chamada:
var encoding =FileHelper.GetEncoding(filePath);if(encoding ==null)thrownewException("The file encoding is not supported. Please choose one of the following encodings: UTF8/UTF7/iso-8859-1");
Aqui está o código:
publicclassFileHelper{/// <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>publicstaticEncodingGetEncoding(string filename){var encodingByBOM =GetEncodingByBOM(filename);if(encodingByBOM !=null)return encodingByBOM;// BOM not found :(, so try to parse characters into several encodingsvar 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;returnnull;// 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>privatestaticEncodingGetEncodingByBOM(string filename){// Read the BOMvar byteOrderMark =newbyte[4];
using (var file =newFileStream(filename,FileMode.Open,FileAccess.Read)){
file.Read(byteOrderMark,0,4);}// Analyze the BOMif(byteOrderMark[0]==0x2b&& byteOrderMark[1]==0x2f&& byteOrderMark[2]==0x76)returnEncoding.UTF7;if(byteOrderMark[0]==0xef&& byteOrderMark[1]==0xbb&& byteOrderMark[2]==0xbf)returnEncoding.UTF8;if(byteOrderMark[0]==0xff&& byteOrderMark[1]==0xfe)returnEncoding.Unicode;//UTF-16LEif(byteOrderMark[0]==0xfe&& byteOrderMark[1]==0xff)returnEncoding.BigEndianUnicode;//UTF-16BEif(byteOrderMark[0]==0&& byteOrderMark[1]==0&& byteOrderMark[2]==0xfe&& byteOrderMark[3]==0xff)returnEncoding.UTF32;returnnull;// no BOM found}privatestaticEncodingGetEncodingByParsing(string filename,Encoding encoding){var encodingVerifier =Encoding.GetEncoding(encoding.BodyName,newEncoderExceptionFallback(),newDecoderExceptionFallback());try{
using (var textReader =newStreamReader(filename, encodingVerifier, detectEncodingFromByteOrderMarks:true)){while(!textReader.EndOfStream){
textReader.ReadLine();// in order to increment the stream position}// all text parsed okreturn textReader.CurrentEncoding;}}catch(Exception ex){}returnnull;// }}
string path =@"path\to\your\file.ext";
using (StreamReader sr =newStreamReader(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();}
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
fonte
StreamReader
, que a implementação é o que mais pessoas vão querer. Eles fazem novas codificações em vez de usar osEncoding.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: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 outraReadXXX
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.
fonte
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.
fonte
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.
UDE
Este é um porte do Mozilla Universal Charset Detector e você pode usá-lo assim ...
fonte
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.
fonte
reader.Peek()
vez dewhile (!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 seureader.ReadToEndAsync()
lugar.var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
é usado notry
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-1Os 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 #
fonte
.NET não é muito útil, mas você pode tentar o seguinte algoritmo:
Aqui está a chamada:
Aqui está o código:
fonte
Procure aqui por c #
https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx
fonte
Pode ser útil
fonte