Qual è il modo più semplice per leggere una riga intera in un programma della console C Il testo inserito potrebbe avere una lunghezza variabile e non possiamo fare alcuna supposizione sul suo contenuto.
Qual è il modo più semplice per leggere una riga intera in un programma della console C Il testo inserito potrebbe avere una lunghezza variabile e non possiamo fare alcuna supposizione sul suo contenuto.
Risposte:
È necessaria la gestione dinamica della memoria e utilizzare la fgets
funzione per leggere la riga. Tuttavia, sembra che non ci sia modo di vedere quanti caratteri legge. Quindi usi 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;
}
Nota : non usare mai gets! Non esegue il controllo dei limiti e può sovraccaricare il buffer
fgetc_unlocked
se la sicurezza dei thread non è un problema ma le prestazioni lo sono.
getline()
è diverso dalla getline()
funzione standard POSIX .
Un'implementazione molto semplice ma non sicura per leggere la riga per l'allocazione statica:
char line[1024];
scanf("%[^\n]", line);
Un'implementazione più sicura, senza possibilità di buffer overflow, ma con la possibilità di non leggere l'intera riga, è:
char line[1024];
scanf("%1023[^\n]", line);
Non la "differenza di uno" tra la lunghezza specificata che dichiara la variabile e la lunghezza specificata nella stringa di formato. È un manufatto storico.
gets
stato rimosso dallo standard del tutto
Quindi, se stavi cercando argomenti di comando, dai un'occhiata alla risposta di Tim. Se vuoi solo leggere una riga dalla 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;
}
Sì, non è sicuro, puoi eseguire il sovraccarico del buffer, non controlla la fine del file, non supporta le codifiche e molte altre cose. In realtà non ho nemmeno pensato se facesse NESSUNA di queste cose. Sono d'accordo, ho un po 'sbagliato :) Ma ... quando vedo una domanda come "Come leggere una riga dalla console in C?", Presumo che una persona abbia bisogno di qualcosa di semplice, come gets () e non 100 righe di codice come sopra. In realtà, penso che se provassi a scrivere quelle 100 righe di codice nella realtà, faresti molti più errori di quelli che avresti fatto se avessi scelto di ottenere;)
gets
non esiste più, quindi questo non funziona in C11.
getline
esempio eseguibile
Menzionato in questa risposta, ma ecco un esempio.
È POSIX 7 , alloca la memoria per noi e riutilizza bene il buffer allocato su un ciclo.
Puntatore newbs, leggi questo: Perché il primo argomento di getline è un puntatore al puntatore "char **" invece di "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;
}
implementazione glibc
Nessun POSIX? Forse vuoi dare un'occhiata all'implementazione di glibc 2.23 .
Risolve a getdelim
, che è un semplice superset POSIX getline
con un terminatore di riga arbitrario.
Raddoppia la memoria allocata ogni volta che è necessario un aumento e sembra thread-safe.
Richiede un po 'di espansione macro, ma è improbabile che tu faccia molto meglio.
len
qui, quando la lettura fornisce anche la lunghezza
man getline
. len
è la lunghezza del buffer esistente, 0
è magico e gli dice di allocare. Read è il numero di caratteri letti. La dimensione del buffer potrebbe essere maggiore di read
.
Molte persone, come me, vengono a questo post con il titolo corrispondente a ciò che viene cercato, anche se la descrizione parla di lunghezza variabile. Per la maggior parte dei casi, conosciamo in anticipo la lunghezza.
Se conosci la lunghezza in anticipo, prova di seguito:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
fonte: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Come suggerito, puoi usare getchar () per leggere dalla console fino a quando non viene restituito un end-of-line o un EOF, costruendo il tuo buffer. Può verificarsi un aumento dinamico del buffer se non si è in grado di impostare una dimensione di riga massima ragionevole.
Puoi anche usare fgets come un modo sicuro per ottenere una riga come stringa C con terminazione 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);
Se hai esaurito l'input della console o se l'operazione non è riuscita per qualche motivo, viene restituito eof == NULL e il buffer di riga potrebbe essere invariato (motivo per cui è utile impostare il primo carattere su "\ 0").
fgets non riempirà la riga [] e assicurerà che ci sia un null dopo l'ultimo carattere accettato in caso di ritorno riuscito.
Se è stata raggiunta la fine della riga, il carattere che precede la terminazione "\ 0" sarà un "\ n".
Se non ci sono "\ n" finali prima della fine "\ 0", è possibile che siano presenti più dati o che la richiesta successiva riporterà la fine del file. Dovrai fare un altro fgets per determinare quale è quale. (A questo proposito, il looping con getchar () è più semplice.)
Nel codice di esempio (aggiornato) sopra, se la riga [sizeof (line) -1] == '\ 0' dopo fgets riusciti, sai che il buffer è stato riempito completamente. Se quella posizione è preceduta da un "\ n" sai di essere stato fortunato. Altrimenti, ci sono più dati o una fine del file più avanti in stdin. (Quando il buffer non è riempito completamente, potresti essere ancora alla fine del file e potrebbe anche non esserci un '\ n' alla fine della riga corrente. Dato che devi scansionare la stringa per trovare e / o eliminare qualsiasi "\ n" prima della fine della stringa (il primo "\ 0" nel buffer), sono propenso a preferire utilizzare getchar () in primo luogo.)
Fai quello che devi fare per gestire il fatto che ci sono ancora più righe rispetto all'importo letto come primo blocco. Gli esempi di crescita dinamica di un buffer possono essere fatti funzionare con getchar o fgets. Ci sono alcuni casi limite difficili a cui prestare attenzione (come ricordare che l'input successivo inizi a memorizzare nella posizione di "\ 0" che ha terminato l'input precedente prima che il buffer fosse esteso).
Come leggere una riga dalla console in C?
Costruire la tua funzione è uno dei modi che ti aiuterebbe a leggere una riga dalla console
Sto usando l'allocazione dinamica della memoria per allocare la quantità di memoria richiesta
Quando stiamo per esaurire la memoria allocata, proviamo a raddoppiare la dimensione della memoria
E qui sto usando un ciclo per scansionare ogni carattere della stringa uno per uno usando la getchar()
funzione finché l'utente non inserisce '\n'
o EOF
carattere
infine rimuoviamo ogni ulteriore memoria allocata prima di restituire la riga
//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;
}
Ora potresti leggere un'intera riga in questo modo:
char *line = NULL;
line = scan_line(line);
Ecco un esempio di programma che utilizza la scan_line()
funzione:
#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
}
input di esempio:
Twinkle Twinkle little star.... in the sky!
output di esempio:
Twinkle Twinkle little star.... in the sky!
Mi sono imbattuto nello stesso problema qualche tempo fa, questa era la mia soluzione, spero che aiuti.
/*
* 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
per gestire il caso di errore. Tuttavia, non pensi di poterlo riutilizzare tmp_buf
, invece di malloc
inserirlo ripetutamente con la stessa dimensione?
has_err
per segnalare gli errori rende questa funzione non sicura per i thread e meno che comoda da usare. Non farlo in questo modo. Hai già indicato un errore restituendo NULL. C'è anche spazio per pensare che i messaggi di errore stampati non siano una buona idea in una funzione di libreria generica.
Su sistemi BSD e Android puoi anche utilizzare fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Così:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
Il line
non è terminato da null e contiene \n
(o qualunque cosa stia utilizzando la tua piattaforma) alla fine. Diventa non valido dopo la successiva operazione di I / O sullo stream.
Qualcosa come questo:
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;
}
Il modo migliore e più semplice per leggere una riga da una console è usare la funzione getchar (), per cui memorizzerai un carattere alla volta in un array.
{
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 );
}
Questa funzione dovrebbe fare quello che vuoi:
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 );
Spero che aiuti.
fgets( buffer, sizeof(buffer), file );
non sizeof(buffer)-1
. fgets
lascia spazio per il null finale.
while (!feof(file))
è sempre sbagliato e questo è solo un altro esempio di uso errato.