Issues reading string from file with fgetc C (without use fscanf,etc) - c

I'm trying to read just a string until a space occurs from a file using fgetc. I could not use fscanf and other i/o functions because I have to know if a line feed is read. A function call leerString(string,f) should change the string variable content, it works if I print the string into the function, but at the outside its value is NONE.
I have got some errors like segmentation faults and others with the realloc function when I try to change the return value with the string variable or pass the "contador" variable like a reference parameter. This is driving me crazy. Sorry the bad english.
char leerString(char *string, FILE *archivo){
char caracter,caracter2 = 0;
char *nombre = malloc(sizeof(char)*30);//works like a buffer
int i = 0;
int j = 0;
while (1){
caracter = fgetc(archivo);
if ((caracter != 32) && (caracter != EOF) && (caracter != '\n')){
if (i < 30){
nombre[i] = caracter;
}
else{
nombre = realloc(nombre,30);
if (nombre != NULL){
nombre[i] = caracter;
}
else{
printf("Error de asignacion de memoria\n");
exit(-2);
}
}
i++;
}
else{
break;
}
}
char *nombre2=malloc(sizeof(char)*i);//allocating the real size of the str
for (j = 0; j<i; j++){ //cleaning the buffer
caracter2 = nombre[j];
nombre2[j] = caracter2;
}
strcpy(string,nombre2);
return caracter;
}

I can see a lot of problems with your code
You are using realloc() to re-allocate a buffer of the same size; it's meant for resizing the buffer.
Your caracter variable has type char, but fgetc() returns int.
You allocate memory for the destination string, you then use realloc() unnecessarily as explained above, and then you allocate space again, and do this
for (j = 0; j<i; j++){ //cleaning the buffer
caracter2 = nombre[j];
nombre2[j] = caracter2;
}
which does nothing more than,
memcpy(nombre2, nombre, i);
You did not allocate space for the terminating nul byte, so strcpy() will fail in your code, causing possible a segmentation fault or anything else, since it's Undefined Behavior.
You should also, be sure that string has enough space to copy the characters into it, or just return a pointer to the malloc()ed string in your function, which returns char and I don't know whether that is useful or not since you didn't post enough code to tell.
A better solution would be
char *
leerString(char *string, size_t size, FILE *archivo)
{
int chr;
size_t i;
i = 0;
chr = fgetc(archivo);
while ((chr != EOF) && (isspace(chr) == 0) && (i < size - 1))
{
string[i++] = chr;
chr = fgetc(archivo);
}
if (i == 0)
return NULL;
string[i] = '\0'; /* ensure it's `nul' terminated */
return string;
}
which you can use as follows
char string[100];
if (leerString(string, sizeof(string), archivo) != NULL)
printf("%s\n", string);

Related

Free() doesn't work as expected after calling malloc()

