Is there a correct way to use realloc, for when you want to add words of variable size to a string array? I am getting a segmentation fault.
Please show me what's wrong
// This function puts every word found in a text file, to a String array, **words
char **concordance(char *textfilename, int *nwords){
FILE * fp;
char *fileName = strdup(textfilename);
fp = fopen(fileName, "r");
if(fp == NULL) {
perror("fopen");
exit(1);
}
char **words = malloc(sizeof(char));
// char **words = NULL
char line[BUFSIZ];
while(fgets(line, sizeof(line), fp) != NULL){
char *word = strdup(line);
word = strtok(word, " ");
do{
words = realloc(words, (*nwords+1) * sizeof(char(*)));
words[*nwords] = word;
} while((word = strtok(NULL, " ")) != NULL);
}
return words;
}
int main(int argc, const char * argv[]) {
int *nwords = malloc(sizeof(int));
nwords = 0;
concordance("test.txt", nwords);
}
You seem to initialize nwords to 0, in a wrong way. As you have declared it as a pointer, you can not access it directly. instead, you should use the de-reference operator *.
make the following change in the main function
*nwords = 0; instead of nwords = 0;
nwords = 0 modifies the location to which nwords is pointing to, to the location with address 0, to which you have no access and can not assign.
WARNING:
It is better not to perform realloc on the same pointer, it will make the pointing location NULL if the realloc fails, leading to the loss of the previously existing data. Instead, as #David suggests, you could use a temp variable to realloc memory and then, check if it is not NULL and then assign its contents to the words pointer.
//your code
char *tmp = realloc(words, /* new size*/);
if(tmp != NULL)
words = tmp;
// your code
while using realloc you usually use it to allocate a block of data, not for a single location.
When you initiate the value of nwords, you were overwriting its pointer address, not its value.
Additionally, as the commenter says, the line char **words = malloc(sizeof(char)); is not correct. But you always re-allocate the variable words so the code still works as expected. To make it super safe you should change it to char **words = malloc(sizeof(char*));
I use the line *nwords = 0; and now it works as expected.
#define BUFSIZ 1000
#include<stdio.h>
// This function puts every word found in a text file, to a String array, **words
char **concordance(char *textfilename, int *nwords){
FILE * fp;
char *fileName = strdup(textfilename);
fp = fopen(fileName, "r");
if(fp == NULL) {
perror("fopen");
exit(1);
}
char **words = malloc(sizeof(char));
// char **words = NULL
char line[BUFSIZ];
while(fgets(line, sizeof(line), fp) != NULL){
char *word = strdup(line);
word = strtok(word, " ");
printf("word='%s'\n",word);
do{
*nwords=*nwords+1;
printf("nwords=%d\n",*nwords);
words = realloc(words, (*nwords+1) * sizeof(char(*)));
words[*nwords] = word;
} while((word = strtok(NULL, " ")) != NULL);
}
return words;
}
int main(int argc, const char * argv[]) {
int *nwords = malloc(sizeof(int));
*nwords = 0;
concordance("test.txt", nwords);
}
Related
I need to use both malloc and realloc and am confused how to do it.
Assuming that the input file looks something like this:
a *b c
a
*a b *c
and I have structs set up as so:
typedef struct Unit {
bool hasAstericks;
char letter;
} unit;
typedef struct Line {
struct unit clause[4]
} line;
Is it possible to create a struct of type unit from each letter add them to an array in struct Line based off of the line they are on? I've tried using loops but couldn't create structs on the fly with them.
This is what I have tried:
int c;
filename = argv[1];
char *filename;
FILE *fp = fopen(filename, "r");
filename = (char *)malloc(size);
do {
c = fgetc(fp);
inputFile = (char *)realloc(inputFile, size + 1);
inputFile[n] = c;
n++;
size++;
} while (c != EOF);
Your do/ while loop attempts to read the file into a array of char which you reallocate for each additional byte read from he file.
Yet you should test for end-of-file before storing the byte into the array.
Also it might be best to set a null terminator at the end of the array when you are done, so the array should have size + 1 bytes.
Here you code modified and encapsulated in a function to read the file:
char *read_file(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return NULL;
size_t size = 0;
char *p = malloc(size + 1);
if (p != NULL) {
int c;
while ((c = getc(fp)) != EOF) {
char *newp = realloc(p, size + 2);
if (newp == NULL) {
free(p);
p = NULL;
break;
}
p = newp;
p[size] = c;
size++;
}
if (p) {
p[size] = '\0'; set a null terminator: make p a C string.
}
}
fclose(fp);
return p;
}
I'm writing in C. I'm trying to read lines from a text file, parse the line, and put some info into an array of strings. When I test my code, every value in the array seems to be the last value inserted. What causes this?
int r;
char *users[51]; //given no more than 50 users
for (r = 0; r < 51; r++) {
int n = 15; //arbitrary guess at length of unknown usernames
users[r] = malloc((n + 1) * sizeof(char));
}
FILE *fp;
fp = fopen(argv[1], "r");
char *username;
int counter = 0;
char line[100];
while (fgets(line, 100, fp) != NULL) {
username = strtok(line, ":");
users[counter] = username;
printf("%s\n", username);
printf("%s\n", users[counter]);
//counter increase for later
counter += 1;
strtok is a very confusing function:
it modifies the array it receives a pointer to
it returns a pointer to an element of this array.
it keeps an internal state which makes it non-reentrant and non thread-safe.
Hence username points inside line. You store this pointer into users[counter]. At the end of the loop, all entries in users point to the same array that has been overwritten by every call to fgets().
You should duplicate the contents of the array with strdup():
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[) {
char *users[51]; //given no more than 50 users
int r;
FILE *fp;
if (argc < 2) {
fprintf(stderr, "missing filename argument\n");
return 1;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
char line[100];
int counter = 0;
while (counter < 50 && fgets(line, 100, fp) != NULL) {
char *username = strtok(line, ":");
if (username != NULL) {
users[counter] = strdup(username);
//counter increase for later
counter += 1;
}
}
users[counter] = NULL;
...
}
You put a sensible value into each entry in the array:
users[r] = malloc((n+1) * sizeof(char));
But then you overwrite it with a nonsensical value (a pointer into line):
users[counter] = username;
What you presumably wanted to do was copy the string pointed to by username into the space allocated at users[counter]. The strcpy function can do this.
I would just like to add to David's answer that you should also check the username string is null terminated before using strcpy(). I can't remember if strtok() null terminates but you shouldn't rely on it anyway.
for (int i = 0; i < 4; i++) {
for (int j = 0; j <4; i++) {
while (c != EOF)
token = strtok((fgets(token,5,fp)), delim);
}
}
Hey everyone, I'm new to C and I was given a project to take a csv file and count the average number of the values in each column. Right now I'm trying to parse the lines by the commas. I found the function [strtok], but I'm definitely implementing it incorrectly. I have the number of rows and columns in the csv file, I just need help figuring out how to parse each line by "," and place those values into a 2D array. Above is my current code that I was going to use to append the values to the array, but I keep getting a "Segmentation fault". Any help would be appreciated.
Here is the whole code for the function. I include stdio.h and stdlib.h:
void main() {
char *strcat(char *dest, const char *src);
char *strtok(char *str, const char *delim);
char *file_name = "test.txt";
FILE *fp = fopen(file_name, "r");
int array[4][4];
//int array1 [2] = {1, 3};
int counter = 0;
char *token = " ";
const char *delim = (const char *)',';
char c = fgetc(fp);
for (int i = 0; i < 4; i++) {
token = "";
for (int j = 0; j <4; i++) {
while (c != EOF)
token = strtok((fgets(token,5,fp)), delim);
}
}
fclose(fp);
}
A sample input would be something like this:
10,20,30,60
40,50,60,70
70,80,90,80
100,110,120,70
Yes you are right, you are using strtok incorrectly.
The first thing I would do is to read each line and the parse the line using
strtok, like this:
char line[1024];
const char *delim=",\n";
while(fgets(line, sizeof line, fp))
{
char *token = strtok(line, delim);
do {
printf("token: %s\n", token);
} while(token = strtok(NULL, delim));
}
strtok requires that all subsequent calls of strtok must be called with
NULL. strtok will return NULL when no more token can be found, usually the
end of the line has been reached. Note that I added the newline in the
delimiters argument. When the destination buffer is large enough fgets writes
the newline as well. Putting the newline in the delimiters list is nice trick
because strtok will get rid of the newline for you.
The code above gives you a way getting each cell of the csv, as a string. You
would have to convert the values yourself. This is the tricky bit, if the csv
contains empty spaces, quotes, etc, you need different strategies to parse the
correct value of the cell. You can use function like strtol & friend which
allow you to recover from errors, but they are not bullet proof, there will be
cases when they fail as well.
An easy example would be:
char line[1024];
const char *delim=",\n";
while(fgets(line, sizeof line, fp))
{
char *token = strtok(line, delim);
do {
int val;
if(sscanf(token, "%d", &n) != 1)
fprintf(stderr, "'%s' is not a number!\n", token);
else
printf("number found: %d\n", val);
} while(token = strtok(NULL, delim));
}
Note that this not cover all cases, for example cell that are in quotes.
The last thing to be done would be to store the values. One way of doing it is
to allocate memory for a pointer to an int array and reallocate memory for
every cell. Here again the problem lies in the csv file, sometimes they have the
wrong format, some rows will be empty or some rows will have more or less
columns than the other rows, this can be tricky. At this point it would be a good
idea to use a library for parsing csv.
The following code will assume that csv is well formatted and the number of
columns is always the same across all rows and no line is longer than 1023
characters long. When *cols is 0, I calculate the number of columns base on
the first line. If other rows have less columns, all remaining values will be 0
(because of the calloc sets new allocated memory to 0). If there are more
colmuns than in the first row, this columns will be ignored:
int **parse_csv(const char *filename, size_t *rows, size_t *cols)
{
if(filename == NULL || rows == NULL || cols == NULL)
return NULL;
FILE *fp = fopen(filename, "r");
if(fp == NULL)
return NULL;
int **csv = NULL, **tmp;
*rows = 0;
*cols = 0;
char line[1024];
char *token;
char *delim = ",\n";
while(fgets(line, sizeof line, fp))
{
tmp = realloc(csv, (*rows + 1) * sizeof *csv);
if(tmp == NULL)
return csv; // return all parsed rows so far
csv = tmp;
if(*cols == 0)
{
// calculating number of rows
char copy[1024];
strcpy(copy, line);
token = strtok(copy, delim);
do {
(*cols)++;
} while((token = strtok(NULL, delim)));
}
int *row = calloc(*cols, sizeof *row);
if(row == NULL)
{
if(*rows == 0)
{
free(csv);
return NULL;
}
return csv; // return all parsed rows so far
}
// increment rows count
(*rows)++;
size_t idx = 0;
token = strtok(line, delim);
do {
if(sscanf(token, "%d", row + idx) != 1)
row[idx] = 0; // in case the conversion fails,
// just to make sure to have a defined value
// in the cell
idx++;
} while((token = strtok(NULL, delim)) && idx < *cols);
csv[*rows - 1] = row;
}
fclose(fp);
return csv;
}
void free_csv(int **csv, size_t rows)
{
if(csv == NULL)
return;
for(size_t i = 0; i < rows; ++i)
free(csv[i]);
free(csv);
}
Now you can parse it like this:
size_t cols, rows;
int **csv = parse_csv("file.csv", &rows, &cols);
if(csv == NULL)
{
// error handling...
// do not continue
}
...
free_csv(csv, rows);
Now csv[3][4] would give you the cell at row 3, col 4 (starting from 0).
edit
Things I noticed from you code:
void main() is wrong. main should have only one of the following prototypes:
int main(void);
int main(int argc, char **argv);
int main(int argc, char *argv[]);
Another:
int main(void)
{
char *strcat(char *dest, const char *src);
char *strtok(char *str, const char *delim);
...
}
Don't put that in the main function, put it outside, also there are standard
header files for this. In this case include string.h
#include <string.h>
int main(void)
{
...
}
Another
const char *delim = (const char *)',';
This is just wrong, it's like trying to sell an apple and call it orange. ','
is a single character of type char. It has the value 44. It's the same as
doing:
const char *delim = (const char*) 44;
you are setting the address where delim should point to 44.
You have to use double quotes:
const char *delim = ",";
Note that 'x' and "x" are not the same. 'x' is 120 (see ASCII), it's
a single char. "x" is a string literal, it returns you a pointer to the start
of a sequence of characters that ends with the '\0'-terminating byte, aka a
string. Those are fundamentally different things in C.
I am trying to dynamically allocate an array of structs but whenever I run the program I keep getting: a.out(6487,0x7fff7ecb8300) malloc: * error for object 0x7fff6f670000: pointer being realloc'd was not allocated
* set a breakpoint in malloc_error_break to debug
struct node {
char course[25];
char category[20];
char prereq[50];
char notes[50];
};
int main(int argc, char* argv[])
{
FILE *fp;
char *filename = argv[1];
char *token;
char buffer[100];
char *del = ",\n";
int num = 5, i = 0, j =0, count = 0;
struct node *d = malloc(num * sizeof(struct node));
char** complete = malloc(num * sizeof(char*));
printf("%s\n", filename);
if( (fp = fopen(filename, "r")) == NULL )
{
printf("unable to open %s\n", filename);
exit(1);
}
while(fgets(buffer, sizeof(buffer), fp) != NULL)
{
if(count == num)
{
num = num + 5;
struct node *d = realloc(d, sizeof(d)*num);
printf("Reallocating\n");
}
token = strtok(buffer, del);
if(strncmp(token, "#", 1) != 0)
{
strcpy(d[count].course, token);
printf("%s\n", d[count].course);
strcpy(d[count].category, strtok(NULL, del));
printf("%s\n", d[count].category);
strcpy(d[count].prereq, strtok(NULL, del));
printf("%s\n", d[count].prereq);
strcpy(d[count].notes, strtok(NULL, del));
printf("%s\n", d[count].notes);
count++;
}
}
struct node *d = realloc(d, sizeof(d)*num);
You're declaring a new d variable which shadows the previous one, and feed its yet-uninitialized value to realloc.
You need to do this :
struct node *newD = realloc(d, num * sizeof *d);
if(!newD) {
// Allocation failure, do something about it and break out
} /* else */
d = newD;
Also note that I corrected the sizeof, which measured the size of the pointer, not the pointee's.
In:
struct node *d = realloc(d, sizeof(d)*num);
That declares a new variable d with initially undetermined value and passes it into realloc. Change that to:
struct node *tmp = realloc(d, sizeof(*d)*num);
if(!tmp)
; // handle error
d = tmp;
I tried really hard to search for a solution to this but I can't think of good enough keywords.
Currently I'm having troubles grasping the concept behind makeargv and it's usage with triple pointers (I have no idea what ***foo means, it doesn't seem to be as easy of a concept as **foo or *foo). So I made my own:
const char **makeargv(char *string, int *numargs) {
string = string + strspn(string, delims);
char *copy = malloc(strlen(string) + 1);
int i;
strcpy(copy, string);
int numtokens;
if (strtok(copy, delims) != NULL) {
for (numtokens = 1; strtok(NULL, delims) != NULL; numtokens++) {}
}
strcpy(copy, string);
const char *results[numtokens+1];
results[0] = strtok(copy, delims);
for (i = 1; i < numtokens; i++) {
results[i] = strtok(NULL, delims);
}
results[numtokens+1] = NULL;
*numargs = numtokens;
return results;
}
Here's the part at where it breaks:
void parse_file(char* filename) {
char* line = malloc(160*sizeof(char));
FILE* fp = file_open(filename);
int i = 0;
int numargs = 0;
int *pointer = &numargs;
while((line = file_getline(line, fp)) != NULL) {
if (strlen(line) == 1){
continue;
}
const char **args = makeargv(line, pointer);
printf("%s\n", args[0]);
printf("%s\n", args[1]);
/* This prints out args[0], but then args[1] causes a seg fault. Even if I replace
the args[1] with another args[0] it still causes a seg fault */
}
fclose(fp);
free(line);
}
I have a working array of strings. However when I try to print out the strings in the array, I can only print 1 of my choice and then it seg faults for any subsequent calls. lets pretend my array of strings is argv[3] = {"Yes", "no", "maybe"}, if i call argv[0], it will let me call "Yes", but any other calls (even if i call argv[0] again) do not work and cause a segfault. I can call any of the elements in the array, but once i call one the rest cease to work causing segfaults.
Help please? D: This is in C.
const char *results[numtokens+1];
This array "results" is a local variable, it is only available inside of "makeargv".
You'd better use malloc:
results = malloc(numtokens+1)
And I believe there is memory leak in your code.
You will not be able to free the memory for "char *copy"
char *copy = malloc(strlen(string) + 1);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **makeargv(char *string, int *numargs) {
static const char *delims = " \t\n";
string = string + strspn(string, delims);
char *copy = malloc(strlen(string) + 1), *p = copy;
strcpy(copy, string);
int numtokens;
for (numtokens = 0; strtok(p, delims); ++numtokens, p = NULL);
char **results = malloc(sizeof(char*)*(numtokens+1));
strcpy(copy, string);
int i;
p = copy;
for (i = 0; i < numtokens; ++i, p = NULL)
results[i] = strtok(p, delims);
results[i] = NULL;
*numargs = numtokens;
return results;
}
FILE *file_open(char *filename){
FILE *fp = fopen(filename, "r");
if(!fp){
perror("file_open");
exit(1);
}
return fp;
}
void parse_file(char* filename) {
char* line = malloc(160*sizeof(char));
FILE* fp = file_open(filename);
int i = 0, numargs = 0;
while(fgets(line, 160, fp)){
if (*line == '\n')
continue;
char **args = makeargv(line, &numargs);
for(i = 0;i<numargs;++i)
printf("%s\n", args[i]);
printf("\n");
if(args[0])
free(args[0]);
free(args);
}
fclose(fp);
free(line);
}
int main(int argc, char *argv[]){
parse_file(argv[1]);
return 0;
}