¿Qué codificaciones? UTF-8 vs UTF-16, ¿big vs little endian? ¿O te refieres a las antiguas páginas de códigos de MSDos, como shift-JIS o cirílico, etc.?
@Oded: Quote "El método getEncoding () devolverá la codificación que se configuró (lea el JavaDoc) para la secuencia. No adivinará la codificación por usted".
Fábio Antunes
2
Para leer algunos antecedentes, joelonsoftware.com/articles/Unicode.html es una buena lectura. Si hay algo que debe saber sobre el texto, es que no existe el texto sin formato.
Martijn
Respuestas:
155
La StreamReader.CurrentEncodingpropiedad rara vez me devuelve la codificación correcta del archivo de texto. He tenido más éxito determinando el endianness de un archivo, analizando su marca de orden de bytes (BOM). Si el archivo no tiene una lista de materiales, esto no puede determinar la codificación del archivo.
* ACTUALIZADO 08/04/2020 para incluir detección UTF-32LE y devolver la codificación correcta 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. Esto también funcionó para mí (mientras que detectEncodingFromByteOrderMarks no lo hizo). Usé "new FileStream (nombre de archivo, FileMode.Open, FileAccess.Read)" para evitar una IOException porque el archivo es de solo lectura.
Polyfun
56
Los archivos UTF-8 pueden estar sin BOM, en este caso devolverá ASCII incorrectamente.
user626528
3
Esta respuesta es incorrecta. En cuanto a la fuente de referencia para StreamReaderque la implementación es lo que más gente va a querer. Hacen nuevas codificaciones en lugar de usar los Encoding.Unicodeobjetos existentes , por lo que las verificaciones de igualdad fallarán (lo que rara vez sucederá de todos modos porque, por ejemplo, Encoding.UTF8puede devolver diferentes objetos), pero (1) no usa el formato UTF-7 realmente extraño, (2) se establece de forma predeterminada en UTF-8 si no se encuentra una lista de materiales, y (3) se puede anular para utilizar una codificación predeterminada diferente.
hangar
2
Tuve más éxito con el nuevo StreamReader (nombre de archivo, verdadero) .CurrentEncoding
Benoit
4
Hay un error fundamental en el código; cuando detecta la firma UTF32 big-endian ( ), devuelve la proporcionada por el sistema , que es una codificación little-endian (como se indica aquí ). Y también, como señaló @Nyerguds, todavía no está buscando UTF32LE, que tiene firma (según en.wikipedia.org/wiki/Byte_order_mark ). Como señaló ese usuario, debido a que está subsumiendo, esa verificación debe venir antes que las verificaciones de 2 bytes. 00 00 FE FFEncoding.UTF32FF FE 00 00
Glenn Slayden
44
El siguiente código funciona bien para mí, usando la StreamReaderclase:
using (var reader =newStreamReader(fileName, defaultEncodingIfNoBom,true)){
reader.Peek();// you need this!var encoding = reader.CurrentEncoding;}
El truco consiste en utilizar la Peekllamada, de lo contrario .NET no ha hecho nada (y no ha leído el preámbulo, la lista de materiales). Por supuesto, si usa cualquier otra ReadXXXllamada antes de verificar la codificación, también funciona.
Si el archivo no tiene BOM, defaultEncodingIfNoBomse utilizará la codificación. También hay un StreamReader sin este método de sobrecarga (en este caso, la codificación predeterminada (ANSI) se utilizará como defaultEncodingIfNoBom), pero recomiendo definir lo que considera la codificación predeterminada en su contexto.
He probado esto con éxito con archivos con BOM para UTF8, UTF16 / Unicode (LE & BE) y UTF32 (LE & BE). No funciona para UTF7.
Vuelvo lo que se configuró como codificación predeterminada. ¿Podría estar perdiendo algo?
Ram
1
@DRAM: esto puede suceder si el archivo no tiene BOM
Simon Mourier
Gracias @Simon Mourier. No espero que mi pdf / cualquier archivo no haya nacido. Este enlace stackoverflow.com/questions/4520184/… podría ser útil para alguien que intente detectar sin bom.
Ram
1
En PowerShell tuve que ejecutar $ reader.close (), o de lo contrario estaba bloqueado para no escribir. 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 Esto no funciona si la codificación del archivo esUTF-8 without BOM
Ozkan
11
Intentaría los siguientes pasos:
1) Compruebe si hay una marca de orden de bytes
2) Compruebe si el archivo es UTF8 válido
3) Utilice la página de códigos "ANSI" local (ANSI como lo define Microsoft)
El paso 2 funciona porque la mayoría de las secuencias que no son ASCII en páginas de códigos que no sean UTF8 no son UTF8 válidas.
Esta parece la respuesta más correcta, ya que la otra respuesta no me funciona. Uno puede hacerlo con File.OpenRead y .Read-ing los primeros bytes del archivo.
user420667
1
Sin embargo, el paso 2 es un montón de trabajo de programación para verificar los patrones de bits.
Nyerguds
1
Sin embargo, no estoy seguro de que la decodificación arroje excepciones, o si simplemente reemplaza las secuencias no reconocidas con '?'. De todos modos, fui escribiendo una clase de verificación de patrones de bits.
Nyerguds
3
Cuando crea una instancia de Utf8Encoding, puede pasar un parámetro adicional que determina si se debe lanzar una excepción o si prefiere la corrupción de datos silenciosa.
CodesInChaos
1
Me gusta esta respuesta. La mayoría de las codificaciones (como el 99% de los casos de uso probablemente) serán UTF-8 o ANSI (página de códigos de Windows 1252). Puede verificar si la cadena contiene el carácter de reemplazo (0xFFFD) para determinar si la codificación falló.
De acuerdo, si está preocupado por la licencia, puede usar esta. Tiene licencia como MIT y puede usarlo tanto para software de código abierto como cerrado. nuget.org/packages/SimpleHelpers.FileEncoding
Alexei Agüero Alba
La licencia es MPL con opción 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
Parece que esta bifurcación es actualmente la más activa y tiene un paquete nuget UDE.Netstandard. github.com/yinyue200/ude
jbtule
biblioteca muy útil, que soporta muchas codificaciones diferentes e inusuales. tanques!
mshakurov
6
Proporcionando los detalles de implementación para los pasos propuestos por @CodesInChaos:
1) Compruebe si hay una marca de orden de bytes
2) Compruebe si el archivo es UTF8 válido
3) Utilice la página de códigos "ANSI" local (ANSI como lo define Microsoft)
El paso 2 funciona porque la mayoría de las secuencias que no son ASCII en páginas de códigos que no sean UTF8 no son UTF8 válidas. https://stackoverflow.com/a/4522251/867248 explica la táctica con más detalles.
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();...
¡Gracias! Esto me resolvió. Pero yo preferiría el uso justo reader.Peek() en lugar de while (!reader.EndOfStream) { var line = reader.ReadLine(); }
Harison Silva
reader.Peek()no lee toda la transmisión. Descubrí que con arroyos más grandes, Peek()era inadecuado. Usé en su reader.ReadToEndAsync()lugar.
Gary Pendlebury
¿Y qué es Utf8EncodingVerifier?
Peter Moore
1
@PeterMoore Es una codificación para utf8, var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());se usa en el trybloque al leer una línea. Si el codificador no puede analizar el texto proporcionado (el texto no está codificado con utf8), Utf8EncodingVerifier arrojará. Se detecta la excepción y luego sabemos que el texto no es utf8, y por defecto es ISO-8859-1
Berthier Lemieux hace
2
Los siguientes códigos son mis códigos de Powershell para determinar si algunos archivos cpp oh o ml están codificados con ISO-8859-1 (Latin-1) o UTF-8 sin BOM, si ninguno de los dos, suponga que es GB18030. Soy un chino que trabaja en Francia y MSVC guarda como Latin-1 en una computadora francesa y guarda como GB en una computadora china, por lo que esto me ayuda a evitar problemas de codificación cuando realizo intercambios de archivos fuente entre mi sistema y mis colegas.
La forma es simple, si todos los caracteres están entre x00-x7E, ASCII, UTF-8 y Latin-1 son todos iguales, pero si leo un archivo no ASCII por UTF-8, encontraremos el carácter especial aparecerá , intente leer con Latin-1. En Latin-1, entre \ x7F y \ xAF está vacío, mientras que GB usa lleno entre x00-xFF, así que si tengo algo entre los dos, no es Latin-1
El código está escrito en PowerShell, pero usa .net, por lo que es fácil de traducir a C # o F #
.NET no es muy útil, pero puede probar el siguiente algoritmo:
intente encontrar la codificación por BOM (marca de orden de bytes) ... es muy probable que no se encuentre
intente analizar en diferentes codificaciones
Aquí está la llamada:
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");
Aquí está el 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();}
Respuestas:
La
StreamReader.CurrentEncoding
propiedad rara vez me devuelve la codificación correcta del archivo de texto. He tenido más éxito determinando el endianness de un archivo, analizando su marca de orden de bytes (BOM). Si el archivo no tiene una lista de materiales, esto no puede determinar la codificación del archivo.* ACTUALIZADO 08/04/2020 para incluir detección UTF-32LE y devolver la codificación correcta para UTF-32BE
fuente
StreamReader
que la implementación es lo que más gente va a querer. Hacen nuevas codificaciones en lugar de usar losEncoding.Unicode
objetos existentes , por lo que las verificaciones de igualdad fallarán (lo que rara vez sucederá de todos modos porque, por ejemplo,Encoding.UTF8
puede devolver diferentes objetos), pero (1) no usa el formato UTF-7 realmente extraño, (2) se establece de forma predeterminada en UTF-8 si no se encuentra una lista de materiales, y (3) se puede anular para utilizar una codificación predeterminada diferente.00 00 FE FF
Encoding.UTF32
FF FE 00 00
El siguiente código funciona bien para mí, usando la
StreamReader
clase:El truco consiste en utilizar la
Peek
llamada, de lo contrario .NET no ha hecho nada (y no ha leído el preámbulo, la lista de materiales). Por supuesto, si usa cualquier otraReadXXX
llamada antes de verificar la codificación, también funciona.Si el archivo no tiene BOM,
defaultEncodingIfNoBom
se utilizará la codificación. También hay un StreamReader sin este método de sobrecarga (en este caso, la codificación predeterminada (ANSI) se utilizará como defaultEncodingIfNoBom), pero recomiendo definir lo que considera la codificación predeterminada en su contexto.He probado esto con éxito con archivos con BOM para UTF8, UTF16 / Unicode (LE & BE) y UTF32 (LE & BE). No funciona para UTF7.
fuente
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
Intentaría los siguientes pasos:
1) Compruebe si hay una marca de orden de bytes
2) Compruebe si el archivo es UTF8 válido
3) Utilice la página de códigos "ANSI" local (ANSI como lo define Microsoft)
El paso 2 funciona porque la mayoría de las secuencias que no son ASCII en páginas de códigos que no sean UTF8 no son UTF8 válidas.
fuente
Utf8Encoding
, puede pasar un parámetro adicional que determina si se debe lanzar una excepción o si prefiere la corrupción de datos silenciosa.Mira esto.
UDE
Este es un puerto de Mozilla Universal Charset Detector y puede usarlo así ...
fuente
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").
Proporcionando los detalles de implementación para los pasos propuestos por @CodesInChaos:
1) Compruebe si hay una marca de orden de bytes
2) Compruebe si el archivo es UTF8 válido
3) Utilice la página de códigos "ANSI" local (ANSI como lo define Microsoft)
El paso 2 funciona porque la mayoría de las secuencias que no son ASCII en páginas de códigos que no sean UTF8 no son UTF8 válidas. https://stackoverflow.com/a/4522251/867248 explica la táctica con más detalles.
fuente
reader.Peek()
en lugar dewhile (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
no lee toda la transmisión. Descubrí que con arroyos más grandes,Peek()
era inadecuado. Usé en sureader.ReadToEndAsync()
lugar.var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
se usa en eltry
bloque al leer una línea. Si el codificador no puede analizar el texto proporcionado (el texto no está codificado con utf8), Utf8EncodingVerifier arrojará. Se detecta la excepción y luego sabemos que el texto no es utf8, y por defecto es ISO-8859-1Los siguientes códigos son mis códigos de Powershell para determinar si algunos archivos cpp oh o ml están codificados con ISO-8859-1 (Latin-1) o UTF-8 sin BOM, si ninguno de los dos, suponga que es GB18030. Soy un chino que trabaja en Francia y MSVC guarda como Latin-1 en una computadora francesa y guarda como GB en una computadora china, por lo que esto me ayuda a evitar problemas de codificación cuando realizo intercambios de archivos fuente entre mi sistema y mis colegas.
La forma es simple, si todos los caracteres están entre x00-x7E, ASCII, UTF-8 y Latin-1 son todos iguales, pero si leo un archivo no ASCII por UTF-8, encontraremos el carácter especial aparecerá , intente leer con Latin-1. En Latin-1, entre \ x7F y \ xAF está vacío, mientras que GB usa lleno entre x00-xFF, así que si tengo algo entre los dos, no es Latin-1
El código está escrito en PowerShell, pero usa .net, por lo que es fácil de traducir a C # o F #
fuente
.NET no es muy útil, pero puede probar el siguiente algoritmo:
Aquí está la llamada:
Aquí está el código:
fuente
Busque aquí c #
https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx
fuente
Puede ser útil
fuente