I wrote the following function in C but have 2 problems:
I am asked to free the dynamically allocated memory to prevent using too much RAM but the line free(word) just causes bugs to my program.
if I delete it everything works fine, why is that happening? I used free after I finished using word as suggested in many different articles.
I am requested to use malloc with the minimum needed space but how could I do that?
currently my code allocates max_str_len blocks of RAM but if the word was much shorter like a letter I don't want to allocate 200 blocks for that.
Any suggestions please?
int read_words(char *words[], int size, int max_str_len) {
char *word;
char ch;
int i;
for (i = 0; i < size; ++i) {
word = (char *)malloc((max_str_len + 1) * sizeof(char));
if (word == NULL)
return -1;
for (int j = 0; j < max_str_len; ++j) {
scanf("%c", &ch);
if (ch == '\n') break;
if (ch == EOF || ch == 'R') return i;
word[j] = ch;
}
words[i] = word;
free(word);
}
return i;
}
You put a malloc'd char* into a caller-provided words[i] and then you free it. That doesn't make sense. If you free it, the caller can't do anything with it.
If you want the malloc'd strings to be minimal you could realloc them or you could read into a large buffer (allocated perhaps at the start of the function) and then copy the result into a malloc'd buffer that's sized just right.
Note that you're also failing to check scanf for errors and that you're leaking memory if you get a memory failure in the middle of the function—by returning -1, you effectively lose info on how many elements of words have been filled with owning pointers. You might want to return that info (return i;) or to free all pointers allocated by the function before the malloc failure.
There are multiple problems in your code:
you free the memory allocated for each word, yet you return pointers to the freed blocks in the array supplied by the caller, causing undefined behavior when the caller will dereference these pointers.
your test for end of file is incorrect: scanf() will return EOF then, bu the character will not be set to EOF, which might not be appropriate for a char anyway. You should use getchar() and make ch an int.
you should set a null terminator at the end of the string read.
you could use realloc to shrink the block of memory once you know the string length.
Here is a modified version:
int read_words(char *words[], int size, int max_str_len) {
char *word, *p;
int i, j, ch;
for (i = 0; i < size;) {
word = (char *)malloc(max_str_len + 1);
if (word == NULL) {
/* free the words allocated so far and return a failure code */
while (i-- > 0)
free(words[i];
return -1;
}
for (j = 0; j < max_str_len; ++j) {
ch = getchar();
if (ch == '\n' || ch == EOF || ch == 'R') break;
word[j] = ch;
}
if (j == 0 && (ch == EOF || ch == 'R'))
break;
word[j] = '\0';
p = (char *)realloc(word, j + 1);
if (p != NULL)
word = p;
words[i++] = word;
if (ch == EOF || ch == 'R')
break;
}
return i;
}

Reading in a line from file or stdin dynamically

I am posed with a situation where my function does exactly what I want except handle higher amounts of input.
I initially thought to process each character one by one but was running into problems doing this. So fscanf not only does what I want it to do but it is essential in reading in only one line. I noticed, I cannot reallocate space for bigger array this way though. I have tried using format specifiers i.e. %*s to include a specific amount of buffer space before hand but this still does not work.
I have noticed also, I would have no way of knowing the size of the string I am reading in.
Here is my attempt and thoughts:
#define LINE_MAX 1000
char* getline(FILE* inputStream)
{
int capacity = LINE_MAX;
char* line = malloc(capacity * sizeof(char));
int ch;
/* if (sizeof(capacity) == sizeof(line)) { // Not a valid comparison? Too late?
capacity *= 2;
line = realloc(line, capacity * sizeof(line));
} */
if (fscanf(stream, "%[^\n]s", line) == 1) {
ch = fgetc(inputStream);
if (ch != '\n' && ch != EOF) {
fscanf(inputStream, "%*[^\n]");
fscanf(inputStream, "%*c");
}
free(line);
return line;
}
free(line);
return NULL;
}
I am new to memory allocation in general but I feel as though I had a good idea of what to do here. Turns out I was wrong.
Here is an example to read a line and store it in a Character array.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
signed char *str;
int c;
int i;
int size = 10;
str = malloc(size*sizeof(char));
for(i=0;(c=getchar()) !='\n' && c != EOF;++i){
if( i == size){
size = 2*size;
str = realloc(str, size*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = c;
}
if(i == size){
str = realloc(str, (size+1)*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = '\0';
printf("My String : %s", str);
return 0;
}
The array is resized to twice it's original size if current array can't hold the characters read from input.

Invalid Next Size (fast) when I try to free a string

when I cannot figure out the issue with my code. The goal of the program is to read words from a text file and add them to a linked list. When I run this:
static char *make_string(char buffer[], int length) {
char *str = (char *)(malloc(length+1));
memcpy(str, buffer, length);
str[length + 1] = '\0';
return str;
}
char *words_next_word() {
char buf[MAXBUF] = {0};
int character = getchar();
int index = 0;
static int count = 0;
printf("it is the %d word \n", count);
count++;
while(isalnum(character) == 0){
character = getchar();
}
while(character != EOF && isalnum(character) != 0){
buf[index] = character;
index++;
character = getchar();
}
return make_string(buf, index);
}
After getting the word from the text file, I add it to a linked list. After adding the word, I free the string. The first 138 words are read and freed without issue. For some reason, the program crashes when trying to read the 138'th word, but I don't know why.
The first 138 words are read and freed without issue.
Not really. The problem exists even in the first make_string() call.
Code is attempting to duplicate the string yet has a off-by-one error.
static char *make_string(char buffer[], int length) {
char *str = (char *)(malloc(length+1)); // size good
memcpy(str, buffer, length); // copy OK
str[length + 1] = '\0'; // null character assigned in wrong place, 1 too far out
return str;
}
Assigning data in the wrong place (assigning out of bounds #EOF) and not defining the value of str[length] with a null character (and later using it) results in undefined behavior.
Here is a strdup() function, yet I suggest trying to code repair your make_string() before reviewing.
The below code does not limit writing to buf[]
while(character != EOF && isalnum(character) != 0){
if (index + 1 >= MAXBUF) {
puts("Too long");
return NULL; // or some other error handling
}
buf[index] = character;

pass string in array

I am trying to pass strings (lines of text file) into arrays (array for f1 and array2 for f2). When I just print the buffer buffer2, the lines come up just fine. When I try to pass them using strcpy the program crashes with no apparent reason. I have tried the following:
Using a two dimensional array with no avail
Working with methods and tried to return char and THEN pass it to the array, so I can avoid this sloppy code, but this will do for now.
I am using windows 7 x64, with DEV-C++.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *arrayF1[20] ;
char *arrayF2[20] ;
int i = 0;
int size = 1024, pos;
int c;
int lineCount = 0;
char *buffer = (char *)malloc(size);
char *buffer2 = (char *)malloc(size);
char *array[100];
char *array2[100];
if (argc!=3)
{
printf("\nCommand Usage %s filename.txt filename.txt\n", argv[0]);
}
else
{
FILE *f1 = fopen(argv[1], "r");
FILE *f2 = fopen(argv[2], "r");
if(f1)
{
do { // read all lines in file
pos = 0;
do{ // read one line
c = fgetc(f1);
if(c != EOF) buffer[pos++] = (char)c;
if(pos >= size - 1) { // increase buffer length - leave room for 0
size *=2;
buffer = (char*)realloc(buffer, size);
}
}while(c != EOF && c != '\n');
lineCount++;
buffer[pos] = 0;
// line is now in buffer
strcpy(array[i], buffer);
printf("%s", array[i]);
//printf("%s", buffer);
i++;
} while(c != EOF);
printf("\n");
fclose(f1);
}
printf("%d\n",lineCount);
free(buffer);
lineCount=0;
i=0;
if (f2)
{
do { // read all lines in file
pos = 0;
do{ // read one line
c = fgetc(f2);
if(c != EOF) buffer2[pos++] = (char)c;
if(pos >= size - 1) { // increase buffer length - leave room for 0
size *=2;
buffer2 = (char*)realloc(buffer, size);
}
}while(c != EOF && c != '\n');
lineCount++;
buffer2[pos] = 0;
// line is now in buffer
strcpy(array2[i], buffer);
//printf("%s", buffer2);
printf("%s", array2[i]);
i++;
} while(c != EOF);
printf("\n");
fclose(f2);
}
printf("%d\n",lineCount);
free(buffer2);
}//end first else
return 0;
}
You haven't allocated any memory for the arrays in array. You'll need to do that before you can copy the strings there.
array[i] = malloc(pos + 1);
if (array[i] == NULL) {
// handle error
}
strcpy(array[i], buffer);
printf("%s", array[i]);
To strcpy() to a char*, you need to have already allocated memory for it. You can do this by making static char arrays:
char array[100][50]; //Strings can hold up to 50 chars
or you can use pointers and dynamically allocate them instead.
char *array[100];
for(int i = 0; i < 100; i++)
array[i] = malloc(sizeof(char) * 50); //Up to 50 chars
...
for(int i = 0; i < 100; i++)
free(array[i]); //Delete when you're finished
After allocating it with one of those methods, it's safe to write to it with strcpy().
Looks to me like you allocated the arrays on the stack but failed to ensure that they'd be big enough, since each has size exactly 100. Since you don't know how big they'll be, you can either allocate them dynamically (using #JohnKugelman's solution) or wait to declare them until after you know what their sizes need to be (i.e., how long the strings are that they need to hold).
the program crashes with no apparent reason
There is always a reason :)
This line:
char *array[100];
Creates an array of 100 pointers to characters.
Then this line:
strcpy(array[i], buffer);
Tries to copy your buffer to the ith pointer. The problem is that you never allocated any memory to those pointers, so strcpy() crashes. Just this:
array[i] = malloc(strlen(buffer)+1);
strcpy(array[i], buffer);
will resolve that error.

How to dynamically allocate memory space for a string and get that string from user?

I want to read input from user using C program. I don't want to use array like,
char names[50];
because if the user gives string of length 10, then the remaining spaces are wasted.
If I use character pointer like,
char *names;
then I need to allocate memory for that in such a way of,
names = (char *)malloc(20 * sizeof(char));
In this case also, there is a possibility of memory wastage.
So, what I need is to dynamically allocate memory for a string which is of exactly same as the length of the string.
Lets assume,
If the user input is "stackoverflow", then the memory allocated should be of 14 (i.e. Length of the string = 13 and 1 additional space for '\0').
How could I achieve this?
Read one character at a time (using getc(stdin)) and grow the string (realloc) as you go.
Here's a function I wrote some time ago. Note it's intended only for text input.
char *getln()
{
char *line = NULL, *tmp = NULL;
size_t size = 0, index = 0;
int ch = EOF;
while (ch) {
ch = getc(stdin);
/* Check if we need to stop. */
if (ch == EOF || ch == '\n')
ch = 0;
/* Check if we need to expand. */
if (size <= index) {
size += CHUNK;
tmp = realloc(line, size);
if (!tmp) {
free(line);
line = NULL;
break;
}
line = tmp;
}
/* Actually store the thing. */
line[index++] = ch;
}
return line;
}
You could have an array that starts out with 10 elements. Read input character by character. If it goes over, realloc another 5 more. Not the best, but then you can free the other space later.
You can also use a regular expression, for instance the following piece of code:
char *names
scanf("%m[^\n]", &names)
will get the whole line from stdin, allocating dynamically the amount of space that it takes. After that, of course, you have to free names.
If you ought to spare memory, read char by char and realloc each time. Performance will die, but you'll spare this 10 bytes.
Another good tradeoff is to read in a function (using a local variable) then copying. So the big buffer will be function scoped.
Below is the code for creating dynamic string :
void main()
{
char *str, c;
int i = 0, j = 1;
str = (char*)malloc(sizeof(char));
printf("Enter String : ");
while (c != '\n') {
// read the input from keyboard standard input
c = getc(stdin);
// re-allocate (resize) memory for character read to be stored
str = (char*)realloc(str, j * sizeof(char));
// store read character by making pointer point to c
str[i] = c;
i++;
j++;
}
str[i] = '\0'; // at the end append null character to mark end of string
printf("\nThe entered string is : %s", str);
free(str); // important step the pointer declared must be made free
}
First, define a new function to read the input (according to the structure of your input) and store the string, which means the memory in stack used. Set the length of string to be enough for your input.
Second, use strlen to measure the exact used length of string stored before, and malloc to allocate memory in heap, whose length is defined by strlen. The code is shown below.
int strLength = strlen(strInStack);
if (strLength == 0) {
printf("\"strInStack\" is empty.\n");
}
else {
char *strInHeap = (char *)malloc((strLength+1) * sizeof(char));
strcpy(strInHeap, strInStack);
}
return strInHeap;
Finally, copy the value of strInStack to strInHeap using strcpy, and return the pointer to strInHeap. The strInStack will be freed automatically because it only exits in this sub-function.
This is a function snippet I wrote to scan the user input for a string and then store that string on an array of the same size as the user input. Note that I initialize j to the value of 2 to be able to store the '\0' character.
char* dynamicstring() {
char *str = NULL;
int i = 0, j = 2, c;
str = (char*)malloc(sizeof(char));
//error checking
if (str == NULL) {
printf("Error allocating memory\n");
exit(EXIT_FAILURE);
}
while((c = getc(stdin)) && c != '\n')
{
str[i] = c;
str = realloc(str,j*sizeof(char));
//error checking
if (str == NULL) {
printf("Error allocating memory\n");
free(str);
exit(EXIT_FAILURE);
}
i++;
j++;
}
str[i] = '\0';
return str;
}
In main(), you can declare another char* variable to store the return value of dynamicstring() and then free that char* variable when you're done using it.
Here's a snippet which I wrote which performs the same functionality.
This code is similar to the one written by Kunal Wadhwa.
char *dynamicCharString()
{
char *str, c;
int i = 0;
str = (char*)malloc(1*sizeof(char));
while(c = getc(stdin),c!='\n')
{
str[i] = c;
i++;
realloc(str,i*sizeof(char));
}
str[i] = '\0';
return str;
}
char* load_string()
{
char* string = (char*) malloc(sizeof(char));
*string = '\0';
int key;
int sizer = 2;
char sup[2] = {'\0'};
while( (key = getc(stdin)) != '\n')
{
string = realloc(string,sizer * sizeof(char));
sup[0] = (char) key;
strcat(string,sup);
sizer++
}
return string;
}
int main()
{
char* str;
str = load_string();
return 0;
}
realloc is a pretty expensive action...
here's my way of receiving a string, the realloc ratio is not 1:1 :
char* getAString()
{
//define two indexes, one for logical size, other for physical
int logSize = 0, phySize = 1;
char *res, c;
res = (char *)malloc(sizeof(char));
//get a char from user, first time outside the loop
c = getchar();
//define the condition to stop receiving data
while(c != '\n')
{
if(logSize == phySize)
{
phySize *= 2;
res = (char *)realloc(res, sizeof(char) * phySize);
}
res[logSize++] = c;
c = getchar();
}
//here we diminish string to actual logical size, plus one for \0
res = (char *)realloc(res, sizeof(char *) * (logSize + 1));
res[logSize] = '\0';
return res;
}

Resources