问题
I'm just a young computer science student, and currently I'm a bit confused about what is the best practice to read a string from stdin. I know that there are a lot of ways to do that, some safer than other, and so on... I'm currently in need of a function that prevents buffer overflow and appends a null terminator character (\0) to the end of the string. I found fgets really useful for that, but ... It stops reading with \n or EOF! What if I want the user to input more than one line at time? Are there some other function that can help me doing that? I'm sorry if this question can seem silly to some of you, but please, understand me! Any help would be appreciated.
回答1:
#define INITALLOC 16 /* #chars initally alloced */
#define STEP 8 /* #chars to realloc by */
#define END (-1) /* returned by getline to indicate EOF */
#define ALLOCFAIL 0 /* returned by getline to indicate allocation failure */
int getline(char **dynline)
{
int i, c;
size_t nalloced; /* #chars currently alloced */
if ((*dynline = malloc(INITALLOC)) == NULL)
return ALLOCFAIL;
nalloced = INITALLOC;
for (i = 0; (c = getchar()) != EOF; ++i) {
/* buffer is full, request more memory */
if (i == nalloced)
if ((*dynline = realloc(*dynline, nalloced += STEP)) == NULL)
return ALLOCFAIL;
/* store the newly read character */
(*dynline)[i] = c;
}
/* zero terminate the string */
(*dynline)[i] = '\0';
if (c == EOF)
return END;
return i+1; /* on success, return #chars read successfully
(i is an index, so add 1 to make it a count */
}
This function allocates memory dynamically, so the caller needs to free
the memory.
This code is not perfect. If, on reallocation, there is a failure, NULL
overwrites the previous, perfectly-good data causing a memory leak and loss of data.
回答2:
If a newline is encountered and fgets
returns, you can run it again as many times as necessary to read as many lines as you want. A loop is useful for this.
If EOF is encountered, you have reached the end of the file(/stream) and there is no point in running it again, because there is nothing left to read.
An example showing the logic to read an entire string to EOF from stdin follows.
There are many ways to do this, and this is just one, but it shows the general logic.
The result buffer grows as the input is read, and there are no bounds on this – so if EOF is never reached you will eventually run out of memory and the program will exit. A simple check could avoid this, or depending on your application you could process the data as it comes in and not need to store it all.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LINE_BUFFER_SIZE 256
// Each time this is exhausted, the buffer will be increased in size by this amount again.
#define INITIAL_BUFFER_SIZE 2048
int main (int argc, char **argv) {
char *result = malloc(INITIAL_BUFFER_SIZE);
if (!result) {
// Out of memory.
return 1;
}
size_t totalBytesRead = 0;
size_t bytesAllocated = INITIAL_BUFFER_SIZE;
char buf[LINE_BUFFER_SIZE];
while (fgets(buf, LINE_BUFFER_SIZE, stdin)) {
size_t bytesRead = strlen(buf);
size_t bytesNeeded = totalBytesRead + bytesRead + 1;
if (bytesAllocated < bytesNeeded) {
char *newPtr = realloc(result, bytesAllocated + INITIAL_BUFFER_SIZE);
if (newPtr) {
result = newPtr;
bytesAllocated += INITIAL_BUFFER_SIZE;
}
else {
// Out of memory.
free(result);
return 1;
}
}
memcpy(result + totalBytesRead, buf, bytesRead);
totalBytesRead += bytesRead;
}
result[totalBytesRead] = '\0';
// result contains the entire contents from stdin until EOF.
printf("%s", result);
free(result);
return 0;
}
回答3:
On POSIX systems, you have getline. It is able to read an arbitrarily wide line (till exhausting resources) in heap allocated memory.
You can also repeatedly call fgetc ... (BTW, you should define exactly what is a string for you)
On Linux, you can read an editable line from the terminal (that is, stdin
when it is a tty) using GNU readline.
To read some kind of strings, you might use fscanf with e.g. %50s
or %[A-Z]
etc...
And you can read an array (of bytes, or some other binary data) using fread
You might read an entire line and parse it later (perhaps using sscanf
). You could read several lines and build some strings in heap memory (e.g. using asprintf or strdup on systems having it) from them.
来源:https://stackoverflow.com/questions/34695410/is-there-a-good-alternative-to-fgets