I want to read the name entered by my user using C programmes.
For this I wrote:
char name[20];
printf("Enter name: ");
gets(name);
开发者_开发百科But using gets
is not good, so what is a better way?
You should never use gets
(or scanf
with an unbounded string size) since that opens you up to buffer overflows. Use the fgets
with a stdin
handle since it allows you to limit the data that will be placed in your buffer.
Here's a little snippet I use for line input from the user:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
This allows me to set the maximum size, will detect if too much data is entered on the line, and will flush the rest of the line as well so it doesn't affect the next input operation.
You can test it with something like:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
I think the best and safest way to read strings entered by the user is using getline()
Here's an example how to do this:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *buffer = NULL;
int read;
unsigned int len;
read = getline(&buffer, &len, stdin);
if (-1 != read)
puts(buffer);
else
printf("No line read...\n");
printf("Size read: %d\n Len: %d\n", read, len);
free(buffer);
return 0;
}
On a POSIX system, you probably should use getline
if it's available.
You also can use Chuck Falconer's public domain ggets
function which provides syntax closer to gets
but without the problems. (Chuck Falconer's website is no longer available, although archive.org has a copy, and I've made my own page for ggets.)
I found an easy and nice solution:
char*string_acquire(char*s,int size,FILE*stream){
int i;
fgets(s,size,stream);
i=strlen(s)-1;
if(s[i]!='\n') while(getchar()!='\n');
if(s[i]=='\n') s[i]='\0';
return s;
}
it's based on fgets but free from '\n' and stdin extra characters (replacing fflush(stdin) doesn't works on all OS, useful if you have to acquire strings after this).
On BSD systems and Android you can also use fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Like so:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
The line
is not null terminated and contains \n
(or whatever your platform is using) in the end. It becomes invalid after the next I/O operation on stream. You are allowed to modify the returned line
buffer.
Using scanf
removing any blank spaces before the string is typed and limiting the amount of characters to be read:
#define SIZE 100
....
char str[SIZE];
scanf(" %99[^\n]", str);
/* Or even you can do it like this */
scanf(" %99[a-zA-Z0-9 ]", str);
If you do not limit the amount of characters to be read with scanf
it can be as dangerous as gets
ANSI C unknown maxinum length solution
Just copy from Johannes Schaub's https://stackoverflow.com/a/314422/895245
Don't forget to free
the returned pointer once you're done with it.
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;
}
This code uses malloc
to allocate 100 chars. Then it fetches char by char from the user. If the user reaches 101 chars, it doubles the buffer with realloc
to 200. When 201 is reached, it doubles again to 400 and so on until memory blows.
The reason we double rather say, just adding 100 extra every time, is that increasing the size of a buffer with realloc
can lead to a copy of the old buffer, which is a potentially expensive operation.
Arrays must be contiguous in memory because we wan to be able to random access them efficiently by memory address. Therefore if we had in RAM:
content buffer[0] | buffer[1] | ... | buffer[99] | empty | empty | int i
RAM address 1000 | 1001 | | 1100 | 1101 | 1102 | 1103
we wouldn't be able to just increase the size of buffer
, as it would overwrite our int i
. So realloc
would need to find another location in memory that has 200 free bytes, and then copy the old 100 bytes there and free the 100 old bytes.
By doubling rather than adding, we quickly reach the order of magnitude of the current string size, since exponentials grow really fast, so only a reasonable number of copies is done.
You can use scanf function to read string
scanf("%[^\n]",name);
i don't know about other better options to receive string,
精彩评论