Question

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

Était-ce utile?

La solution

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

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

Autres conseils

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

Une implémentation très simple mais peu sûre à lire en ligne pour l'allocation statique:

char line[1024];

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

Une implémentation plus sûre, sans possibilité de débordement de mémoire tampon, mais avec la possibilité de ne pas lire toute la ligne, 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.

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.

Donc, si vous cherchiez des arguments de commande, jetez un œil à la réponse de Tim. Si vous voulez juste lire une ligne de 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 suis même pas demandé si cela avait été fait. Je suis d'accord, j'ai un peu foiré :) Mais ... quand je vois une question du type "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 de 100 lignes de code comme ci-dessus. En fait, je pense que si vous essayez d’écrire ces 100 lignes de code en réalité, vous feriez beaucoup plus d’erreurs que si vous aviez choisi ce qui vous convient;)

getline exemple exécutable

Mentionnez dans cette réponse , mais voici un exemple.

Il s'agit de POSIX 7 , alloue de la mémoire pour nous, et réutilise joliment le tampon alloué sur une boucle.

Pointeur newbs, lisez ceci: Pourquoi le premier argument de getline est-il un pointeur à un pointeur" quot ** "? 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 la glibc

Pas de POSIX? Vous voulez peut-être regarder la implémentation de la glibc 2.23 .

Il se résout en 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 donne une apparence thread-safe.

Cela nécessite une certaine expansion macro, mais vous ne ferez probablement pas 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 croissance 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 utile de définir le premier caractère sur '\ 0'). <

fgets ne remplira pas trop la ligne [] et garantira la nullité 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 lecture en boucle avec getchar () est plus facile.)

Dans l'exemple de code (mis à jour) ci-dessus, si ligne [sizeof (ligne) -1] == '\ 0' après la réussite de la création d'un gadget, 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 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 faut se méfier des cas difficiles (par exemple, ne pas oublier que la prochaine entrée doit commencer à être stockée à l'emplacement du '\ 0' qui a terminé l'entrée précédente avant l'extension du tampon).

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 d'avance, 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

  

Comment lire une ligne de la console en C?

  • Créer votre propre fonction est l’un des moyens permettant de lire une ligne depuis la console dans C .

  • J'utilise l'allocation de mémoire dynamique strong> 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, je me sers d'une boucle pour balayer chaque caractère de la chaîne un à un à l'aide de la fonction getchar () jusqu'à ce que l'utilisateur entre '\ n' ou caractère 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 d'entrée:

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

exemple de sortie:

<*>

Je suis tombé sur le même problème il y a quelque temps, c'était ma solution, j'espère que cela 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;
}

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

#include <stdio.h>

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

Comme si:

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

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

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

Cette fonction doit faire ce que vous voulez:

char* readLine( FILE* file )
 {
 char buffer[1024];
 char* result = 0;
 int length = 0;

 while( !feof(file) )
  {
  fgets( buffer, sizeof(buffer), file );
  int len = strlen(buffer);
  buffer[len] = 0;

  length += len;
  char* tmp = (char*)malloc(length+1);
  tmp[0] = 0;

  if( result )
   {
   strcpy( tmp, result );
   free( result );
   result = tmp;
   }

  strcat( result, buffer );

  if( strstr( buffer, "\n" ) break;
  }

 return result;
 }

char* line = readLine( stdin );
/* Use it */
free( line );

J'espère que cela vous aidera.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top