File Encoding Expert

Présentation
File Encoding Expert est une application réalisée avec Lazarus, qui utilise trois façons différentes de détecter l'encodage d'un fichier, permettant ainsi de comparer les résultats obtenus, qui ne sont pas toujours concordants !

L'application est bâtie, d'une part, sur la fonction GuessEncoding() de l'unité LConvEncoding de Lazarus ; d'autre part sur une fonction équivalente de Delphi (XE2), préalablement exportée dans une bibliothèque dynamique, pour pouvoir être utilisée dans un programme Lazarus ; enfin sur la bibliothèque Charset Detector :

http://chsdet.sourceforge.net/
Téléchargement
Compatibilité
Windows
2  0 
Téléchargé 15 fois Voir les 8 commentaires
Détails
Catégories : Programmes complets
Avatar de Roland Chastain
Rédacteur / Modérateur
Voir tous les téléchargements de l'auteur
Licence : Non renseignée
Date de mise en ligne : 13 février 2016




Avatar de DomDA91 DomDA91 - Membre éclairé https://www.developpez.com
le 11/02/2016 à 11:49
Bon ! finalement la solution est plutot simple c'est Utf8ToWinCP.
Ce qui l'est moins c'est cette f... documentation. Une page du Wiki Lazarus fait bien mention de cette fonction (et de son opposé WinCPToUtf8) mais ne précise pas l'unité qui la contient. Comme elle est mentionnée sous un gros titre "RTL ... etc." j'ai naïvement cru que cétait une nouvelle fonction de FPC 3.0. Après de nombreux essais soldés par un "Identifier not found" j'ai fini par abandonner pour rechercher autre chose. C'est au cours de ces recherches que je suis tombé dessus par hazard, dans l'unité LazUtf8 de la LCL. Les fonctions Utf8ToWinCP et WinCPToUtf8 sont nouvelles et ne figurent pas (encore ??) dans aucun des CHM installés avec la 1.6RC2.

Pour l'appel de GetEncoding il suffit donc d'ajouter LazUtf8 dans le Uses et de coder :

Code : Sélectionner tout
   result := GetFileEncoding(PAnsiChar(Utf8ToWinCP(Edit1.Text)));
Remarque : Comme la fontion Utf8ToWinCp est nouvelle il faudra peut-être prévoir des directives de compilation conditionelle pour s'adapter aux versions nouvelle et anciennes de Lazarus.

Autre suggestion :
Ce serait bien d'effacer le contenu d'Edit3 lorsque le résultat de la fonction est 0. En effet, dans ce cas, le libellé de l'analyse précédente reste affiché et cela peut induire l'utilisateur en erreur. Pareil pour Edit5.
Avatar de DomDA91 DomDA91 - Membre éclairé https://www.developpez.com
le 13/02/2016 à 12:15
Citation Envoyé par FChrisF
En fait, et bien que je ne sache avec précision depuis quand ces 2 fonctions existent, ce qui est certain c'est qu'elles existent depuis déjà quelques temps (probablement depuis la version 1.4.0).
Ah merçi, j'ignorais. Effectivement les fonctions Utf8ToWinCP et WinCPToUtf8 sont là, bien cachées dans LazUtf8 de mon Lazarus 1.4.2.

Sous Lazarus 1.4 elles ont une fonction semblable à, respectivement, Utf8ToSys et SysToUtf8.

Avec Lazarus 1.6 leur comportement diffère :
  • UTF8ToSys et SysToUtf8 sont maintenant des fonctions "zombies", qui se contentent de renvoyer leur paramètre sans changement, laissant jouer la conversion automatique s'il y a lieu.
  • Utf8ToWinCP et WinCPToUtf8 font explicitement la conversion qui convient en tenant compte de l'encodage de la chaîne passée en paramètre et l'encodage ANSI par défaut du système.


@Roland Chastain :

Compte-tenu de ce qui précède, on pourrait dès maintenant coder le projet comme suit :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
Uses LazUtf8, ...


//  if not FileExists(Edit1.Text) then
    if not FileExists(UTF8ToSys(Edit1.Text)) then

//  s := LoadStringFromFile(Edit1.Text);
    s := LoadStringFromFile(UTF8ToSys(Edit1.Text));

//result := GetFileEncoding(PAnsiChar(Edit1.Text));
         { Attention, ici Utf8ToSys ne marcherait pas en 1.6 }
  result := GetFileEncoding(PAnsiChar(UTF8ToWinCP(Edit1.Text)));

Par ailleurs dans FormCreate les paramètres, récupérés de la ligne de commande, sont en Ansi et doivent aussi être convertis en Utf8 s'ils sont passés à une routine LCL :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  
procedure TForm1.FormCreate(Sender: TObject);
begin
  if ParamCount = 0 then
