I am trying to use a function that returns char * buffer to trap random user input from standard input and return a char* (that is, a string) with the input until '\n' or '\0' is detected as input.
However, the function user_input() crashed at the second occurence of c = getchar(); near the end of the function (at the //Error Here comment).
What am I doing wrong?
char* user_input(){
char *buffer=NULL;
size_t num_read = 0;
size_t buffer_size = 100;
char c = '\0';
char *new_buffer=NULL;
buffer = malloc(buffer_size);
fflush(stdin);
c = getchar();
while ( c != '\n' || c !='\0' ) {
if (num_read >= buffer_size) {
new_buffer=NULL;
buffer_size *= 2; // try a buffer that's twice as big as before
new_buffer = realloc(buffer, buffer_size);
if (new_buffer == NULL) {
free(buffer);
return NULL;
/* Abort - out of memory */
}
buffer = new_buffer;
}
buffer[num_read] = c;
num_read++;
c = getchar(); //Error Here
}//while
buffer[num_read] = '\0';
return buffer;
}
while ( c != '\n' || c !='\0' ) {
should use && instead of || or the loop will never exit
while ( c != '\n' && c !='\0' ) {
I built it and it worked although there are other bugs in the code such as the "buffer[num_read] = '\0';" at the end could overflow the buffer if there are exactly buffer_size items in the array.
Here are the issues that I found:
You cannot call fflush() on stdin. It leads to undefined behavior. fflush() is only for output streams.
The type for c should be an int. The getchar() will return EOF when there is no more input, and EOF is a value not equal to any valid value for unsigned char. That is why getchar() returns an int, so your variable receiving the return value of getchar() should match that type.
The sense of the while check is incorrect. You will only leave the loop if c equals both \n and \0 at the same time, which is not possible. Thus the loop will never exit. If you actually ended the input given to your program, you probably entered an infinite loop, continually allocating until your system ran out of memory. So, you want to leave the loop if c equals \n or \0.
You should add a check to leave the loop if c is EOF.
You should make sure num_read is less than buffer_size before you assign the \0 character. This can be done by moving your realloc() code to be at the bottom of the while loop instead of at the top.
With these changes, the code looks like:
char *user_input() {
char *buffer = NULL;
size_t num_read = 0;
size_t buffer_size = 100;
int c = '\0';
char *new_buffer = NULL;
buffer = malloc(buffer_size);
c = getchar();
while (!(c == EOF || c == '\n' || c == '\0')) {
assert(num_read < buffer_size);
buffer[num_read++] = c;
if (num_read >= buffer_size) {
buffer_size *= 2; // try a buffer that's twice as big as before
new_buffer = realloc(buffer, buffer_size);
if (new_buffer == NULL) {
free(buffer);
return NULL;
}
buffer = new_buffer;
}
c = getchar();
} //while
assert(num_read < buffer_size);
buffer[num_read] = '\0';
return buffer;
}
I have modified your code below. Try to run it as is, using the 'q' character at first, then you can experiment with other exit criteria later. By the way, your code worked without modification and did not crash for me (unless buffer overrun occured, but it would never leave the while loop otherwise (as Brian Walker pointed out). In any case, Try this in any ANSI C compiler: (just don't enter more than 1000 entries :)
#include <ansi_c.h>
char* user_input(void);
int main(void)
{
char *buf;
buf = malloc(1000);
//SearchDirectory("C:\\dev");
sprintf(buf, "%s", user_input());
printf("%s", buf);
free(buf);
return 0;
}
char* user_input(void){
char *buffer=NULL;
size_t num_read = 0;
size_t buffer_size = 100;
char c = '\0';
char *new_buffer=NULL;
buffer = malloc(buffer_size);
fflush(stdin);
c = getchar();
while ( c != 'q' ) {
if (num_read >= buffer_size) {
new_buffer=NULL;
buffer_size *= 2; // try a buffer that's twice as big as before
new_buffer = realloc(buffer, buffer_size);
if (new_buffer == NULL) {
free(buffer);
return NULL;
/* Abort - out of memory */
}
buffer = new_buffer;
}
buffer[num_read] = c;
num_read++;
c = getchar();
}//while
buffer[num_read] = '\0';
return buffer;
}
Related
I have a college exercise to do, that consists in writing a bank in C and I do know that scanf is a really buggy function and fgets reads the '\n', that I don't really want to be read. So I have written new functions trying to solve this problem:
char *readstr(char *buff, size_t bytes) {
char *ptr;
for (ptr = buff;
(*ptr = getchar()) != '\n' && *ptr != EOF && ptr - buff < bytes - 1;
ptr++)
;
int c = *ptr;
*ptr = '\0';
if (c != EOF && c != '\n') {
while ((c = getchar()) != '\n' && c != EOF)
;
}
return buff;
}
int readint() {
char buffer[256] = "";
readstr(buffer, 256);
return atoi(strpbrk(buffer, "0123456789"));
}
double readfloat() {
char buffer[256] = "";
readstr(buffer, 256);
return atof(strpbrk(buffer, "0123456789"));
}
char readchar() {
char buffer[2] = "";
readstr(buffer, 2);
return *buffer;
}
until now, I wrote these ones. Any advice or suggestion? a more elegant one or simpler solution? apparently they work, but I don't know if this is the best approach.
scanf is not buggy, it's simply hard to use correctly.
Anyway, you can use fgets and remove manually the newline character. A way to do this is by using the strcspn function like follows:
fgets(str, size, stdin);
str[strcspn(str, "\n")] = 0;
Your goal seems to read a line of input into a buffer with a given size, not storing the trailing newline and discarding any excess characters present on the line. You do need to read the newline so it does not linger in the input stream, but you do not want to store it into the destination array as fgets() does.
Note that this can be achieved with scanf() this way:'
char buf[100] = "";
if (scanf("%99[^\n]", buf) == EOF) {
// handle end of file
} else {
// line was read into buf or empty line was detected and buf was not modified
scanf("%*[^\n]"); // consume extra bytes on the line if any
scanf("%*1[\n]"); // consume the newline if present
}
This is very cumbersome, and it gets even worse if the buffer size is a variable value. As many savvy C programmers noted, scanf is not buggy, it is just difficult to use correctly. I would even say its semantics are confusing and error prone, yet it can be used safely, unlike gets(), which was removed from recent versions of the C Standard because any arbitrary long input can cause undefined behavior with it.
Reading a line and discarding the trailing newline can be done with fgets(), but combining the reading the line, discarding extra bytes and consuming the newline may be useful as a separate function.
There are problems in your code:
you store the return value of getchar() directly into the char array, removing the distinction between EOF and the char value '\377' on architectures with signed chars, while *ptr != EOF would never match on architectures where char is unsigned by default. You must store it into an int variable.
there is no way to tell if the end of file was reached: the function returns an empty string at end of file, just like it does for blank lines int the stream
the buffer size cannot be zero.
truncation cannot be detected: instead of returning the destination array, you could return the number of characters needed for the full line and -1 at end of file.
calling atoi(strpbrk(buffer, "0123456789")) poses multiple problems: strpbrk can return a null pointer, causing undefined behavior, it will skip a leading sign, and atoi is not fully defined for contents representing values out of range for type int.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int readstr(char *buff, size_t bytes) {
size_t pos = 0;
int c;
while ((c = getchar()) != EOF && c != '\n') {
if (pos + 1 < bytes)
buff[pos] = (char)c;
pos++;
}
if (size > 0) {
if (pos < size)
buff[pos] = '\0';
else
buff[size - 1] = '\0';
}
if (c == EOF && pos == 0)
return -1;
return (int)pos;
}
int readint(void) {
char buffer[256];
long n;
if (readstr(buffer, sizeof buffer) < 0)
return -1;
n = strtol(buffer, NULL, 0);
#if LONG_MIN < INT_MIN
if (n < INT_MIN) {
errno = ERANGE;
return INT_MIN;
}
#endif
#if LONG_MAX > INT_MAX
if (n > INT_MAX) {
errno = ERANGE;
return INT_MAX;
}
#endif
return (int)n;
}
double readdouble(void) {
char buffer[256];
if (readstr(buffer, sizeof buffer) < 0)
return NAN;
else
return strtod(buffer, NULL);
}
int readchar(void) {
char buffer[2] = "";
if (readstr(buffer, sizeof buffer) < 0)
return EOF;
else
return (unsigned char)*buffer;
}
I wrote this code, but inserts garbage in the start of string:
void append(char *s, char c) {
int len = strlen(s);
s[len] = c;
s[len + 1] = '\0';
}
int main(void) {
char c, *s;
int i = 0;
s = malloc(sizeof(char));
while ((c = getchar()) != '\n') {
i++;
s = realloc(s, i * sizeof(char));
append(s, c);
}
printf("\n%s",s);
}
How can I do it?
There are multiple problems in your code:
you iterate until you read a newline ('\n') from the standard input stream. This will cause an endless loop if the end of file occurs before you read a newline, which would happen if you redirect standard input from an empty file.
c should be defined as int so you can test for EOF properly.
s should be null terminated at all times, you must set the first byte to '\0' after malloc() as this function does not initialize the memory it allocates.
i should be initialized to 1 so the first realloc() extends the array by 1 etc. As coded, your array is one byte too short to accommodate the extra character.
you should check for memory allocation failure.
for good style, you should free the allocated memory before exiting the program
main() should return an int, preferably 0 for success.
Here is a corrected version:
#include <stdio.h>
#include <stdlib.h>
/* append a character to a string, assuming s points to an array with enough space */
void append(char *s, char c) {
size_t len = strlen(s);
s[len] = c;
s[len + 1] = '\0';
}
int main(void) {
int c;
char *s;
size_t i = 1;
s = malloc(i * sizeof(char));
if (s == NULL) {
printf("memory allocation failure\n");
return 1;
}
*s = '\0';
while ((c = getchar()) != EOF && c != '\n') {
i++;
s = realloc(s, i * sizeof(char));
if (s == NULL) {
printf("memory allocation failure\n");
return 1;
}
append(s, c);
}
printf("%s\n", s);
free(s);
return 0;
}
when you call strlen it searches for a '\0' char to end the string. You don't have this char inside your string to the behavior of strlen is unpredictable.
Your append function is acually good.
Also, a minor thing, you need to add return 0; to your main function. And i should start from 1 instead if 0.
Here is how it should look:
int main(void){
char *s;
size_t i = 1;
s = malloc (i * sizeof(char));//Just for fun. The i is not needed.
if(s == NULL) {
fprintf(stderr, "Coul'd not allocate enough memory");
return 1;
}
s[0] = '\0';
for(char c = getchar(); c != '\n' && c != EOF; c = getchar()) {//it is not needed in this case to store the result as an int.
i++;
s = realloc (s,i * sizeof(char) );
if(s == NULL) {
fprintf(stderr, "Coul'd not allocate enough memory");
return 1;
}
append (s,c);
}
printf("%s\n",s);
return 0;
}
Thanks for the comments that helped me improve the code (and for my english). I am not perfect :)
The inner realloc needs to allocate one element more (for the trailing \0) and you have to initialize s[0] = '\0' before starting the loop.
Btw, you can replace your append by strcat() or write it like
size_t i = 0;
s = malloc(1);
/* TODO: check for s != NULL */
while ((c = getchar()) != '\n') {
s[i] = c;
i++;
s = realloc(s, i + 1);
/* TODO: check for s != NULL */
}
s[i] = '\0';
I'm using this function to read, char by char, a text file or a stdin input
void readLine(FILE *stream, char **string) {
char c;
int counter = 0;
do {
c = fgetc(stream);
string[0] = (char *) realloc (string[0], (counter+1) * sizeof(char));
string[0][counter++] = c;
} while(c != ENTER && !feof(stream));
string[counter-1] = '\0';
}
But when I call it, my program crashed and I really don't know why, because I don't forget the 0-terminator and I'm convinced that I stored correctly the char sequence. I've verified the string length, but it appears alright.
This is an error:
do {
c = fgetc(stream);
// What happens here?!?
} while(c != ENTER && !feof(stream));
"What happens here" is that you add c to string before you've checked for EOF, whoops.
This is very ungood:
string[0] = (char *) realloc (string[0], (counter+1) * sizeof(char));
in a loop. realloc is a potentially expensive call and you do it for every byte of input! It is also a silly and confusing interface to ask for a pointer parameter that has (apparently) not been allocated anything -- passing in the pointer usually indicates that is already done. What if string were a static array? Instead, allocate in chunks and return a pointer:
char *readLine (FILE *stream) {
// A whole 4 kB!
int chunksz = 4096;
int counter = 0;
char *buffer = malloc(chunksz);
char *test;
int c;
if (!buffer) return NULL;
while (c = fgetc(stream) && c != ENTER && c != EOF) {
buffer[counter++] = (char)c;
if (counter == chunksz) {
chunksz *= 2;
test = realloc(buffer, chunksz);
// Abort on out-of-memory.
if (!test) {
free(buffer);
return NULL;
} else buffer = test;
}
}
// Now null terminate and resize.
buffer[counter] = '\0';
realloc(buffer, counter + 1);
return buffer;
}
That is a standard "power of 2" allocation scheme (it doubles). If you really want to submit a pointer, pre-allocate it and also submit a "max length" parameter:
void *readLine (FILE *stream, char *buffer, int max) {
int counter = 0;
int c;
while (
c = fgetc(stream)
&& c != ENTER
&& c != EOF
&& counter < max - 1
) buffer[counter++] = (char)c;
// Now null terminate.
buffer[counter] = '\0';
}
There are a few issues in this code:
fgetc() returns int.
Don't cast the return value of malloc() and friends, in C.
Avoid using sizeof (char), it's just a very clumsy way of writing 1, so multiplication by it is very redundant.
Normally, buffers are grown more than 1 char at a time, realloc() can be expensive.
string[0] would be more clearly written as *string, since it's not an array but just a pointer to a pointer.
Your logic around end of file means it will store the truncated version of EOF, not very nice.
Change this line
string[counter-1] = '\0';
to
string[0][counter-1] = '\0';
You want to terminate string stored at string[0].
How do I constantly get user input (strings) until enter is pressed in C just like string class in C++?
I don't know the input size so I can't declare a variable of fixed size or even I can't allocate memory dynamically using malloc() or calloc().
Is there any way to implement this as a separate function?
As H2CO3 said, you should allocate a buffer with malloc(), then resize it with realloc() whenever it fills up. Like this:
size_t bufsize = 256;
size_t buf_used = 0;
int c;
char *buf = malloc(bufsize);
if (buf == NULL) { /* error handling here */ }
while ((c = fgetc(stdin)) != EOF) {
if (c == '\n') break;
if (buf_used == bufsize-1) {
bufsize *= 2;
buf = realloc(buf, bufsize);
if (buf == NULL) { /* error handling here */ }
}
buf[buf_used++] = c;
}
buf[buf_used] = '\0';
Use exponential storage expansion:
char *read_a_line(void)
{
size_t alloc_size = LINE_MAX;
size_t len = 0;
char *buf = malloc(LINE_MAX); // should be good for most, euh, *lines*...
if (!buf)
abort();
int c;
while ((c = fgetc(stdin)) != '\n' && c != EOF) {
if (len >= alloc_size) {
alloc_size <<= 1;
char *tmp = realloc(buf, alloc_size);
if (!tmp)
abort(); // or whatever
buf = tmp;
}
buf[len++] = c;
}
if (len >= alloc_size) {
alloc_size++;
char *tmp = realloc(buf, alloc_size);
if (!tmp)
abort(); // or whatever
buf = tmp;
}
buf[len] = 0;
return buf;
}
In C, you have little choice: If you want to input a string of unbounded length, have to use allocations in a loop. Whether you use realloc() or a linked list of buffers, it comes down to reading (usually through fgets()), reading some more, and so on until the buffer you've just read contains a \n.
Then, depending on the method, you either already have a contiguous buffer (the realloc method) or just need to concatenate them all (the linked list method). Then you can return.
If you're lucky, your platform comes with the extension function getline() that does the realloc method for you. If not, you'll have to write it yourself.
This is how I've done it but I'm not sure this is the preferred idiom:
FILE *fp = fopen(argv[0], "r");
// handle fopen() returning NULL
while (!feof(fp)) {
char buffer[80]; // statically allocated, may replace this later with some more sophisticated approach
int num_chars = 0;
for (int ch = fgetc(fp); ch != EOF && ch != '\n'; ch = fgetc()) {
buffer[num_chars++] = ch;
}
// null-terminate the string
buffer[num_chars] = '\0';
printf("%s\n", buffer);
}
Is this okay, any suggestions to improve this?
If you are not going to use fgets() (perhaps because you want to remove the newline, or you want to deal with "\r", "\n" or "\r\n" line endings, or you want to know how many characters were read), you can use this as a skeleton function:
int get_line(FILE *fp, char *buffer, size_t buflen)
{
char *end = buffer + buflen - 1; /* Allow space for null terminator */
char *dst = buffer;
int c;
while ((c = getc(fp)) != EOF && c != '\n' && dst < end)
*dst++ = c;
*dst = '\0';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
It recognizes only newline as the end of line; it drops the newline. It does not overflow the buffer; it does not discard excess characters, so if called upon to read a very long line, it will read the line in chunks; it returns the number of characters read. If you need to distinguish between overflow and a line that happens to be the length of the buffer - 1, then you probably need to preserve the newline - with consequential changes in the code:
int get_line(FILE *fp, char *buffer, size_t buflen)
{
char *end = buffer + buflen - 1; /* Allow space for null terminator */
char *dst = buffer;
int c;
while ((c = getc(fp)) != EOF && dst < end)
{
if ((*dst++ = c) == '\n')
break;
}
*dst = '\0';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
There are endless minor variants on this, such as discarding any excess characters if the line has to be truncated. If you want to handle DOS, (old) Mac or Unix line endings, then borrow a leaf out of the CSV code from "The Practice of Programming" by Kernighan & Pike (an excellent book) and use:
static int endofline(FILE *ifp, int c)
{
int eol = (c == '\r' || c == '\n');
if (c == '\r')
{
c = getc(ifp);
if (c != '\n' && c != EOF)
ungetc(c, ifp);
}
return(eol);
}
Then you can use that in place of the c != '\n' test:
int get_line(FILE *fp, char *buffer, size_t buflen)
{
char *end = buffer + buflen - 1; /* Allow space for null terminator */
char *dst = buffer;
int c;
while ((c = getc(fp)) != EOF && !endofline(fp, c) && dst < end)
*dst++ = c;
*dst = '\0';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
The other alternative way of dealing with the whole process is using fread() and fwrite():
void copy_file(FILE *in, FILE *out)
{
char buffer[4096];
size_t nbytes;
while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), in)) != 0)
{
if (fwrite(buffer, sizeof(char), nbytes, out) != nbytes)
err_error("Failed to write %zu bytes\n", nbytes);
}
}
In context, you'd open the file and check it for validity, then call:
copy_file(fp, stdout);
You're risking buffer overflow if the user inputs 80 characters or more.
I'm with ThiefMaster: you should use fgets(), instead. Read the input into a buffer that's larger than any legitimate input and then check that the last character is a newline.
Unless you're hoping to get a ultra-high efficient way to set the number of characters read, use fgets().
Replacing your example with a similar but different simple fgets(), you "lose" the num_chars variable.
fgets(buffer, sizeof buffer, stdin);
fputs(buffer, stdout); /* buffer contains a '\n' */
If you need to remove the last '\n'
buffer[0] = 0;
if (!fgets(buffer, sizeof buffer, stdin)) /* error or eof */;
num_chars = strlen(buffer);
if (num_chars && (buffer[num_chars - 1] == '\n')) buffer[--num_chars] = 0;
puts(buffer); /* add a '\n' to output */
If the strings are really humongous (like 42 Mega bytes worth), you may be better off reading character by character and keeping count with num_chars than using fgets first and strlen later.
If you need every char in order to inspect it or modify or whatever else then use fgets.
For everything else, use fgets.
fgets (buffer, BUFFER_SIZE, fp);
Note that fgets will read until a new line or EOF is reached (or the buffer is full of course). New line character "\n" is also appended to the string if read from the file. Null character is also appended.
fgets returns :
On success, the function returns the same str parameter.
If the End-of-File is encountered and no characters have been read, the contents of str remain unchanged and a null pointer is returned.
If an error occurs, a null pointer is returned.
Use either ferror or feof to check whether an error happened or the End-of-File was reached.
No linesize-limit und strictly C89 (your code is only C99) like:
FILE *fp = fopen(argv[0], "r");
size_t len=1;
char c, *buffer=calloc(1,1);
/* handle fopen() returning NULL*/
while( c=fgetc(fp),!feof(fp) )
if( c=='\n' )
{
puts(buffer);
len=1;
*buffer=0;
}
else
strncat(buffer=realloc(buffer,++len),&c,1); /* check for NULL needed */
puts(buffer);
free(buffer);
fclose(fp);
#include<stdio.h>
void main()
{
FILE *fp;
char c;
int ch=0,w=0,l=0;
fp=fopen("c:\read.txt","w");
clrscr();
if(fp==NULL)
{
printf("\n\n\tDOES NOT EXIXST");
getch();
exit(0);
}
while(!feof(fp))
{
c=fgetc(fp);
ch++;
if(c==' ')
{
w++;
}
if(c=='\n')
{
l++;
w++;
}
}
printf("\n\n\tTOTAL CHAR = %d\n\n\tTOTAL WORDS = %d\n\n\tTOTAL LINES = %d",ch,w,l);
}