Dans notre application, nous recevons des fichiers texte (.txt
, .csv
, etc.) provenant de sources diverses. Lors de la lecture, ces fichiers contiennent parfois des ordures, car ils ont été créés dans une page de codes différente/inconnue.
Existe-t-il un moyen de détecter (automatiquement) la page de codes d'un fichier texte?
La detectEncodingFromByteOrderMarks
, sur le constructeur StreamReader
, fonctionne pour UTF8
et les autres fichiers marqués Unicode, mais je cherche un moyen de détecter les pages de codes, comme ibm850
, windows1252
.
Merci pour vos réponses, voici ce que j'ai fait.
Les fichiers que nous recevons proviennent d'utilisateurs finaux. Ils n'ont aucune idée des pages de codes. Les destinataires sont également des utilisateurs finaux. À présent, voici ce qu’ils connaissent des pages de codes: Les pages de codes existent et sont gênantes.
Solution:
Vous ne pouvez pas détecter la page de codes, vous devez le savoir. Vous pouvez analyser les octets et les deviner, mais cela peut donner des résultats bizarres (parfois amusants). Je ne le trouve pas maintenant, mais je suis sûr que le Bloc-notes peut être amené à afficher du texte anglais en chinois.
Quoi qu'il en soit, voici ce que vous devez lire: Le minimum absolu que chaque développeur de logiciel a absolument, doit savoir absolument sur Unicode et les jeux de caractères (sans excuses!) .
Plus précisément, Joël dit:
Le fait le plus important concernant les codages
Si vous oubliez complètement tout ce que je viens d’expliquer, rappelez-vous un fait extrêmement important. Il n’a pas de sens d’avoir une chaîne sans savoir quel encodage elle utilise. Vous ne pouvez plus vous mettre la tête dans le sable et prétendre que le texte "ordinaire" est au format ASCII . Il n'y a aucune chose telle que le texte simple.
Si vous avez une chaîne, en mémoire, dans un fichier ou dans un courrier électronique, vous devez savoir dans quel encodage il se trouve ou vous ne pouvez pas l'interpréter ni l'afficher correctement aux utilisateurs.
Si vous souhaitez détecter des codages non-UTF (c.-à-d. Pas de nomenclature), il vous faut essentiellement une analyse heuristique et statistique du texte. Vous voudrez peut-être jeter un coup d'œil au papier Mozilla sur la détection de jeu de caractères universel ( même lien, avec un meilleur formatage via Wayback Machine ).
Avez-vous essayé le port C # du détecteur de charset universel Mozilla
Exemple tiré 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.");
}
}
}
Vous ne pouvez pas détecter la page de codes
Ceci est clairement faux. Chaque navigateur Web possède une sorte de détecteur de jeux de caractères universel pour traiter les pages qui n’ont aucune indication d’un codage. Firefox en a un. Vous pouvez télécharger le code et voir comment il le fait. Voir la documentation ici . Fondamentalement, c'est une heuristique, mais qui fonctionne vraiment bien.
Avec une quantité de texte raisonnable, il est même possible de détecter la langue.
Voici un autre Je viens de trouver en utilisant Google:
Je sais qu'il est très tard pour cette question et que cette solution ne plaira pas à certains (en raison de son biais anglais et de son absence de tests statistiques/empiriques), mais cela a très bien fonctionné pour moi, en particulier pour le traitement des données CSV téléchargées:
http://www.architectshack.com/TextFileEncodingDetector.ashx
Avantages:
Remarque: je suis celui qui a écrit cette classe, donc prenez-le évidemment avec un grain de sel! :)
A la recherche d'une solution différente, j'ai trouvé que
https://code.google.com/p/ude/
cette solution est un peu lourde.
J'avais besoin d'une détection d'encodage de base, basée sur 4 premiers octets et probablement d'une détection de jeu de caractères xml - j'ai donc pris un exemple de code source sur Internet et ajouté une version légèrement modifiée de
http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html
écrit pour 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
}
Il suffit de lire probablement les 1024 premiers octets du fichier, mais je charge tout le fichier.
Notepad ++ a cette fonctionnalité prête à l'emploi. Il prend également en charge le changement.
Si quelqu'un recherche une solution à 93,9%. Cela fonctionne pour moi:
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();
}
}
}
J'ai fait quelque chose de similaire en Python. Fondamentalement, vous avez besoin d’un grand nombre d’échantillons de données provenant de divers codages, qui sont décomposés par une fenêtre glissante de deux octets et stockés dans un dictionnaire (hachage), saisis sur des paires d’octets fournissant des valeurs de listes de codages.
Étant donné ce dictionnaire (hash), vous prenez votre texte d'entrée et:
Si vous avez également échantillonné des textes codés en UTF qui pas commencent par une nomenclature, la deuxième étape couvrira ceux qui ont glissé depuis la première étape.
Jusqu'ici, cela fonctionne pour moi (les données d'échantillon et les données d'entrée suivantes sont des sous-titres dans différentes langues) avec des taux d'erreur décroissants.
L'outil "uchardet" le fait bien en utilisant des modèles de distribution de fréquence de caractère pour chaque jeu de caractères. Les fichiers plus volumineux et les fichiers plus "typiques" ont plus de confiance (évidemment).
Sur Ubuntu, vous venez de apt-get install uchardet
.
Sur d’autres systèmes, obtenez la source, l’utilisation et la documentation ici: https://github.com/BYVoid/uchardet
Le constructeur de la classe StreamReader prend un paramètre 'detect encoding'.
Si vous pouvez créer un lien vers une bibliothèque C, vous pouvez utiliser libenca
. Voir http://cihar.com/software/enca/ . De la page de manuel:
Enca lit les fichiers texte donnés, ou l’entrée standard quand aucun fichier n’est donné, et utilise des connaissances sur leur langue (vous devez les soutenir) et un mélange d'analyse syntaxique, d'analyse statistique, de devinettes et de magie noire pour déterminer leurs encodages.
C'est la GPL v2.
Vous avez le même problème, mais vous n’avez pas encore trouvé de bonne solution pour le détecter automatiquement ... Maintenant, je me sers de PsPad (www.pspad.com) pour cela;)
Merci @ Erik Aronesty d’avoir mentionné uchardet
.
Entre-temps, le même outil existe pour linux: chardet
.
Ou, sur cygwin, vous pouvez utiliser: chardetect
.
Voir: Page de manuel de chardet:https://www.commandlinux.com/man-page/man1/chardetect.1.html
Cela détectera de manière heuristique (deviner) le codage de caractères pour chaque fichier donné et indiquera le nom et le niveau de confiance du codage de caractères détecté pour chaque fichier.
En fait, je cherchais un moyen générique de détecter le codage du fichier, mais pas celui de la programmation, mais je ne l’avais pas trouvé pour le moment ..___.
Donc, là où je travaillais pour la première fois: StreamReader file = File.OpenText (fullfilename);
Je devais le changer en: StreamReader file = new StreamReader (fullfilename, System.Text.Encoding.UTF7);
OpenText suppose que c'est UTF-8.
vous pouvez également créer le StreamReader comme cecinew StreamReader (fullfilename, true), le deuxième paramètre signifiant qu'il doit essayer de détecter le codage à partir du byteordermark du fichier, mais cela n'a pas fonctionné dans mon cas.
En tant que complément à la publication ITmeze, j'ai utilisé cette fonction pour convertir la sortie du port C # du détecteur de charset universel Mozilla.
private Encoding GetEncodingFromString(string codePageName)
{
try
{
return Encoding.GetEncoding(codePageName);
}
catch
{
return Encoding.ASCII;
}
}
Ouvrez le fichier dans AkelPad (ou copiez/collez un texte tronqué), sélectionnez Edition -> Sélection -> Recoder ... -> cochez la case "Détection automatique".
Comme il s'agit essentiellement d'heuristiques, il peut être utile d'utiliser comme premier indice le codage de fichiers précédemment reçus provenant de la même source.
La plupart des gens (ou des applications) font les choses dans le même ordre à chaque fois, souvent sur le même ordinateur. Il est donc fort probable que, lorsque Bob crée un fichier .csv et l'envoie à Mary, il utilisera toujours Windows 1252 ou quel que soit le comportement par défaut de sa machine.
Dans la mesure du possible, un peu de formation client ne fait pas de mal non plus :-)
10Y (!) Était écoulé depuis que cette question avait été posée, et je ne vois toujours aucune mention de la bonne solution de MS sans GPL: IMultiLanguage2 API.
La plupart des bibliothèques déjà mentionnées sont basées sur l'UDE de Mozilla - et il semble raisonnable que les navigateurs aient déjà abordé des problèmes similaires. Je ne sais pas quelle est la solution de chrome, mais depuis IE 5.0 MS ont publié la leur, elle se présente comme suit:
Il s’agit d’un appel COM natif, mais voici un très beau travail de Carsten Zeumer, qui gère le désordre d’interopérabilité pour l’utilisation du .net. Il y en a d’autres, mais dans l’ensemble, cette bibliothèque n’obtient pas l’attention qu’elle mérite.