web-dev-qa-db-fra.com

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 sur console C Le texte saisi peut avoir une longueur variable et nous ne pouvons en déduire aucune hypothèse sur son contenu.

96
pbreault

Vous avez besoin d'une gestion dynamique de la mémoire et utilisez la fonction fgets pour lire votre ligne. Cependant, il semble n'y avoir aucun moyen de voir combien de caractères il lit. Donc, vous utilisez fgetc:

char * getline(void) {
    char * line = malloc(100), * linep = line;
    size_t lenmax = 100, len = lenmax;
    int c;

    if(line == NULL)
        return NULL;

    for(;;) {
        c = fgetc(stdin);
        if(c == EOF)
            break;

        if(--len == 0) {
            len = lenmax;
            char * linen = realloc(linep, lenmax *= 2);

            if(linen == NULL) {
                free(linep);
                return NULL;
            }
            line = linen + (line - linep);
            linep = linen;
        }

        if((*line++ = c) == '\n')
            break;
    }
    *line = '\0';
    return linep;
}

Note : Ne jamais utiliser obtient! Il ne vérifie pas les limites et peut déborder de votre mémoire tampon

75

Si vous utilisez la bibliothèque GNU C ou une autre bibliothèque compatible POSIX, vous pouvez utiliser getline() et lui transmettre stdin pour le flux de fichiers.

26
dmityugov

Une implémentation très simple mais dangereuse pour lire la ligne pour l'allocation statique: 

char line[1024];

scanf("%[^\n]", line);

Une implémentation plus sûre, sans possibilité de débordement de tampon, mais avec la possibilité de ne pas lire la ligne entière, est la suivante:

char line[1024];

scanf("%1023[^\n]", line);

Pas la «différence par un» entre la longueur spécifiée déclarant la variable et la longueur spécifiée dans la chaîne de format. C'est un artefact historique.

14
Anish Jhaveri

Vous devrez peut-être utiliser une boucle caractère par caractère (getc ()) pour vous assurer de ne pas avoir de dépassement de mémoire tampon et de ne pas tronquer l'entrée. 

11
Tim

Donc, si vous cherchiez des arguments de commande, jetez un oeil à la réponse de Tim . Si vous voulez juste lire une ligne depuis la console:

#include <stdio.h>

int main()
{
  char string [256];
  printf ("Insert your full address: ");
  gets (string);
  printf ("Your address is: %s\n",string);
  return 0;
}

Oui, ce n'est pas sécurisé, vous pouvez faire un dépassement de tampon, il ne vérifie pas la fin du fichier, il ne supporte pas les encodages et beaucoup d'autres choses . En fait, je ne me demandais même pas si c'était vrai de ce genre de choses. Je conviens que j'ai un peu foiré:) Mais ... quand je vois une question comme "Comment lire une ligne de la console en C?", Je suppose qu'une personne a besoin de quelque chose de simple, comme gets () et non pas 100 lignes de code comme ci-dessus… .. En fait, si vous essayez d'écrire ces 100 lignes de code en réalité, vous feriez beaucoup plus d'erreurs que vous n'auriez fait si vous aviez choisi de le faire;)

8
badbadboy

getline exemple exécutable

Mentionné sur cette réponse mais voici un exemple.

C'est POSIX 7 , alloue de la mémoire pour nous, et réutilise joliment le tampon alloué.

Pointeur newbs, lisez ceci: Pourquoi le premier argument de getline est-il un pointeur sur le pointeur "char **" au lieu de "char *"?

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    while (read != -1) {
        puts("enter a line");
        read = getline(&line, &len, stdin);
        printf("line = %s", line);
        printf("line length = %zu\n", read);
        puts("");
    }
    free(line);
    return 0;
}

implémentation de glibc

Pas de POSIX? Peut-être voudrez-vous regarder l’application glibc 2.23 .

Il résout getdelim, qui est un simple sur-ensemble POSIX de getline avec un terminateur de ligne arbitraire.

Il double la mémoire allouée chaque fois qu'une augmentation est nécessaire et a l'apparence d'un thread-safe.

Cela nécessite une certaine expansion macro, mais il est peu probable que vous fassiez beaucoup mieux.

Comme suggéré, vous pouvez utiliser getchar () pour lire à partir de la console jusqu'à ce qu'une fin de ligne ou un EOF soit renvoyé, créant ainsi votre propre tampon. Une augmentation dynamique de la mémoire tampon peut se produire si vous ne pouvez pas définir une taille de ligne maximale raisonnable.

Vous pouvez également utiliser fgets comme moyen sûr d’obtenir une ligne sous forme de chaîne C terminée par un caractère null:

