IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Free Pascal Discussion :

Différence entre écrire une chaîne et écrire un caractère dans la console


Sujet :

Free Pascal

  1. #1
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut Différence entre écrire une chaîne et écrire un caractère dans la console
    Bonjour ! Je suis en train d'étudier les questions d'encodage et je m'intéresse à titre d'exemple au problème des accents dans la console Windows.

    Je ne m'explique pas le résultat du programme suivant. Pourquoi le résultat n'est-il pas deux fois le même ? À quel moment la différence se fait-elle ?

    Si je remplace 133 par 233, c'est le premier caractère qui est correct.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var
      s: string;
     
    begin
      SetLength(s, 1);
      s[1] := #130;
      WriteLn(s);
      WriteLn(s[1]);
    end.
     
    {
      '
      é
    }
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  2. #2
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Bref, si j'écris une chaîne de caractères dans la console, je dois utiliser la table ANSI ; si j'écris un caractère, la table OEM.

    Question subsidiaire. En supposant que l'usage normal soit d'écrire une chaîne, il faut donc encoder en ANSI pour écrire dans la console, et non pas en OEM ? Pourtant si je veux écrire depuis un batch avec des caractères accentués correctement restitués, je dois encoder le batch en OEM. Je n'y comprends plus rien.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  3. #3
    Membre éclairé

    Homme Profil pro
    Rédacteur technique (retraité)
    Inscrit en
    Octobre 2009
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 81
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Rédacteur technique (retraité)

    Informations forums :
    Inscription : Octobre 2009
    Messages : 168
    Points : 807
    Points
    807
    Par défaut
    Bonjour, et Bonne Année (avec 24h d'avance) !

    La différence est que s renvoie une valeur de type ansistring (sujet à des conversions codepage) alors que s[1] renvoie un type char (jamais de conversion).

    Il est intéressant d'essayer ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    uses sysutils, Windows;
    var
      s: string;
     
    begin
      SetLength(s, 1);
      s[1] := #130;
      WriteLn('CP=', intToStr(stringcodepage(s)));  // affiche 1252 (page de code ANSI)
      SetCodePage(rawbytestring(s), GetOemCP, false); // change l'ID page de code de s sans modifier la chaîne elle-même
      WriteLn('CP=', intToStr(stringcodepage(s)));  // affiche 850 (page de code OEM)
      WriteLn(s);   
      WriteLn(s[1]); 
    end.
    Je présume qu'une conversion intervient lors du passage d'un paramètre ansistring à WriteLn si WriteLn est associé à la console et que la chaîne n'est pas OEM.
    Cette conversion n'intervient pas pour un type char.

  4. #4
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    @DomDA91

    Merci, je crois que j'ai compris. Dans la foulée, j'ai relu avec profit cette discussion, que j'avais un peu oubliée.

    Moralité, il ne suffit pas de mettre les bonnes valeurs dans le corps de la chaîne, il faut aussi mettre à jour la "carte d'identité" de la chaîne. Si on ne le fait pas, le compilateur reconvertit la chaîne, d'où les erreurs. J'ai bon ?

    J'attends un peu avant de clore la discussion. Bonne année à vous aussi ! Mais c'est vrai que nous n'y sommes pas encore.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  5. #5
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    C'est aujourd'hui que j'ai compris ce que fait la procédure SetCodePage. J'avais toujours plus ou moins cru qu'elle faisait une conversion du contenu de la chaîne. Merci de m'avoir éclairé.

    Pour conclure (provisoirement) l'histoire du "é" dans la console Windows, un petit programme basé sur un exemple de Paul TOTH et sur l'explication de DomDA91.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    uses
      SysUtils;
     
    { https://www.developpez.net/forums/d1732510/environnements-developpement/delphi/contribuez/ne-prenez-pieds-strings/#post9515065 }
     
    type
      TStringInfo = record
        CodePage: Word;
        CharSize: Word;
        RefCount: Integer;
        Length  : Integer;
      end;
     
    procedure StringInfo(p: Pointer);
    var
      i: ^TStringInfo;
    begin
      i := p; // l'adresse du premier caractère
      Dec(i); // remonter de SizeOf(TStringInfo) en mémoire
      WriteLn(' CodePage = ', i^.CodePage);
      WriteLn(' CharSize = ', i^.CharSize);
      WriteLn(' RefCount = ', i^.RefCount);
      WriteLn(' Length   = ', i^.Length);
    end;
     
    var
      s: string;
     
    begin
      s := 'é';
      WriteLn(Length(s) = 1); // FALSE
      StringInfo(pointer(s));
      WriteLn(s);
     
      SetCodePage(rawbytestring(s), 850, FALSE); // 850 ou GetOemCP de l'unité Windows
      StringInfo(pointer(s));
      WriteLn(s);
     
      SetLength(s, 1);
      s[1] := #130;
      StringInfo(pointer(s));
      WriteLn(s);
    end.
    Code X : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
      C:\Atelier\Pascal\encoding>stringinfotest
      FALSE
       CodePage = 0
       CharSize = 1
       RefCount = -1
       Length   = 2
      é
       CodePage = 850
       CharSize = 1
       RefCount = 1
       Length   = 2
      ├®
       CodePage = 850
       CharSize = 1
       RefCount = 1
       Length   = 1
      é
    
      C:\Atelier\Pascal\encoding>
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  6. #6
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 941
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 941
    Points : 5 652
    Points
    5 652
    Par défaut
    Bonjour,

    Eh oui, et ce problème va encore nous les briser longtemps, jusqu'à ce que TOUS les systèmes soient en Unicode.
    Si les cons volaient, il ferait nuit à midi.

  7. #7
    Membre éclairé

    Homme Profil pro
    Rédacteur technique (retraité)
    Inscrit en
    Octobre 2009
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 81
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Rédacteur technique (retraité)

    Informations forums :
    Inscription : Octobre 2009
    Messages : 168
    Points : 807
    Points
    807
    Par défaut
    Citation Envoyé par Roland Chastain
    C'est aujourd'hui que j'ai compris ce que fait la procédure SetCodePage. J'avais toujours plus ou moins cru qu'elle faisait une conversion du contenu de la chaîne. Merci de m'avoir éclairé.
    En fait la procédure SetCodePage a les deux usages. Si le troisième paramètre est à TRUE, elle fait effectivement une conversion intégrale de la chaine. S'il est à FALSE elle change uniquement l'ID de page de code sans toucher à la chaîne elle-même.

  8. #8
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 671
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 671
    Points : 13 065
    Points
    13 065
    Par défaut
    Et en déclarant la variable ainsi ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type
      TConsoleString = type ansistring(850);
     
    var
      s :TConsoleString;

  9. #9
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Et en déclarant la variable ainsi ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type
      TConsoleString = type ansistring(850);
     
    var
      s :TConsoleString;
    Oui, ça fonctionne.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var
      s :TConsoleString;
     
    begin
      SetLength(s, 1);
      s[1] := #130;
      WriteLn(s);
    end.
     
    {
      é
    }
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  10. #10
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par DomDA91 Voir le message
    En fait la procédure SetCodePage a les deux usages. Si le troisième paramètre est à TRUE, elle fait effectivement une conversion intégrale de la chaine. S'il est à FALSE elle change uniquement l'ID de page de code sans toucher à la chaîne elle-même.
    Exact, merci pour le rappel. Voici un exemple que j'ai fait.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var
      s: string;
     
    begin
      s := 'é';
      SetCodePage(RawByteString(s), 65001, FALSE);
      SetCodePage(RawByteString(s), 850, TRUE);
      WriteLn(s);
    end.
     
    {
      é
    }
    Variante.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var
      s: string;
     
    type
      PRawByteString = ^RawByteString;
     
    begin
      s := 'é';
      SetCodePage(PRawByteString(@s)^, 65001, FALSE);
      SetCodePage(PRawByteString(@s)^, 850, TRUE);
    { http://forum.lazarus.freepascal.org/index.php/topic,42756.msg298651.html#msg298651 }
      WriteLn(s);
    end.
     
    {
      é
    }
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  11. #11
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    J'ai continué d'explorer les différentes façons d'écrire des caractères accentués dans la console Windows. J'ai essayé une autre approche, qui consiste à changer la page de code de la console, pour pouvoir écrire directement sans faire de conversion. Ça paraissait bien fonctionner mais je viens de m'apercevoir que ça ne fonctionne pas dans tous les cas. Voyez l'exemple.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    uses
      SysUtils, Windows;
     
    var
      LPageCode: UINT;
      LChaine: string;
     
    begin
      LChaine := 'é';
      LPageCode := GetConsoleOutputCP;
      WriteLn(LPageCode);
      SetConsoleOutputCP(CP_UTF8);
      WriteLn(GetConsoleOutputCP);  
      WriteLn('é');
      WriteLn(LChaine);
      SetConsoleOutputCP(LPageCode);
      WriteLn(GetConsoleOutputCP);
    end.
     
    {
      850
      65001
      é
      Ǹ
      850
    }
    Merci de m'aider à éclaircir ce nouveau mystère.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  12. #12
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Le problème est résolu avec l'option -FcUTF8, équivalent de {$codepage utf8}.

    Explication (que je comprends plus ou moins) sur cette page.

    Usually {$codepage utf8} / -FcUTF8 is not needed. This is rather counter-intuitive because the meaning of that flag is to treat string literals as UTF-8. However the new UTF-8 mode switches the encoding at run-time, yet constants are evaluated at compile-time.
    Moralité : cette option règle le problème mais normalement on ne devrait pas l'utiliser.

    Je n'ai plus qu'à réessayer tous mes exemples avec cette option activée, pour voir ce que ça donne.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  13. #13
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Pour clore cette discussion, je retiens, premièrement, que les développeurs de Free Pascal recommandent d'utiliser systématiquement les deux options suivantes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    {$codepage utf8}// -Fcutf8
    {$DEFINE EnableUTF8RTL}// -dEnableUTF8RTL
    Avec ces options, on peut écrire dans la console Windows sans avoir rien à faire de particulier.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {$codepage utf8}// -Fcutf8
    {$DEFINE EnableUTF8RTL}// -dEnableUTF8RTL
     
    const
      CChaine = 'Hélène parle d''Ysaÿe avec l''évêque.';
     
    var
      LChaine: string;
     
    begin  
      WriteLn(CChaine);
      LChaine := CChaine;
      WriteLn(LChaine);
    end.
     
    {
      Hélène parle d'Ysaÿe avec l'évêque.
      Hélène parle d'Ysaÿe avec l'évêque.
    }
    Deuxièmement, je retiens qu'on peut obtenir le même effet simplement en déclarant l'unité LazUTF8.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    program project2;
     
    uses
      SysUtils, LazUTF8;
     
    const
      CChaine = 'Hélène parle d''Ysaÿe avec l''évêque.';
     
    var
      LChaine: string;
     
    begin
      WriteLn(CChaine);
      LChaine := CChaine;
      WriteLn(LChaine);
    end.
     
    {
      H├®l├¿ne parle d'Ysa├┐e avec l'├®v├¬que.
      Hélène parle d'Ysaÿe avec l'évêque.
    }
    Mais comme vous le voyez, il y a un problème dans ce cas avec les chaînes constantes. Je retiens donc, troisièmement, qu'il faut toujours passer par une variable.

    Voilà, j'espère que je n'ai pas dit trop de bêtises. En tout cas je m'arrête là.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  14. #14
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 930
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 930
    Points : 59 398
    Points
    59 398
    Billets dans le blog
    2
    Par défaut
    Merci Roland, jolie trouvaille : la solution avec les deux directives de compilation fonctionne dans tous les cas de figure.
    Règles du forum
    Cours et tutoriels Pascal, Delphi, Lazarus et Assembleur
    Avant de poser une question, consultez les FAQ Pascal, Delphi, Lazarus et Assembleur
    Mes tutoriels et sources Pascal

    Le problème en ce bas monde est que les imbéciles sont sûrs d'eux et fiers comme des coqs de basse cour, alors que les gens intelligents sont emplis de doute. [Bertrand Russell]
    La tolérance atteindra un tel niveau que les personnes intelligentes seront interdites de toute réflexion afin de ne pas offenser les imbéciles. [Fiodor Mikhaïlovitch Dostoïevski]

  15. #15
    Membre éclairé

    Homme Profil pro
    Rédacteur technique (retraité)
    Inscrit en
    Octobre 2009
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 81
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Rédacteur technique (retraité)

    Informations forums :
    Inscription : Octobre 2009
    Messages : 168
    Points : 807
    Points
    807
    Par défaut
    Citation Envoyé par Alcatîz Voir le message
    Merci Roland, jolie trouvaille : la solution avec les deux directives de compilation fonctionne dans tous les cas de figure.
    Avec une petite nuance cependant: c'est vrai a condition que le fichier source Pascal soit effectivement encodé en UTF8, ce qui est généralement le cas si on compile depuis Lazarus par exemple, mais nous sommes ici dans une discussion Free Pascal donc pas nécessairement dans un tel environnement.

    La directive {$CodePage xyz} implique que le fichier source dans lequel elle apparaît soit effectivement en encodé en xyz. Une incohérence à cet égard n'est pas détectable par le compilateur mais se traduira immanquablement par des bizarreries au niveau des constantes chaînes.

  16. #16
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    @DomDA91

    Oui, cela méritait d'être précisé. Cela dit j'ai l'impression qu'UTF-8 est devenu la norme un peu partout. Par exemple, Notepad++ (que j'utilise beaucoup personnellement) crée par défaut des fichiers UTF-8. Mais quoi qu'il en soit vous avez raison.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

Discussions similaires

  1. Réponses: 5
    Dernier message: 16/10/2015, 14h31
  2. Réponses: 3
    Dernier message: 18/11/2011, 20h23
  3. Écrire une chaîne de caractères
    Par chester89 dans le forum x86 16-bits
    Réponses: 1
    Dernier message: 18/11/2010, 01h54
  4. Réponses: 5
    Dernier message: 30/09/2008, 14h36
  5. différence entre exporter une base de données ou la détacher
    Par boumbo73 dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 12/03/2008, 12h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo