Pregunta

En nuestra aplicación, recibimos archivos de texto (.txt, .csv, etc.) de diversas fuentes.Al leer, estos archivos a veces contienen basura, porque los archivos se crearon en una página de códigos diferente/desconocida.

¿Hay alguna forma de detectar (automáticamente) la página de códigos de un archivo de texto?

El detectEncodingFromByteOrderMarks, sobre el StreamReader constructor, trabaja para UTF8 y otros archivos marcados Unicode, pero estoy buscando una manera de detectar páginas de códigos, como ibm850, windows1252.


Gracias por sus respuestas, esto es lo que he hecho.

Los archivos que recibimos son de usuarios finales, que no tienen ni idea de las páginas de códigos.Los receptores también son usuarios finales; a estas alturas esto es lo que saben sobre las páginas de códigos:Las páginas de códigos existen y son molestas.

Solución:

  • Abra el archivo recibido en el Bloc de notas y observe un fragmento de texto confuso.Si alguien se llama François o algo así, con tu inteligencia humana puedes adivinarlo.
  • Creé una pequeña aplicación que el usuario puede usar para abrir el archivo e ingresar un texto que el usuario sabe que aparecerá en el archivo cuando se use la página de códigos correcta.
  • Recorra todas las páginas de códigos y muestre las que brindan una solución con el texto proporcionado por el usuario.
  • Si aparece más de una página de códigos, solicite al usuario que especifique más texto.
¿Fue útil?

Solución

No puede detectar la página de códigos, es necesario que se lo indiquen.Puedes analizar los bytes y adivinarlos, pero eso puede dar resultados extraños (a veces divertidos).No puedo encontrarlo ahora, pero estoy seguro de que se puede engañar al Bloc de notas para que muestre texto en inglés en chino.

De todos modos, esto es lo que necesitas leer:Lo mínimo absoluto que todo desarrollador de software debe saber absolutamente y positivamente sobre Unicode y los conjuntos de caracteres (¡sin excusas!).

Específicamente Joel dice:

El hecho más importante sobre las codificaciones

Si olvida por completo todo lo que acabo de explicar, recuerde un hecho extremadamente importante.No tiene sentido tener una cadena sin saber qué codificación utiliza.Ya no puedes esconder la cabeza en la arena y pretender que el texto "sin formato" es ASCII.No existe el texto plano.

Si tiene una cadena, en la memoria, en un archivo o en un mensaje de correo electrónico, debe saber en qué codificación está o no podrá interpretarla ni mostrarla a los usuarios correctamente.

Otros consejos

Si busca detectar codificaciones que no sean UTF (es decir,sin BOM), básicamente se trata de heurísticas y análisis estadístico del texto.Tal vez quieras echarle un vistazo a Documento de Mozilla sobre la detección universal de caracteres (Mismo enlace, con mejor formato a través de Wayback Machine.).

Has probado Puerto C# para Mozilla Universal Charset Detector

Ejemplo de http://code.google.com/p/ude/

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.");
        }
    }
}    

No puedes detectar la página de códigos.

Esto es claramente falso.Cada navegador web tiene algún tipo de detector de juegos de caracteres universal para manejar páginas que no tienen indicación alguna de codificación.Firefox tiene uno.Puedes descargar el código y ver cómo lo hace.Ver alguna documentación aquí.Básicamente, es una heurística, pero que funciona muy bien.

Con una cantidad razonable de texto, incluso es posible detectar el idioma.

Aqui hay otro más Acabo de encontrar usando Google:

Sé que es muy tarde para esta pregunta y esta solución no atraerá a algunos (debido a su sesgo centrado en el inglés y su falta de pruebas estadísticas/empíricas), pero funcionó muy bien para mí, especialmente para procesar datos CSV cargados:

http://www.architectshack.com/TextFileEncodingDetector.ashx

Ventajas:

  • Detección de lista de materiales incorporada
  • Codificación predeterminada/de reserva personalizable
  • bastante confiable (en mi experiencia) para archivos basados ​​en Europa occidental que contienen algunos datos exóticos (por ejemplo, nombres franceses) con una mezcla de archivos de estilo UTF-8 y Latin-1, básicamente la mayor parte de los entornos de EE. UU. y Europa occidental.

Nota:Yo soy quien escribió esta clase, ¡así que obviamente tómalo con cautela!:)

Bloc de notas++ tiene esta característica lista para usar.También admite cambiarlo.

Buscando una solución diferente, encontré que

https://code.google.com/p/ude/

esta solución es un poco pesada.

Necesitaba algo de detección de codificación básica, basada en los 4 primeros bytes y probablemente en la detección del juego de caracteres xml, así que tomé un código fuente de muestra de Internet y agregué una versión ligeramente modificada de

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

escrito para Java.

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

        if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
            return Encoding.UTF32;

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

Probablemente sea suficiente leer los primeros 1024 bytes del archivo, pero estoy cargando el archivo completo.

Si alguien busca una solución del 93,9%.Esto funciona para mí:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}

Hice algo similar en Python.Básicamente, necesita muchos datos de muestra de varias codificaciones, que se desglosan en una ventana deslizante de dos bytes y se almacenan en un diccionario (hash), codificados en pares de bytes que proporcionan valores de listas de codificaciones.