#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 renvoyé et le tampon de ligne peut rester inchangé (c'est pourquoi il est pratique de définir le premier caractère sur '\ 0').

fgets ne remplira pas trop la ligne [] et garantira qu’il existe une valeur null après le dernier caractère accepté lors d’un retour réussi.

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

S'il n'y a pas de terminaison '\ n' avant la fin '\ 0', il se peut qu'il y ait plus de données ou que la demande suivante indique la fin du fichier. Vous devrez faire un autre fgets pour déterminer lequel est lequel. (À cet égard, la boucle avec getchar () est plus facile.)

Dans l'exemple de code (mis à jour) ci-dessus, si vous saisissez line [sizeof (line) -1] == '\ 0' après la réussite de la conversion, vous savez que la mémoire tampon a été complètement remplie. Si cette position est précédée d'un "\ n", vous savez que vous avez eu de la chance. Sinon, il y a plus de données ou une fin de fichier dans stdin. (Lorsque la mémoire tampon n'est pas complètement remplie, vous pouvez toujours vous trouver à la fin du fichier et il peut ne pas y avoir de '\ n' à la fin de la ligne en cours. Comme vous devez scanner la chaîne 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 gérer le nombre de lignes dépassant toujours le montant que vous lisez comme premier bloc. Les exemples de croissance dynamique d'un tampon peuvent être utilisés avec getchar ou fgets. Il y a quelques cas délicats à surveiller (par exemple, rappelez-vous que la prochaine entrée commence à être stockée à la position du "0" qui a terminé l'entrée précédente avant l'extension du tampon).

4
orcmid

Beaucoup de gens, comme moi, arrivent à ce billet avec le titre correspondant à ce qui est recherché, bien que la description parle de longueur variable. Dans la plupart des cas, nous connaissons la longueur à l’avance. 

Si vous connaissez la longueur avant la main, essayez ci-dessous:

char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read

source: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

2

Comment lire une ligne de la console en C?

  • Construire votre propre fonction}, est l'un des moyens qui vous aideraient à lire une ligne depuis la console dans C.

  • J'utilise allocation de mémoire dynamique pour allouer uniquement la quantité de mémoire suffisante pour contenir tous les caractères d'une ligne avec le caractère '\0'.

  • Et ici, j'utilise une boucle pour balayer chaque caractère de la chaîne, l'un après l'autre, à l'aide de la fonction getchar() jusqu'à ce que l'utilisateur entre '\n' ou EOF

    //the function to read lines of variable length
    
    char* scan_line(char *line)
    {
        int ch; //as getchar() returns `int`
    
        if( (line = malloc(sizeof(char))) == NULL) //allocating memory
        {
            //checking if allocation was successful or not
            printf("unsuccessful allocation");
            exit(1);
        }
    
        line[0]='\0';
    
        for(int index = 0; ( (ch = getchar())!='\n' ) && (ch != EOF) ; index++)
        {
            if( (line = realloc(line, (index + 2)*sizeof(char))) == NULL )
            {
                //checking if reallocation was successful or not
                printf("unsuccessful reallocation");
                exit(1);
            }
    
            line[index] = (char) ch; //type casting `int` to `char`
            line[index + 1] = '\0'; //inserting null character at the end
        }
    
        return line;
    }  
    
  • Vous pouvez maintenant lire une ligne complète de cette façon:

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

Voici un exemple de programme utilisant la fonction scan_line():

#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
}

exemple: :

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

exemple de sortie:

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

Quelque chose comme ça:

unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
    char * strbfr;
    int c;
    unsigned int i;
    i = 0;
    strbfr = (char*)malloc(sizeof(char));
    if(strbfr==NULL) goto error;
    while( (c = getchar()) != '\n' && c != EOF )
    {
        strbfr[i] = (char)c;
        i++;
        strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
        //on realloc error, NULL is returned but original buffer is unchanged
        //NOTE: the buffer WILL NOT be NULL terminated since last
        //chracter came from console
        if(strbfr==NULL) goto error;
    }
    strbfr[i] = '\0';
    *pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
    return i + 1; 
    error:
    *pStrBfr = strbfr;
    return i + 1;
}
0
user2074102

Je suis tombé sur le même problème il y a quelque temps, c'était ma solution, j'espère que ça aide.

/*
 * 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;
}
0
dsm

Sur les systèmes BSD et Android, vous pouvez également utiliser fgetln:

#include <stdio.h>

char *
fgetln(FILE *stream, size_t *len);

Ainsi:

size_t line_len;
const char *line = fgetln(stdin, &line_len);

La line n'est pas terminée par un null et contient finalement \n (ou celui que votre plate-forme utilise). Il devient invalide après la prochaine opération d'E/S sur le flux.

0
wonder.mice