Quelle est la manière la 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.
Quelle est la manière la 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.
Réponses:
Vous avez besoin d'une gestion dynamique de la mémoire et utilisez la fgets
fonction pour lire votre ligne. Cependant, il ne semble y avoir aucun moyen de voir combien de caractères il a lu. 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 : n'utilisez jamais gets! Il ne vérifie pas les limites et peut déborder de votre tampon
fgetc_unlocked
si la sécurité des threads n'est pas un problème mais les performances le sont.
getline()
est différent de la getline()
fonction standard POSIX .
Une implémentation très simple mais non sécurisée 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 toute la ligne, est:
char line[1024];
scanf("%1023[^\n]", line);
Pas la «différence de 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.
gets
été complètement supprimé de la norme
Donc, si vous cherchiez des arguments de commande, jetez un œil à 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, cela ne vérifie pas la fin du fichier, il ne prend pas en charge les encodages et bien d'autres choses. En fait, je ne me suis même pas demandé si ça faisait quoi que ce soit. Je suis d'accord, 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 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 vous n'auriez fait si vous aviez choisi get;)
gets
n'existe plus, donc cela ne fonctionne pas en C11.
getline
exemple exécutable
Mentionné sur cette réponse mais voici un exemple.
C'est POSIX 7 , nous alloue de la mémoire, et réutilise joliment le tampon alloué sur une boucle.
Pointer newbs, lisez ceci: Pourquoi le premier argument de getline est-il un pointeur vers 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 la glibc
Pas de POSIX? Peut-être voulez-vous regarder l' implémentation de la glibc 2.23 .
Il résout en getdelim
, qui est un simple sur-ensemble POSIX getline
avec un terminateur de ligne arbitraire.
Il double la mémoire allouée chaque fois qu'une augmentation est nécessaire et semble sûr pour les threads.
Cela nécessite une certaine expansion macro, mais il est peu probable que vous fassiez beaucoup mieux.
len
ici, lorsque lu fournit la longueur aussi
man getline
. len
est la longueur du tampon existant, 0
est magique et lui dit d'allouer. La lecture correspond au nombre de caractères lus. La taille du tampon peut être supérieure à read
.
Beaucoup de gens, comme moi, viennent à cet article 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 à l'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
Comme suggéré, vous pouvez utiliser getchar () pour lire depuis la console jusqu'à ce qu'une fin de ligne ou un EOF soit renvoyé, en construisant votre propre tampon. Une croissance dynamique du tampon peut se produire si vous ne parvenez pas à définir une taille de ligne maximale raisonnable.
Vous pouvez également utiliser des fgets comme moyen sûr d'obtenir une ligne sous forme de chaîne C terminée par un 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 définir le premier caractère sur '\ 0' est pratique).
fgets ne remplira pas trop la ligne [] et s'assurera qu'il y a un nul 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» de fin sera un «\ n».
S'il n'y a pas de fin '\ n' avant la fin '\ 0', c'est peut-être qu'il y a plus de données ou que la prochaine requête signalera la fin de fichier. Vous devrez faire un autre fgets pour déterminer lequel est lequel. (À cet égard, faire une boucle avec getchar () est plus facile.)
Dans l'exemple de code (mis à jour) ci-dessus, si la ligne [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 en avance dans stdin. (Lorsque le tampon n'est pas complètement rempli, vous pouvez toujours être à une fin de fichier et il se peut qu'il n'y ait pas non plus de «\ n» à la fin de la ligne actuelle. Puisque vous devez analyser la chaîne pour trouver et / ou éliminer tout '\ n' avant la fin de la chaîne (le premier '\ 0' du tampon), j'ai tendance à préférer utiliser getchar () en premier lieu.)
Faites ce que vous devez faire pour faire en sorte qu'il y ait encore plus de lignes que le montant que vous lisez comme premier morceau. Les exemples de croissance dynamique d'un tampon peuvent être conçus pour fonctionner avec getchar ou fgets. Il y a quelques cas de bord délicats à surveiller (comme se souvenir que l'entrée suivante commence à stocker à la position du '\ 0' qui a mis fin à l'entrée précédente avant l'extension du tampon).
Comment lire une ligne de la console en C?
Construire votre propre fonction est l'un des moyens qui vous aiderait à lire une ligne depuis la console
J'utilise l' allocation de mémoire dynamique pour allouer la quantité de mémoire requise
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 scanner chaque caractère de la chaîne un par un en utilisant la getchar()
fonction jusqu'à ce que l'utilisateur entre '\n'
ou EOF
caractère
enfin nous supprimons toute mémoire supplémentaire allouée avant de renvoyer 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`
}
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;
}
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 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
}
exemple d'entrée:
Twinkle Twinkle little star.... in the sky!
exemple de sortie:
Twinkle Twinkle little star.... in the sky!
J'ai rencontré 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;
}
goto
pour gérer le cas d'erreur. Néanmoins, ne pensez-vous pas que vous pourriez le réutiliser tmp_buf
, au lieu de malloc
le réutiliser avec la même taille encore et encore dans la boucle?
has_err
pour signaler les erreurs rend cette fonction peu sûre pour les threads et moins confortable à utiliser. Ne fais pas ça de cette façon. Vous indiquez déjà une erreur en renvoyant NULL. Il est également possible 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.
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);
Le line
n'est pas terminé par 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;
}
Le moyen le meilleur et le plus simple de lire une ligne à partir d'une console est d'utiliser la fonction getchar (), dans laquelle vous stockerez un caractère à la fois dans un tableau.
{
char message[N]; /* character array for the message, you can always change the character length */
int i = 0; /* loop counter */
printf( "Enter a message: " );
message[i] = getchar(); /* get the first character */
while( message[i] != '\n' ){
message[++i] = getchar(); /* gets the next character */
}
printf( "Entered message is:" );
for( i = 0; i < N; i++ )
printf( "%c", message[i] );
return ( 0 );
}
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 ça aide.
fgets( buffer, sizeof(buffer), file );
pas sizeof(buffer)-1
. fgets
laisse de l'espace pour le null de fin.
while (!feof(file))
c'est toujours faux et ce n'est qu'un autre exemple d'utilisation erronée.