//  ListBox1.Items := FindAllFiles(ExtractFilePath(ParamStr(0)) + 'samples', '*.*', FALSE)
    ListBox1.Items := FindAllFiles(SysToUTF8(ExtractFilePath(ParamStr(0))) + 'samples', '*.*', FALSE)
  else
    if DirectoryExists(ParamStr(1)) then
//    ListBox1.Items := FindAllFiles(ParamStr(1), '*.*', FALSE)
      ListBox1.Items := FindAllFiles(SysToUtf8(ParamStr(1)), '*.*', FALSE)
    else
      if FileExists(ParamStr(1)) then
//      Edit1.Text := ParamStr(1);
        Edit1.Text := SysToUtf8(ParamStr(1));
end;

Ainsi modifié, le projet devrait pouvoir être compilé et fonctionner aussi bien avec la verion 1.6 qu'avec les versions 1.4 précédentes, sans nécessiter aucun changement.

A signaler aussi les nouveautés de l'unité SysUtils de la RTL livrée avec Lazarus 1.6 :
  • Classe TEncoding qui permet de ré-écrire en Free Pascal un équivalent de ce que fait la DLL Encoding de Delphi (une quatrième façon à ajouter à FileEncodingExpert ?);
  • Routines de conversion CodePageToCodePageName et CodePageNameToCodePage. Va-t-on vers une uniformisation des libellés ? Ce ne serait pas dommage car entre les directives $CODEPAGE de FPC, les noms de constantes TSystemCodePage (unité System), la macro Lazarus %encoding (reconnue par LazUtils.GuessEncoding), les noms de fichier source de la RTL (CPxxx.pas) et j'en passe, c'est actuellement une belle cacophonie !
