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

C++Builder Discussion :

Le sprintf de AnsiString, bug ou pas?


Sujet :

C++Builder

  1. #1
    Membre expérimenté
    Homme Profil pro
    Enseignant
    Inscrit en
    Mars 2012
    Messages
    164
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2012
    Messages : 164
    Par défaut Le sprintf de AnsiString, bug ou pas?
    en tentant de répondre au problème de commande SQL de faniette, je me souvenu d'un "bug" que j'avais cherché longtemps, soit un "(null)" qui s'insérait dans un champ de table de données.

    J'ai fini par le trouver, me suis demandé s'il y avait un bug dans la méthode de "sprintf" de AnsiString ou si c'était une subtilité qui m'échappait.

    Voilà le bug(?)
    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
     
    {
      AnsiString dest,buf ;
     
      buf = "allo" ;
      dest.sprintf("1- une string: [%s]",buf) ;
      ShowMessage(dest) ;
     
      buf = "" ;
      dest.sprintf("2- une string: [%s]",buf) ;
      ShowMessage(dest) ;
     
      dest.sprintf("3- une string: [%s]",buf.c_str()) ;
      ShowMessage(dest) ;
    }
    Voici ce qui sera affiché:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    1- une string: [allo]
    2- une string: [(null)]
    3- une string: []
    La version #2, vous trouvez ça normal ou pas?

    Et vous trouvez normal, vous voulez m'expliquer pourquoi?

  2. #2
    Membre Expert
    Avatar de DjmSoftware
    Homme Profil pro
    Responsable de compte
    Inscrit en
    Mars 2002
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Responsable de compte
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 044
    Billets dans le blog
    1
    Par défaut
    Salut Guyt
    dans le cas 2 tu copie un objet d'on tu ignore le contenu puis qu'il n'a pas été initialisé.

    dans le cas 3 c'est bien une chaîne de caractère qui est copiée dans ton buffer
    la méthode .c_str() te retourne le contenu de ton buffer.

    cdlt
    vous trouverez mes tutoriels à l'adresse suivante: http://djmsoftware.developpez.com/
    je vous en souhaite une excellente lecture ...

    A lire : Les règles du forum

  3. #3
    Membre expérimenté
    Homme Profil pro
    Enseignant
    Inscrit en
    Mars 2012
    Messages
    164
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2012
    Messages : 164
    Par défaut
    Citation Envoyé par DjmSoftware Voir le message
    Salut Guyt
    dans le cas 2 tu copie un objet d'on tu ignore le contenu puis qu'il n'a pas été initialisé.
    cdlt
    Na, je comprends toujours pas, y a une subtilité qui m'échappe.

    Juste avant le cas 2, je fais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    buf= "" ;  // ce qui serait équivalent à buf = AnsiString("") ;
    dest.sprintf("2- une string: [%s]",buf) ;
    J’initialise bien la chaine avec une chaine vide, le contenu je le connais, c'est une string de longueur zéro!

    Tiens, je te balance une autre curiosité:

    Un bouton, une TEdit avec "Edit1" dans sa propriété Text.

    Le code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     
    void __fastcall TForm3::Button1Click(TObject *Sender)
    {
      AnsiString stg ;
     
      stg.sprintf("%s",Edit1->Text) ;
      ShowMessage(stg) ;
      stg.sprintf("%s",AnsiString(Edit1->Text)) ;
      ShowMessage(stg) ;
    }
    ce qui sera affiché:
    Ouate de phoque???

  4. #4
    Membre Expert
    Avatar de DjmSoftware
    Homme Profil pro
    Responsable de compte
    Inscrit en
    Mars 2002
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Responsable de compte
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 044
    Billets dans le blog
    1
    Par défaut
    Salut
    Ne confond pas un string qui correspond à un tableau de caractère avec un Ansistring qui lui est un object
    %s correspond à une chaîne de caractère
    Dans ton premier exemple tu ressort le premier caractère normal en Unicode le caractère suivant est un 0
    Dans ton second exemple tu construit un object de type Ansistring à partir d'un object unicode
    La syntaxe correcte est de suffixer avec un c_str ou. T_str
    Cdlt
    vous trouverez mes tutoriels à l'adresse suivante: http://djmsoftware.developpez.com/
    je vous en souhaite une excellente lecture ...

    A lire : Les règles du forum

  5. #5
    Membre expérimenté
    Homme Profil pro
    Enseignant
    Inscrit en
    Mars 2012
    Messages
    164
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2012
    Messages : 164
    Par défaut
    Citation Envoyé par DjmSoftware Voir le message
    Salut
    La syntaxe correcte est de suffixer avec un c_str ou. T_str
    Cdlt
    C'est ce que je faisais depuis un bon bout de temps, sans savoir exactement pourquoi.

    Maintenant je le sais, un gros merci!

  6. #6
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 081
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 081
    Par défaut
    J'avais même déposé une demande sur Quality Central pour que le sprintf (plus élégant et performant que le Format ARRAYOFCONST) émettent un Warning lors de l'utilisation d'un String comme paramètre !

    Je suis d'accord avec Guyt54, la documentation de UnicodeString.sprintf ou AnsiString.sprintf ne fournis aucun avertissement, c'est bien dommage, de plus le lien vers Format renforce la confusion car cette fonction supporte les objets comme paramètre (puisque c'est du code Delphi)

    Venant du Delphi et ayant l'habitude du Format, je pensais utilise ce dernier mais la template ARRAYOFCONST m'a découragé vu le code que cela génère assez infâme du coup, je suis passé au sprintf !
    J'ai bêtement écrit 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
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    {
      UnicodeString S1 = "Chaine S1";
      UnicodeString S2 = "Chaine S2";
      UnicodeString S3 = "Chaine S3";
      UnicodeString S4 = "Chaine S4";
      UnicodeString S5 = "Chaine S5";
      UnicodeString S6 = "Chaine S6";
      UnicodeString LocalStr = "";
     
      for (int i = 0; i < 10; i++)
      {
        S1 += UnicodeString().sprintf(L"%s %d", EmptyStr, i);
        S2 += UnicodeString().sprintf(L"%s %d", "", i);
        S3 += UnicodeString().sprintf(L"%s %d", LocalStr, i);
     
        S4 += Format(L"%s %d", ARRAYOFCONST((EmptyStr, i)));
        S5 += Format(L"%s %d", ARRAYOFCONST(("", i)));
        S6 += Format(L"%s %d", ARRAYOFCONST((LocalStr, i)));
      }
      MemoTrace->Lines->Add("sprintf");
      MemoTrace->Lines->Add(S1);
      MemoTrace->Lines->Add(S2);
      MemoTrace->Lines->Add(S3);
      MemoTrace->Lines->Add("Format");
      MemoTrace->Lines->Add(S4);
      MemoTrace->Lines->Add(S5);
      MemoTrace->Lines->Add(S6);
    }
    Avec ce beau résultat :

    Result actually

    sprintf
    Chaine S1(null) 0(null) 1(null) 2(null) 3(null) 4(null) 5(null) 6(null) 7(null) 8(null) 9
    Chaine S2 0 1 2 3 4 5 6 7 8 9
    Chaine S3(null) 0(null) 1(null) 2(null) 3(null) 4(null) 5(null) 6(null) 7(null) 8(null) 9
    Format
    Chaine S4 0 1 2 3 4 5 6 7 8 9
    Chaine S5 0 1 2 3 4 5 6 7 8 9
    Chaine S6 0 1 2 3 4 5 6 7 8 9

    Result expected

    sprintf
    Chaine S1 0 1 2 3 4 5 6 7 8 9
    Chaine S2 0 1 2 3 4 5 6 7 8 9
    Chaine S3 0 1 2 3 4 5 6 7 8 9
    Format
    Chaine S4 0 1 2 3 4 5 6 7 8 9
    Chaine S5 0 1 2 3 4 5 6 7 8 9
    Chaine S6 0 1 2 3 4 5 6 7 8 9

    Leur réponse sur QC fut : "Please try to add .c_str() as below, because EmptyStr and LocalStr are instances of UnicodeString class."
    Belle Astuce, j'aurais bien aimé qu'elle soit dans la Documentation, j'aurais évité une perte de temps !
    D'ailleurs, c'était aussi pour générer du SQL à la volée via ma couche d'objet persistant tout en utilisant les Parameters pour simplifier le typage des champs\propriétés

    les AnsiString sont des objets en allocation statique, le constructeur défini la chaîne sur un pointeur NULL qui est géré par le C++ tout comme en Delphi, il y avait même une constante prévue à cet effet (avant on ne pouvait pas définir '' ou "" directement, il fallait utiliser la constante EmptyStr

    Et le (null) est tout simplement la valeur du membre privé Data dans la AnsiString !
    Tout en rappelant que Data[-12] pointe sur StrRec et que le code d'allocation des chaines est écrit en Delphi !


    J'ai conservé un petit code (j'ai aussi une version UnicodeString) pour me rappeler de cette surprise !

    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    //---------------------------------------------------------------------------
    void __fastcall TLanguageBasicsForm::BtnReveiverAndParametersprintfClick(TObject *Sender)
    {
      MemoTrace->Lines->Add("S.sprintf(\"%s %d\", S, i++);");
      AnsiString SP1 = "Chaine SP1";
      AnsiString SP2 = "Chaine SP2";
      AnsiString SP3 = "Chaine SP3";
      AnsiString SP4 = "Chaine SP4";
      AnsiString SP4c = "Chaine SP4c";
      AnsiString SP5 = "Chaine SP5";
      AnsiString SP6 = "Chaine SP6";
      AnsiString SP6c = "Chaine SP6c";
      AnsiString SP7 = "Chaine SP7";
      AnsiString SF1 = "Chaine SF1";
      AnsiString SF2 = "Chaine SF2";
      AnsiString SF3 = "Chaine SF3";
     
      for (int i = 0; i < 10; i++)
      {
        SP1.sprintf("%s %d", SP1, i);
        SP2 = AnsiString().sprintf("%s %d", SP2, i);
        SP3 += ", ";
        SP3 += AnsiString().sprintf("%d", i);
        SP4 += AnsiString().sprintf("%s %d", EmptyStr, i);
        SP4c += AnsiString().sprintf("%s %d", EmptyStr.c_str(), i);
        SP5 += AnsiString().sprintf("%s %d", "", i);
        AnsiString LocalStr = "";
        SP6 += AnsiString().sprintf("%s %d", LocalStr, i);
        SP6c += AnsiString().sprintf("%s %d", LocalStr.c_str(), i);
        SP7 += AnsiString().sprintf("%s %d", AnsiString(this->ClassName()).c_str(), i);
     
        SF1.Format("%s %d", ARRAYOFCONST((SF1, i)));
        SF2 += AnsiString().Format("%s %d", ARRAYOFCONST((EmptyStr, i)));
        SF3 += AnsiString().Format("%s %d", ARRAYOFCONST((LocalStr, i)));
      }
      MemoTrace->Lines->Add("sprintf");
      MemoTrace->Lines->Add(SP1);
      MemoTrace->Lines->Add(SP2);
      MemoTrace->Lines->Add(SP3);
      MemoTrace->Lines->Add(SP4);
      MemoTrace->Lines->Add(SP4c);
      MemoTrace->Lines->Add(SP5);
      MemoTrace->Lines->Add(SP6);
      MemoTrace->Lines->Add(SP6c);
      MemoTrace->Lines->Add(SP7);
      MemoTrace->Lines->Add("Format");
      MemoTrace->Lines->Add(SF1);
      MemoTrace->Lines->Add(SF2);
      MemoTrace->Lines->Add(SF3);
     
      MemoTrace->Lines->Add("sprintf too params : " + AnsiString().sprintf("a %d b %d c %d", 1, 2, 3, 4, 5, 6));
      MemoTrace->Lines->Add("sprintf missing params : " + AnsiString().sprintf("a %d b %d c %d", 1, 2));
     
      MemoTrace->Lines->Add("sprintf %8x et %08x");
      int i = 123;
      MemoTrace->Lines->Add(AnsiString().sprintf("%8x", i));
      MemoTrace->Lines->Add(AnsiString().sprintf("%08x", i));
      MemoTrace->Lines->Add(AnsiString().sprintf("%01x", i));
      MemoTrace->Lines->Add(AnsiString().sprintf("%d", i));
      MemoTrace->Lines->Add(AnsiString().sprintf("%06d", i));
      MemoTrace->Lines->Add(AnsiString().sprintf("%02d", i));
      bool flag = true;
      MemoTrace->Lines->Add(AnsiString().sprintf("Boolean true = %d", flag));
      flag = false;
      MemoTrace->Lines->Add(AnsiString().sprintf("Boolean false = %d", flag));
    }
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  7. #7
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 081
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 081
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Venant du Delphi et ayant l'habitude du Format, je pensais utilise ce dernier mais la template ARRAYOFCONST m'a découragé vu le code que cela génère assez infâme du coup, je suis passé au sprintf !
    Désolé, de faire remonter ce sujet, mais je voudrais corriger mes propres propos, du moins les mettre à jour parce que les amateurs du sprintf pourrait être intéressé par Format du Delphi

    comme sprintf ne fourni pas les extensions microsoft du wsprintf permettant de specifier %s et %S pour gérer ANSI\WIDE,
    on peut utiliser Format qui n'a pas besoin qu'on lui précise ce que l'on fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
      AnsiString MaAnsi = "Ma chaîne ANSI accentuée !";
      UnicodeString MaUni = "Ma chaîne Unicode accentuée !";
     
      UnicodeString ResUni = System::Sysutils::Format("Format Unicode Fmt ANSI : AnsiString '%s' | UnicodeString '%s'", ARRAYOFCONST((MaAnsi, MaUni)));
      UnicodeString ResUni2 = System::Sysutils::Format(L"Format Unicode Fmt Unicode : AnsiString '%s' | UnicodeString '%s'", ARRAYOFCONST((MaAnsi, MaUni)));
      AnsiString ResAnsi = System::Ansistrings::Format("Format ANSI Fmt ANSI : AnsiString '%s' | UnicodeString '%s'", ARRAYOFCONST((MaAnsi, MaUni)));
    Toute donne bien "... : AnsiString 'Ma chaîne ANSI accentuée !' | UnicodeString 'Ma chaîne Unicode accentuée !'".

    En lisant le code de Format, on voit qu'il gère proprement vtAnsiString et vtUnicodeString, et les autres types char

    Je suis en train de passer de 2007 à XE2, j'ai des sprintf et des c_str(), pas de warning alors que je mélange Ansi et Uni, je vais tout passer en Format et je n'aurais plus de soucis comme ceux de Guyt54 où l'octet de poids fort Uni provoquait un arrêt de chaine Ansi ou pire l'absence de double zéro peuvent provoquer une VA

    Faudrait tester avec de l'arabe, du russe ou chinois, je n'ai pas installé ces langues, donc, je ne peux pas tester semble-t-il : "W8114 Le caractère représenté par un nom de caractère universel '\u043a' ne peut pas être représenté dans les paramètres régionaux en cours".

    le Code évite l'utilisation systèmatique de .c_str() pour prévenir d'une éventuelle chaine vide
    cela évite le problème lorsque l'on utilise la méthode String().sprintf(... devenu Wide en 2009..XE2...
    ou inversement une utilisation brute d'une chaine wchar_t* dans un AnsiString().sprintf ...

    Et Maintenant Format peut-être vraiment être utilisé ARRAYOFCONST !
    Moi, j'utilisais manuellement OpenArray<TVarRec> et fournissait le High en dur (oui fallait que je compte) dans mes Eeception.CreateFmt,
    je n'aurais plus à faire cela à la main car Embarcadero a optimisé son template !


    la Macro en ARRAYOFCONST en 2007
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
      // ARRAYOFCONST: construct an OpenArray<TVarRec> on the fly
      #define ARRAYOFCONST(values) \
        OpenArray<TVarRec>values, OpenArrayCount<TVarRec>values.GetHigh()
    la Macro en ARRAYOFCONST en XE2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      // ARRAYOFCONST: construct an OpenArray<TVarRec> on the fly
      #define ARRAYOFCONST(values) \
       ::System::OpenArray< ::System::TVarRec>values,
      sizeof(::System::OpenArrayCounter< ::System::TVarRec>::Count values) - 1
    Code de Test :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int ValueForFormat(int &v)
    {
      return ++v;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      int EvalCount = 0;
      MemoTrace->Lines->Add("Format with ARRAYOFCONST - Three Param : " + AnsiString().Format("%d %d %d", ARRAYOFCONST((ValueForFormat(EvalCount), ValueForFormat(EvalCount), ValueForFormat(EvalCount)))));
      MemoTrace->Lines->Add("Format with ARRAYOFCONST - Three Param Count : " + IntToStr(EvalCount));
    En 2007, voici le comportement
    Cela génère DEUX Tableaux en RunTime
    le Premier utilisé comme paramètre
    le Seconde utilisé comme Compteur
    Embarcadero Quality Central : 86675 et 82831

    Code test : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Format with ARRAYOFCONST of 2007 - Three Param : 4 5 6
    Format with ARRAYOFCONST of 2007 - Three Param Count : 6

    En XE et donc XE2, le comportement a été optimisé
    Cela génère un tableau en RunTime et un autre interprêté à la compilation
    sizeof calcul la taille du paramètre retour templaté dès la compilation, c'est nettement mieux

    Code test : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Format with sizeof ARRAYOFCONST of XE2 - Three Param : 1 2 3
    Format with sizeof ARRAYOFCONST of XE2 - Three Param Count : 3

    que l'on peut comparer à sprintf

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      EvalCount = 0;
      MemoTrace->Lines->Add("sprintf - Three Param : " + String().sprintf(L"%d %d %d", ValueForFormat(EvalCount), ValueForFormat(EvalCount), ValueForFormat(EvalCount)));
      MemoTrace->Lines->Add("sprintf - Three Param Count : " + IntToStr(EvalCount));
    Code test : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    sprintf - Three Param : 3 2 1
    sprintf - Three Param Count : 3
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. bug excel pas ordinaire calendrier fantôme
    Par alsimbad dans le forum Macros et VBA Excel
    Réponses: 7
    Dernier message: 03/12/2006, 18h59
  2. FB: DB vide, impossible de connecter : Bug ou Pas Bug ?
    Par Rica dans le forum Connexion aux bases de données
    Réponses: 1
    Dernier message: 14/05/2005, 11h15

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