122 votes

Comment lire une ligne de la console en C ?

Quel est le moyen le plus simple de lire une ligne complète dans un programme C en console ? Le texte saisi peut avoir une longueur variable et nous ne pouvons pas faire d'hypothèse sur son contenu.

11voto

Tim Points 13334

Vous devrez peut-être utiliser une boucle caractère par caractère (getc()) pour vous assurer qu'il n'y a pas de débordement de tampon et que l'entrée n'est pas tronquée.

6voto

orcmid Points 1700

Comme suggéré, vous pouvez utiliser getchar() pour lire depuis la console jusqu'à ce qu'une fin de ligne ou un EOF soit retourné, en construisant votre propre tampon. La croissance dynamique du tampon peut se produire si vous êtes incapable de définir une taille de ligne maximale raisonnable.

Vous pouvez également utiliser fgets comme un moyen sûr d'obtenir une ligne sous forme de chaîne C à terminaison nulle :

#include <stdio.h>

char line[1024];  /* Generously large value for most situations */

char *eof;

line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0';  /* Ensure no false-null at end of buffer */

eof = fgets(line, sizeof(line), stdin);

Si vous avez épuisé l'entrée de la console ou si l'opération a échoué pour une raison quelconque, eof == NULL est retourné et le tampon de ligne peut rester inchangé (c'est pourquoi le fait de mettre le premier caractère à ' \0 est pratique).

fgets ne remplira pas trop la ligne[] et s'assurera qu'il y a un null après le dernier caractère accepté lors d'un retour réussi.

Si la fin de la ligne a été atteinte, le caractère précédant la terminaison ' \0 sera un \n '.

S'il n'y a pas de terminaison ' \n avant la terminaison \0 Il est possible qu'il y ait plus de données ou que la prochaine requête signale la fin du fichier. Vous devrez faire un autre fgets pour déterminer ce qui est vrai. (A cet égard, boucler avec getchar() est plus facile).

