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 Discussion :

Espace dans un scanf


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 28
    Par défaut Espace dans un scanf
    Question surement très simple pour la plupart d'entre vous mais dont je ne connais pas la reponse, comment faire pour prendre une chaine contenant un espace avec un scanf

    Ex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ...
    ...
    scanf("%s", nom);
    ...
    Où "nom" est une chaine contenant un espace.

    Merci d'avance pour vos reponses.

  2. #2
    Expert confirmé
    Avatar de Thierry Chappuis
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Mai 2005
    Messages
    3 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Industrie Pharmaceutique

    Informations forums :
    Inscription : Mai 2005
    Messages : 3 499
    Par défaut
    Voici une manière sécurisée de saisir une ligne entrée par l'utilisateur avec scanf():
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    void vider_stdin(void)
    {
        scanf("%*[^\n]");
        getchar();
    }
     
    int main(void)
    {
        int c;
        int saisie_OK = 0;
        char s_buffer[51] = {0};
     
        while (saisie_OK != 1)
        {
            printf("Entrez une phrase: ");
            fflush(stdout);
     
            if (scanf("%50[^\n]", s_buffer) == 1)
            {
                /* La saisie avec scanf() a fonctionné */
                if ( (c = getchar()) == '\n')
                {
                    saisie_OK = 1;
                }
                else
                {
                    /* saisie trop longue */
                    fprintf(stderr, "La saisie est trop longue!\n");
                    vider_stdin();
                }
            }
            else
            {
                fprintf(stderr, "Erreur durant la saisie\n");
                vider_stdin();
            }
        }
     
        puts(s_buffer);
     
     
        return EXIT_SUCCESS;
    }
    Ce code est inspiré de l'excellent tutoriel de Xavier Renault http://xrenault.developpez.com/tutoriels/c/scanf/

    Lorsqu'il s'agit de saisir des chaînes de caractères, je préfère toutefois utiliser la fonction fgets():
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void vider_stdin(void)
    {
        scanf("%*[^\n]");
        getchar();
    }
     
    int main(void)
    {
        char *pc;
        char s_buffer[51] = {0};
        int saisie_OK = 0;
     
        while (saisie_OK == 0)
        {
            printf("Entrez une phrase: ");
            fflush(stdout);
     
            fgets(s_buffer, sizeof s_buffer, stdin);
            if ((pc = strchr(s_buffer, '\n')) != NULL)
            {
                *pc = '\0';
                saisie_OK = 1;
            }
            else
            {
                /* saisie trop longue */
                fprintf(stderr, "La saisie est trop longue!\n");
                vider_stdin();
            }
        }
     
        puts(s_buffer);
     
        return EXIT_SUCCESS;
    }
    Thierry
    "The most important thing in the kitchen is the waste paper basket and it needs to be centrally located.", Donald Knuth
    "If the only tool you have is a hammer, every problem looks like a nail.", probably Abraham Maslow

    FAQ-Python FAQ-C FAQ-C++

    +

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 28
    Par défaut
    Etant au debut de mon apprentissage du C, je dois avouer que ceci est assez complexe pour moi.

    Je vous poste mon code, c'est une application très simple ayant pour but d'ajouter des signets à la PSP.
    Attention, il se peut que ça pique les yeux je ne pense pas que le code soit très propre.
    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
    /* Par Changedman le 21/02/07, créé pour planete-psp, le site de toute l'actualité de la PSP
    Mise à jour V0.70
    Merci à developpez.com, un site parfait pour toutes vos questions sur tous les langages*/
     
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        char adresse[100];
        char nom[100];
        char Nom_Fichier[250];
        char lettre = 'C';
        long validation = 1;
        long compteur = 0;
        FILE* fichier = NULL;
     
            printf("Bookmarks Adder V0.70 By Changedman\n\n\n\n");
            printf("La lettre du lecteur de la PSP : ");
            scanf("%c", &lettre); // detection de l'emplacement de la PSP
            do
            {
            printf("Adresse a enregistrer : ");
            scanf("%s", adresse); //enregistrement de l'adresse
            printf("Nom a afficher dans le navigateur internet : ");
            scanf("%s", nom); //enregistrement du nom
            sprintf ( Nom_Fichier, "%c:\\PSP\\SYSTEM\\BROWSER\\bookmarks.html", lettre);
            fichier = fopen(Nom_Fichier, "a+"); //ouverture du fichier en mode ajout
     
                if (fichier != NULL)
            {
                fprintf(fichier, "<DT><A HREF=\"%s\" ADD_DATE=\"000000000%ld\" LAST_VISIT=\"000000000%ld\" LAST_MODIFIED=\"000000000%ld\" LAST_CHARSET=\"UTF-8\">%s</A></DL><p>  \n", adresse, compteur, compteur, compteur, nom); //balises HTMl ecrites dans le fichier
                fclose(fichier);
            }
            else
            {
                printf("Impossible d'ouvrir bookmarks.html\n"); // Message d'erreur
            }
            compteur++;
            printf("Appuyer sur 1 pour ajouter un nouveau signet ou sur 0 pour quitter : "); // propose à l'utilisateur d'en ajouter un autre
            scanf("%ld", &validation);
            }while (validation != 0);
     
            printf("\nA bientot et n hesitez pas a repasser"); // message d'au revoir (la politesse n'a jamais tué personne :p )
     
            return 0;
    }
    Ne comprenant pas entierement le code que vous m'avez fourni, je ne sais pas comment l'integrer à ce code. Pourriez-vous me le detailler, ce serait vraiment extraordinaire ou alors me dire comment l'integrer au code, j'essaierai d'en comprendre le fonctionnement ensuite.

    EDIT : Posté avant lecture de la reponse, je vais m'interesser de plus près à fgets et ses avantages

    EDIT n°2 : Je peux resoudre ce problème avec la methode des %c qui ne supprime pas les espaces automatiquement mais ce ne sera qu'une astuce et cela pourrait me manquer sur d'autres codes, je ne veux pas commencer à garder des lacunes.

  4. #4
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut Re:
    Quand on utilise "%s" dans scanf() la lecture s'arrête à la rencontre d'un espace, d'une tabulation ou d'une fin de ligne. Si tu veux saisir une chaîne comportant des espaces tu dois utiliser gets() ou mieux fgets(). Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char s[20+1];
    fgets(s, sizeof(s), stdin);

  5. #5
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par Melem
    tu dois utiliser gets()
    Une connerie par post. Tu vas t'arrêter un jour, ou il faut que j'en parle à Leon ?


  6. #6
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Citation Envoyé par Emmanuel Delahaye
    Citation Envoyé par Melem
    tu dois utiliser gets()
    Non Melem a écrit
    tu dois utiliser gets() ou mieux fgets()
    .

  7. #7
    Expert confirmé
    Avatar de Thierry Chappuis
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Mai 2005
    Messages
    3 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Industrie Pharmaceutique

    Informations forums :
    Inscription : Mai 2005
    Messages : 3 499
    Par défaut
    Salut,

    La fonction scanf() est une fonction très complexe, malgré ce que laissent parfois penser les cours d'introduction au langage C. La plupart des spécialistes la laisse même de côté au profit de fgets() dont le comportement est simple appréhender. C'est d'ailleurs l'alternative que je préconise. Mais regardons de plus prêt comment utiliser la fonction scanf() avec les chaînes de caractères.

    La saisie de chaînes de caractère avec scanf() engendre problème de sécurité majeur:

    Si l'utilisateur introduit une chaîne trop longue par rapport à l'espace prévu pour stocker cette chaîne, il y aura un débordement de tampon. Cette faille peut avoir des conséquences fâcheuses et peut être exploitée par un utilisateur mal intentionné dans le but de faire planter ton programme. Dans le contexte d'un exercice de programmation, ça ne porte pas à conséquence, mais dans le monde professionnel, cela représente un risque inacceptable (d'autant qu'avec un minimum de précautions, il est facile de contrôler ce problème). Dans ce contexte, il est donc nécessaire de contrôler le nombre de caractères saisis afin d'éviter les débordements de tampon. Ainsi les utilisations telles que scanf("%s", s_tampon) ou gets(s_tampon) (comme j'ai pu le lire dans ce fil) sont des BUGS et sont à banir du vocabulaire de tout programmeur C dès ses débuts.

    Voici l'exemple minimal pour la saisie d'une chaîne de caractère avec scanf():
    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
    #include <stdio.h>
     
    void vider_stdin(void);
     
    int main(void)
    {
        /* On définit un tableau de caractères qui servira de tampon pour la
            saisie de chaînes de caractères */
        char s_tampon[21] = {0};
     
        /* On demande à l'utilisateur d'entrer une phrase */
        printf("Entrez une phrase: ");
        fflush(stdout);
     
        /* On saisit au maximum 20 caractère pour éviter les débordements
            de tampon et place un caractère nul à la fin */
        scanf("%20s", s_tampon);
        /* Au minimum, il reste le caractère de fin de ligne sur le flux d'entrée.
            Je vide le flux entrant avec une fonction maison. 
            Voir:http://xrenault.developpez.com/tutoriels/c/scanf/ */
        vider_stdin();
     
        /* On constate que seul le 1er mot a été stocké dans le tampon, que
            les espaces de tête ont été ignoré, et que si le 1er mot est trop long,
            il a été tronqué automatiquement */
        puts(s_tampon);
     
        return 0;
    }
     
    void vider_stdin(void)
    {
        scanf("%*[^\n]");
        getchar();
    }


    Dans le code ci-dessus:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    scanf("%20s", s_tampon);
    /* est équivalent à */
    scanf(" %20[^ \n\t]", s_tampon);
    ou la chaine de format " %20[^ \n\t]" se compose d'un espace afin d'ignorer les espaces de tête, du nombre de caractères à saisir afin d'éviter les débordements de tampon, d'une expression rationnelle qui dit à scanf() de saisir tout caractères qui n'est pas un espace (espace, tabulation ou fin de ligne). La saisie s'arrête à la rencontre du 1er caractère d'espace.

    Maintenant, pour accepter la lecture de plusieurs mots séparés par des espaces ou des tabulations, il suffit de le préciser au niveau de l'expression rationnelle. Ainsi, le code suivant permet de lire plusieurs mots en évitant les débordement de tampon:
    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
    #include <stdio.h>
     
    void vider_stdin(void);
     
    int main(void)
    {
        char s_tampon[21] = {0};
     
        printf("Entrez une phrase: ");
        fflush(stdout);
     
        scanf(" %20[^\n]", s_tampon);
        vider_stdin();
     
        puts(s_tampon);
        return 0;
    }
     
    void vider_stdin(void)
    {
        scanf("%*[^\n]");
        getchar();
    }
    Tout cela est comme tu peux le voir beaucoup plus complexe que ce que disent les cours d'introduction au C sur scanf(). La fonction fgets() à mon avis plus simple maîtriser, et facilite la maintenance (en effet, rien ne dit que la personne chargée de la maintenance de ton programme sera un gourou de scanf() ). Voici l'exemple ci-dessus ré-écrit avec fgets():
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void vider_stdin(void);
     
    int main(void)
    {
        char s_tampon[21] = {0};
        char *pc;
     
        printf("Entrez une phrase: ");
        fflush(stdout);
     
        /* On saisit la ligne entrée par l'utilisateur avec fgets() */
        fgets(s_tampon, sizeof s_tampon, stdin);
        /* Si la chaîne entrée par l'utilisateur est trop longue, on ne trouve pas
           de caractère de fin de ligne '\n' dans s_tampon. On utilise la fonction
           strchr() de la bibliothèque standard pour effectuer cette recherche */
        if ((pc = strchr(s_tampon, '\n')) == NULL)
        {
            /* On affiche un avertissement à l'utilisateur pour lui signifier
               que son entrée a été tronquée */
            fprintf(stderr, "L'entrée est trop longue et a été tronquée!!!\n");
            /* On vide le tampon du flux d'entrée standard en prévision des
               saisies futures */
            vider_stdin();
        }
        else /* La chaîne a été saisie complètement */
        {
            /* On élimine le caractère de fin de ligne qui ne sert à rien */
            *pc = '\0';
        }
     
        puts(s_tampon);
     
        return 0;
    }
     
    void vider_stdin(void)
    {
        scanf("%*[^\n]");
        getchar();
    }
    Désolé d'avoir été un peu long. J'espère que ce poste est plus clair que le dernier que j'ai posté, un peu avare en commentaires.

    Salutations

    Thierry
    "The most important thing in the kitchen is the waste paper basket and it needs to be centrally located.", Donald Knuth
    "If the only tool you have is a hammer, every problem looks like a nail.", probably Abraham Maslow

    FAQ-Python FAQ-C FAQ-C++

    +

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 28
    Par défaut
    C'est moi qui ait été long, desolé. Merci beaucoup pour ce post instructif, je vais l'integrer à mon code et le securisé au mieux.

  9. #9
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par Changedman
    Question surement très simple pour la plupart d'entre vous mais dont je ne connais pas la reponse, comment faire pour prendre une chaine contenant un espace avec un scanf

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ...
    ...
    scanf("%s", nom);
    ...
    Où "nom" est une chaine contenant un espace.
    Si tu n'est pas un expert de scanf(), je te conseille d'utiliser plutôt fgets() ou de fabriquer ta propre fonction d'entrée :

    http://emmanuel-delahaye.developpez.com/inputs.htm

    Fabriqué avec 100% de caractères recyclés.

Discussions similaires

  1. Réponses: 9
    Dernier message: 06/11/2007, 12h36
  2. [Access] Nom d'une table avec un espace dans SQL
    Par Corsaire dans le forum Langage SQL
    Réponses: 7
    Dernier message: 21/04/2006, 15h50
  3. xml->html : retour chariot, espaces dans un div
    Par d'Oursse dans le forum XML/XSL et SOAP
    Réponses: 9
    Dernier message: 27/04/2004, 19h13
  4. caractère espace dans un lien ?
    Par kayser dans le forum ASP
    Réponses: 2
    Dernier message: 20/04/2004, 09h52
  5. [debutant] preservation des espace dans un fichier xml
    Par Eric B dans le forum XML/XSL et SOAP
    Réponses: 7
    Dernier message: 03/09/2003, 09h43

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