Dado ese diccionario (hash), tomas el texto de entrada y:

  • si comienza con cualquier carácter BOM ('\xfe\xff' para UTF-16-BE, '\xff\xfe' para UTF-16-LE, '\xef\xbb\xbf' para UTF-8, etc.), tratarlo como se sugiere
  • de lo contrario, tome una muestra lo suficientemente grande del texto, tome todos los pares de bytes de la muestra y elija la codificación menos común sugerida en el diccionario.

Si también ha probado textos codificados en UTF que no no comience con cualquier lista de materiales, el segundo paso cubrirá aquellas que se deslizaron desde el primer paso.

Hasta ahora, me funciona (los datos de muestra y los datos de entrada posteriores son subtítulos en varios idiomas) con tasas de error decrecientes.

El constructor de la clase StreamReader toma un parámetro de "detección de codificación".

La herramienta "uchardet" hace esto bien utilizando modelos de distribución de frecuencia de caracteres para cada conjunto de caracteres.Los archivos más grandes y los archivos más "típicos" tienen más confianza (obviamente).

En ubuntu, simplemente apt-get install uchardet.

En otros sistemas, obtenga la fuente, el uso y los documentos aquí: https://github.com/BYVoid/uchardet

Si puede vincular a una biblioteca C, puede usar libenca.Ver http://cihar.com/software/enca/.Desde la página de manual:

El ENCA lee archivos de texto dados, o entrada estándar cuando no se dan ninguno, y usa conocimiento sobre su lenguaje (debe ser compatible con usted) y una mezcla de análisis, análisis estadístico, adivinación y magia negra para determinar sus codificaciones.

Es GPL v2.

Tuve el mismo problema pero aún no encontré una buena solución para detectarlo automáticamente.Ahora estoy usando PsPad (www.pspad.com) para eso;) Funciona bien

Dado que todo se reduce básicamente a la heurística, puede resultar útil utilizar la codificación de archivos recibidos previamente de la misma fuente como primera pista.

La mayoría de las personas (o aplicaciones) hacen cosas prácticamente en el mismo orden cada vez, a menudo en la misma máquina, por lo que es muy probable que cuando Bob cree un archivo .csv y se lo envíe a Mary, siempre use Windows-1252 o lo que sea que su máquina esté predeterminada.

Siempre que sea posible, un poco de formación al cliente tampoco viene mal :-)

En realidad, estaba buscando una forma genérica, sin programación, de detectar la codificación del archivo, pero aún no la encontré.Lo que encontré al probar con diferentes codificaciones fue que mi texto era UTF-7.

Entonces, donde estaba haciendo por primera vez:Archivo StreamReader = File.OpenText (nombre de archivo completo);

Tuve que cambiarlo a:Archivo StreamReader = nuevo StreamReader (nombre de archivo completo, System.Text.Encoding.UTF7);

OpenText asume que es UTF-8.

También puede crear el StreamReader como este nuevo StreamReader (FullFileName, True), el segundo parámetro, lo que significa que debería intentar detectar la codificación del Byteordermark del archivo, pero eso no funcionó en mi caso.

Abra el archivo en AkelPad (o simplemente copie/pegue un texto confuso), vaya a Editar -> Selección -> Recodificar...-> marque "Detección automática".

Como complemento a la publicación de ITmeze, he usado esta función para convertir la salida del puerto C# para Mozilla Universal Charset Detector

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN

Gracias @Erik Aronesty por mencionar uchardet.

Mientras tanto, existe la (¿la misma?) herramienta para Linux: chardet.
O, en cygwin, es posible que desee utilizar: chardetect.

Ver: página de manual de chardet: https://www.commandlinux.com/man-page/man1/chardetect.1.html

Esto detectará (adivinará) heurísticamente la codificación de caracteres de cada archivo determinado e informará el nombre y el nivel de confianza de la codificación de caracteres detectada de cada archivo.

Han pasado 10 años (!) desde que se preguntó esto, y todavía no veo ninguna mención de la buena solución de MS, sin GPL: IMultiIdioma2 API.

La mayoría de las bibliotecas ya mencionadas están basadas en el UDE de Mozilla, y parece razonable que los navegadores ya hayan abordado problemas similares.No sé cuál es la solución de Chrome, pero desde IE 5.0 MS lanzó la suya, y es:

  1. Libre de problemas de licencia GPL y similares,
  2. Respaldado y mantenido probablemente para siempre,
  3. Proporciona resultados enriquecidos: todos los candidatos válidos para codificación/páginas de códigos junto con puntuaciones de confianza.
  4. Sorprendentemente fácil de usar (es una llamada de función única).

Es una llamada COM nativa, pero aquí hay un trabajo muy bonito por Carsten Zeumer, que maneja el desorden de interoperabilidad para el uso de .net.Hay algunas otras, pero en general esta biblioteca no recibe la atención que merece.

Utilizo este código para detectar Unicode y la página de códigos ansi predeterminada de Windows al leer un archivo.Para otras codificaciones es necesaria una verificación del contenido, manualmente o mediante programación.Esto se puede utilizar para guardar el texto con la misma codificación que cuando se abrió.(Yo uso VB.NET)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top