I wrote this simple readline function, it can return each line length but it doesn't return a pointer to the allocated buffer. Another issue is the last line ignored(it doesn't return it):
FILE *passFile = NULL;
char *current = NULL;
size_t len = 0;
passFile = fopen("pass.txt", "r");
while(readline(passFile, ¤t, &len) != -1) {
printf("%s\n", current); // SEGMENTAION FAULT
printf("%d\n", len);
free(current);
current = NULL;
}
ssize_t
readline(FILE *file, char **bufPtr, size_t *len)
{
char c, *buf = NULL;
size_t n = 0;
buf = (char*)malloc(sizeof(char));
while((c = fgetc(file)) != '\n' && (c != EOF)) {
buf[n] = c;
++n;
buf = realloc(buf, n + 1);
}
buf[n] = '\0';
*bufPtr = buf;
*len = n;
if(c == EOF) // reach end of file
return -1;
return 0;
}
Your readline() function is not returning a pointer to allocated memory. In your call, current is never set, so the pointer is invalid and you get the error.
In C, functions are "call by value". Inside readline(), bufPtr is a copy of whatever was passed to readline(). Assigning to bufPtr merely overwrites the local copy and does not return a value that the calling code can see.
In pseudocode:
TYPE a;
define function foo(TYPE x)
{
x = new_value;
}
foo(a); // does not change a
This only changes the local copy of x and does not return a value. You change it to use a pointer... the function still gets a copy, but now it's a copy of a pointer, and it can use that pointer value to find the original variable. In pseudocode:
TYPE a;
define function foo(TYPE *px)
{
*px = new_value;
}
foo(&a); // does change a
Now, to change your function:
ssize_t
readline(FILE *file, char **pbufPtr, size_t *len)
{
// ...deleted...
buf[n] = '\0';
*pbufPtr = buf;
// ...deleted...
}
And you call it like so:
while(readline(passFile, ¤t, &len) != -1)
P.S. It is not a good idea to call realloc() the way you do here. It's potentially a very slow function, and for an input string of 65 characters you will call it 65 times. It would be better to use an internal buffer for the initial file input, then use malloc() to allocate a string that is just the right size and copy the string into the buffer. If the string is too long to fit in the internal buffer at once, use malloc() to get a big-enough place to copy out the part of the string you have in the internal buffer, then continue using the internal buffer to copy more of the string, and then call realloc() as needed. Basically I'm suggesting you have an internal buffer of size N, and copy the string in chunks of N characters at a time, thus minimizing the number of calls to realloc() while still allowing arbitrary-length input strings.
EDIT: Your last-line problem is that you return -1 when you hit end of file, even though there is a line to return.
Change your code so that you return -1 only if c == EOF and n == 0, so a final line that ends with EOF will be correctly returned.
You should also make readline() use the feof() function to check if file is at end-of-file, and if so, return -1 without calling malloc().
Basically, when you return -1, you don't want to call malloc(), and when you did call malloc() and copy data into it, you don't want to return -1! -1 should mean "you got nothing because we hit end of file". If you got something before we hit end of file, that's not -1, that is 0. Then the next call to readline() after that will return -1.
In your readline function you pass current by value. So if you change bufPtr inside your function, it doesn't change value of current outside. If you want to change value of current pass it by reference: ¤t and change readline() parameter to char **bufPTR.
You could pass current the way you did if you wanted to change something it points to, but you want to change where it points in first place.
replace your readlinefunction with this
char* readline(FILE *file, size_t *len)
{
char c, *buf = NULL;
size_t n = 0;
buf = (char*)malloc(sizeof(char));
while((c = fgetc(file)) != '\n' && (c != EOF)) {
buf[n] = c;
++n;
buf = realloc(buf, n + 1);
}
buf[n] = '\0';
bufPtr = buf;
*len = n;
if(c == EOF) // reach end of file
return NULL;
return buf;
}
and then in main replace this line while(readline(passFile, current, &len) != -1) with this while((current = readline(passFile, &len) != NULL)
Now it works:
ssize_t
readline(FILE *file, char **bufPtr, size_t *len)
{
if(feof(file)) // reach end of file
return -1;
char c, *buf = NULL;
size_t n = 0, portion = CHUNK;
buf = (char*)malloc(sizeof(char) * CHUNK);
while((c = fgetc(file)) != '\n' && (c != EOF)) {
buf[n] = c;
++n;
if(n == portion) {
buf = realloc(buf, CHUNK + n);
portion += n;
}
}
buf[n] = '\0';
*bufPtr = buf;
*len = n;
return 0;
}
Related
i'm writing a function (get_next_line) that returns a line read from a file descriptor, so i have the line stored in a char array that i return, now i need to process this line
so i read values into a buffer, whenever there are values read, the buffer should be processed, the newly added values should be joined with the result char array, if however, we encounter a newline, an extra step is required, i append the characters to the resulting array, until i reach the newline, which will be replaced by a null char, then the remaining values in the buffer should be stored in a static array that will append those values to the resulting string next time gnl is called.
the output of my function is correct my i need to deal with the leaks , i solved all of the leaks except the one related to the static char. btw im only allowed to use read() malloc() and free , all the other functions should be made by me (that explains the ft_ before each function in my code) here is my code :
valgrind results for the leaks
#include "get_next_line.h"
char * get_next_line(int fd) {
// // the reminder will contain the values after '\n' because the function should let
// you read the text file one line at a time. for example if the the buffer size is 5 and
// the first line is abc\nz we need to store the 'z' for the next line.
static char *reminder = "";
char buf[BUFFER_SIZE + 1];
int nbytes;
char * line = malloc(BUFFER_SIZE + 1);
if (!line || reminder == NULL) {
return NULL;
}
if (fd < 0 || fd > 999) {
free(line);
return NULL;
}
while ((nbytes = read(fd, buf, BUFFER_SIZE)) > 0) {
buf[nbytes] = '\0';
// ft_strjoin allocates and returns a new
// string, which is the result of the concatenation of ’s1’ and ’s2’.
reminder = ft_strjoin(reminder, buf);
//i modified strchr to returns a pointer to the character after '\n'
if (ft_strchr(reminder, '\n')) {
// each line from the txt file start from the index 0 to '\n'
line = ft_substr(reminder, 0, '\n');
reminder = ft_strchr(reminder, '\n');
return line;
}
}
if (ft_strcmp(reminder, "") == 0 || nbytes < 0 || reminder == NULL) {
free(line);
return NULL;
}
if ((!(ft_strchr(reminder, '\n')))) {
ptr = reminder;
reminder = NULL;
return ptr;
}
if (ft_strchr(reminder, '\n')) {
ptr = ft_substr(reminder, 0, '\n');
reminder = ft_strchr(reminder, '\n');
return ptr;
}
return NULL;
}
// here is my strjoin
char *ft_strjoin(char *s1, char *s2)
{
size_t size_s1;
size_t size_s2;
char *strjoin;
size_s1 = ft_strlen(s1);
size_s2 = ft_strlen(s2);
if (!(strjoin = malloc(size_s1 + size_s2 + 1)))
return (NULL);
ft_strcpy(strjoin, s1);
ft_strcat(strjoin, s2);
if(!(ft_strcmp(s1, "") == 0))
{
free(s1);
}
return (strjoin);
}
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;
}
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.
In C i can use the char *fgets(char *s, int size, FILE *stream) function to read user input from stdin. But the size of the user input is limited to size.
How can i read user input of variable size?
In C you are responsible for your buffers, and responsible for their size. So you can not have some dynamic buffer ready for you.
So the only solution is to use a loop (either of fgets or fgetc - depends on your processing and on your stop condition)
If you go beyond C to C++, you will find that you can accept std::string objects of variable sizes (there you need to deal with word and/or line termination instead - and loop again)
This function reads from standard input until end-of-file is encountered, and returns the number of characters read. It should be fairly easy to modify it to read exactly one line, or alike.
ssize_t read_from_stdin(char **s)
{
char buf[1024];
char *p;
char *tmp;
ssize_t total;
size_t len;
size_t allocsize;
if (s == NULL) {
return -1;
}
total = 0;
allocsize = 1024;
p = malloc(allocsize);
if (p == NULL) {
*s = NULL;
return -1;
}
while(fgets(buf, sizeof(buf), stdin) != NULL) {
len = strlen(buf);
if (total + len >= allocsize) {
allocsize <<= 1;
tmp = realloc(p, allocsize);
if (tmp == NULL) {
free(p);
*s = NULL;
return -1;
}
p = tmp;
}
memcpy(p + total, buf, len);
total += len;
}
p[total] = 0;
*s = p;
return total;
}