Dans l'exemple de code (mis à jour) ci-dessus, si line[sizeof(line)-1] == ' \0 après des fgets réussis, vous savez que le tampon a été complètement rempli. Si cette position est précédée d'un ' \n vous savez que vous avez eu de la chance. Sinon, il y a soit plus de données, soit une fin de fichier à venir dans stdin. (Lorsque le tampon n'est pas complètement rempli, il se peut que vous soyez toujours en fin de fichier et qu'il n'y ait pas non plus de ' \n à la fin de la ligne en cours. Puisque vous devez parcourir la chaîne de caractères pour trouver et/ou éliminer tout ' \n avant la fin de la chaîne (le premier ' \0 ' dans le tampon), je suis enclin à préférer utiliser getchar() en premier lieu).

Faites ce que vous devez faire pour faire face au fait qu'il y a toujours plus de lignes que la quantité que vous avez lue comme premier morceau. Les exemples de croissance dynamique d'un tampon peuvent fonctionner avec getchar ou fgets. Il y a quelques cas délicats auxquels il faut faire attention (comme se souvenir que l'entrée suivante commence à être stockée à la position de la balise ' \0 ' qui a terminé l'entrée précédente avant que le tampon ne soit étendu).

5voto

plan9assembler Points 2209

4voto

Cherubim Points 3621

Comment lire une ligne de la console en C ?

  • Construire votre propre fonction est l'un des moyens qui vous aidera à lire une ligne de la console.

  • J'utilise allocation dynamique de la mémoire pour allouer la quantité de mémoire nécessaire

  • Lorsque nous sommes sur le point d'épuiser la mémoire allouée, nous essayons de doubler la taille de la mémoire.

  • Et ici, j'utilise une boucle pour analyser chaque caractère de la chaîne un par un en utilisant la fonction getchar() jusqu'à ce que l'utilisateur entre '\n' o EOF caractère

  • enfin nous supprimons toute mémoire allouée en plus avant de retourner la ligne

    //the function to read lines of variable length

    char scan_line(char line) { int ch; // as getchar() returns int long capacity = 0; // capacity of the buffer long length = 0; // maintains the length of the string char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks

    while ( ((ch = getchar()) != '\n') && (ch != EOF) )
    {
        if((length + 1) >= capacity)
        {
            // resetting capacity
            if (capacity == 0)
                capacity = 2; // some initial fixed length 
            else
                capacity *= 2; // double the size
    
            // try reallocating the memory
            if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
            {
                printf("ERROR: unsuccessful allocation");
                // return line; or you can exit
                exit(1);
            }
    
            line = temp;
        }
    
        line[length] = (char) ch; //type casting `int` to `char`
        length++;
    }
    line[length + 1] = '\0'; //inserting null character at the end
    
    // remove additionally allocated memory
    if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
    {
        printf("ERROR: unsuccessful allocation");
        // return line; or you can exit
        exit(1);
    }
    
    line = temp;
    return line;

    }

  • Maintenant, vous pouvez lire une ligne complète de cette façon :

     char *line = NULL;
     line = scan_line(line);

Voici un exemple de programme en utilisant le scan_line() fonction :

#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions

char* scan_line(char *line)
{
    ..........
}

int main(void)
{
    char *a = NULL;

    a = scan_line(a); //function call to scan the line

    printf("%s\n",a); //printing the scanned line

    free(a); //don't forget to free the malloc'd pointer
}

entrée de l'échantillon :

Twinkle Twinkle little star.... in the sky!

sortie de l'échantillon :

Twinkle Twinkle little star.... in the sky!

0 votes

Contrairement aux autres réponses, il fonctionne sous GCC, Windows 10, compilé en C11, 64bit.

1voto

dsm Points 7429

J'ai rencontré le même problème il y a quelques temps, voici ma solution, j'espère que cela vous aidera.

/*
 * Initial size of the read buffer
 */
#define DEFAULT_BUFFER 1024

/*
 * Standard boolean type definition
 */
typedef enum{ false = 0, true = 1 }bool;

/*
 * Flags errors in pointer returning functions
 */
bool has_err = false;

/*
 * Reads the next line of text from file and returns it.
 * The line must be free()d afterwards.
 *
 * This function will segfault on binary data.
 */
char *readLine(FILE *file){
    char *buffer   = NULL;
    char *tmp_buf  = NULL;
    bool line_read = false;
    int  iteration = 0;
    int  offset    = 0;

    if(file == NULL){
        fprintf(stderr, "readLine: NULL file pointer passed!\n");
        has_err = true;

        return NULL;
    }

    while(!line_read){
        if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
            fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
            free(tmp_buf);

            break;
        }

        if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
            line_read = true;

        offset = DEFAULT_BUFFER * (iteration + 1);

        if((buffer = realloc(buffer, offset)) == NULL){
            fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
            free(tmp_buf);
            has_err = true;

            return NULL;
        }

        offset = DEFAULT_BUFFER * iteration - iteration;

        if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
            fprintf(stderr, "readLine: Cannot copy to buffer\n");
            free(tmp_buf);
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        free(tmp_buf);
        iteration++;
    }

    return buffer;
}

1 votes

Votre code deviendrait BEAUCOUP plus simple si vous utilisez goto pour gérer le cas d'erreur. Néanmoins, ne pensez-vous pas que vous pourriez réutiliser tmp_buf au lieu de malloc avec la même taille encore et encore dans la boucle ?

0 votes

Utilisation d'une seule variable globale has_err pour signaler les erreurs rend cette fonction non sécurisée et moins confortable à utiliser. Ne le faites pas de cette façon. Vous indiquez déjà une erreur en retournant NULL. Il y a également lieu de penser que les messages d'erreur imprimés ne sont pas une bonne idée dans une fonction de bibliothèque à usage général.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X