Avatar de Roland Chastain Roland Chastain - Rédacteur/Modérateur https://www.developpez.com
le 13/02/2016 à 17:40
Citation Envoyé par DomDA91 Voir le message
Compte-tenu de ce qui précède, on pourrait dès maintenant coder le projet comme suit :
Merci bien pour toutes ces corrections. Je les ai faites et j'ai mis à jour le téléchargement.
Avatar de DomDA91 DomDA91 - Membre éclairé https://www.developpez.com
le 09/02/2016 à 10:17
Testé : OK mais à la condition que le chemin des fichiers testés ne comportent QUE des caractères 'US-ASCII' (< #128). Echec sinon .

Quant au fait que les résultats puissent être différents, ce peut être normal. Un fichier donné, selon son contenu, peut correspondre à plusieurs pages de code possibles et la fonction, ne renvoyant qu'un seul résultat, doit choisir.

La plupart des fonctions se contentent de rechercher certaines métadonnées au début du fichier et en leur absence, se replient sur une valeur par défaut. Par exemple pour un fichier pur 'US-ASCII' LConvEncoding privilégie UTF8, alors que la quasi-totalité des pages de code fondées sur l'ASCII soient aussi possibles. D'autres fonctions privilégieront la page de code par défaut définie par le système en fonction de sa localisation.
Avatar de DomDA91 DomDA91 - Membre éclairé https://www.developpez.com
le 10/02/2016 à 11:14
Citation Envoyé par Roland Chastain Voir le message
... Là tout de suite je n'ai pas en tête les modifications à faire.
Moi j'ai un peu regardé. La solution sera d'ailleurs différente selon que l'on compile sous Lazarus 1.4/FPC 2 ou pour le nouveau Lazarus 1.6/FPC 3.0.

Avec Lazarus 1.4, il suffit de saupoudrer 7 ou 8 "UTF8ToSys" (ou "SysToUTF8", chaque fois que l'on passe une spécification de fichier à une fonction de la RTL qui elle ne connaît que l'Ansi.

Avec Lazarus 1.6, ce n'est, en principe, plus nécessaire car les conversions se font automatiquement. J'ai essayé : ça marche pour "LconvEncoding" et pour "chsdet.dll" mais la fonction "encoding.dll" elle renvoie toujours 0.
Je pense qu'ici le problème vient de ce que le paramètre est passé à la DLL via un pChar et que dans ce cas il n'y a pas de conversion automatique. Il faut donc forcer explicitement la conversion.
Sur mon PC (Windows 8.1/Configuré France) un forçage, en dur, à CP1252 fonctionne, mais il faudrait pouvoir connaître exactement, par programme, la page de code qu'utilise la machine cible.
Je cherche à cet effet une fonction de la RTL qui fasse cette conversion ou qui, au moins, retournerait la page de code à utiliser. Je pense avoir une piste et je confirmerait après vérification.

Citation Envoyé par Roland Chastain Voir le message
Sur les trois fonctions que le programme utilise, la seule qui ne se contente pas, justement, des métadonnées (et même qui les ignore, si je ne me trompe), c'est la fonction de la bibliothèque Charset Detector. Je n'ai pas regardé le code source, mais j'ai cru comprendre que la fonction cherche à reconnaître les lettres caractéristiques d'une langue et ensuite en déduit la page de code appropriée. Ce qui fait que si on lui donne un fichier ne contenant que des caractères ASCII, elle renvoie 0.
En fait elle reconnaît tout de même le BOM qui, selon la définition générale qu'en donne Wikipedia, peut être considéré comme une forme particulière de métadonnée. Je n'ai pas non plus examiné le source de près mais le simple examen des noms de fichier et le volume global du projet (plus de 400Ko) est assez parlant. D'autant plus que reconnaître les lettres caractéristiques d'un langage, alors que l'on ne sait pas encore par quel code elles sont représentées, ne doit pas être très simple !
Avatar de FChrisF FChrisF - Membre confirmé https://www.developpez.com
le 11/02/2016 à 23:02
Citation Envoyé par DomDA91 Voir le message

[...]
Remarque : Comme la fontion Utf8ToWinCp est nouvelle il faudra peut-être prévoir des directives de compilation conditionelle pour s'adapter aux versions nouvelle et anciennes de Lazarus.
[...]
En fait, et bien que je ne sache avec précision depuis quand ces 2 fonctions existent, ce qui est certain c'est qu'elles existent depuis déjà quelques temps (probablement depuis la version 1.4.0).
Avatar de Roland Chastain Roland Chastain - Rédacteur/Modérateur https://www.developpez.com
le 09/02/2016 à 17:48
Citation Envoyé par DomDA91 Voir le message
Testé : OK mais à la condition que le chemin des fichiers testés ne comportent QUE des caractères 'US-ASCII' (< #128). Echec sinon .
Merci pour l'essai et pour m'avoir signalé cette erreur. Il faudra que je m'y penche. Là tout de suite je n'ai pas en tête les modifications à faire.

Citation Envoyé par DomDA91 Voir le message
Quant au fait que les résultats puissent être différents, ce peut être normal. Un fichier donné, selon son contenu, peut correspondre à plusieurs pages de code possibles et la fonction, ne renvoyant qu'un seul résultat, doit choisir.

La plupart des fonctions se contentent de rechercher certaines métadonnées au début du fichier et en leur absence, se replient sur une valeur par défaut. Par exemple pour un fichier pur 'US-ASCII' LConvEncoding privilégie UTF8, alors que la quasi-totalité des pages de code fondées sur l'ASCII soient aussi possibles. D'autres fonctions privilégieront la page de code par défaut définie par le système en fonction de sa localisation.
Excellent résumé, me semble-t-il. Merci !

Sur les trois fonctions que le programme utilise, la seule qui ne se contente pas, justement, des métadonnées (et même qui les ignore, si je ne me trompe), c'est la fonction de la bibliothèque Charset Detector. Je n'ai pas regardé le code source, mais j'ai cru comprendre que la fonction cherche à reconnaître les lettres caractéristiques d'une langue et ensuite en déduit la page de code appropriée. Ce qui fait que si on lui donne un fichier ne contenant que des caractères ASCII, elle renvoie 0.
Avatar de Roland Chastain Roland Chastain - Rédacteur/Modérateur https://www.developpez.com
le 11/02/2016 à 13:55
Citation Envoyé par DomDA91 Voir le message
Bon ! finalement la solution est plutot simple c'est Utf8ToWinCP.
...

Pour l'appel de GetEncoding il suffit donc d'ajouter LazUtf8 dans le Uses et de coder :

Code : Sélectionner tout
   result := GetFileEncoding(PAnsiChar(Utf8ToWinCP(Edit1.Text)));
Remarque : Comme la fontion Utf8ToWinCp est nouvelle il faudra peut-être prévoir des directives de compilation conditionelle pour s'adapter aux versions nouvelle et anciennes de Lazarus.
Merci pour cette suggestion et pour vos recherches.

Auparavant j'avais essayé (conformément à votre suggestion précédente) ceci, qui m'a paru fonctionner correctement :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
  //if not FileExists(Edit1.Text) then
  if not FileExists(UTF8ToSys(Edit1.Text)) then
  ...

  //s := LoadStringFromFile(Edit1.Text);
  s := LoadStringFromFile(UTF8ToSys(Edit1.Text));
  ...

  //result := GetFileEncoding(PAnsiChar(Edit1.Text));
  result := GetFileEncoding(PAnsiChar(UTF8ToSys(Edit1.Text)));
Mais je prends note quand même de l'existence de cette fonction UTF8ToWinCP.

Citation Envoyé par DomDA91 Voir le message
Autre suggestion :
Ce serait bien d'effacer le contenu d'Edit3 lorsque le résultat de la fonction est 0. En effet, dans ce cas, le libellé de l'analyse précédente reste affiché et cela peut induire l'utilisateur en erreur. Pareil pour Edit5.
Bien vu. Je vais faire la correction.
Developpez.com décline toute responsabilité quant à l'utilisation des différents éléments téléchargés.