Reputation:
I've been given a .txt file with a specific structure: each line has a String with 5 characters but with a random number of lines, and we should read the file and store it as we want. I've tried doing it with a linked list and it worked just fine but as the size of the file grew up, the time it took to execute was too long. Since then i've been trying to store the Strings into an array of strings, so everything would be stored contiguously in memory. When executing, i get a segmentation fault error and i have no idea why. The code goes as follows:
int nLines (char *path)
{
int answer = 0;
FILE* fp;
fp = fopen(path,"r");
char line[6];
while (fgets(line, sizeof(line),fp))
{
answer++;
}
return answer;
}
int main (int argc, char *argv[])
{
FILE* fp;
fp = fopen(argv[1], "r");
int numberLines = nLines(argv[1]);
char **storage = malloc(numberLines * 6 * sizeof(char));
if(storage != NULL)
{
int i = 0;
char line [6];
while (fgets(line, sizeof(line),fp))
{
strcpy(storage[i], line);
i++;
}
}
free(storage);
}
The first function is supposed to return the number of lines there is in the file. With this information, i'm trying to allocate memory equal to the number of strings * the size of each string since i know before hand this value. I'm imagining the problem comes from the line:
char **storage = malloc (numberLines * 6 *sizeof(char));
I haven't touched C in a long time and i'm kinda rusty with the whole pointers and memory stuff. Can someone help please. Thank you!
Upvotes: 0
Views: 75
Reputation: 1922
If one wants to truly have an on-line algorithm, one isn't going to have the number of lines available. The idiomatic way to have a contiguous dynamic container is to reallocate geometrically increasing capacity, like vector or ArrayList. C
doesn't have that type built-in, but it's worth the extra code if one uses it a lot. For example, this reads from stdin
until EOF
and uses a Fibonacci sequence as it's capacities.
#include <stddef.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
/** One line of maximum 5 `char` plus 1 `NUL`. */
struct Line { char str[6]; };
/** A dynamic line array. */
struct LineArray {
struct Line *data; /* data -> (c0 < c1 || c0 == c1 == max_size) */
size_t capacity, next_capacity; /* !data -> !size, data -> size<=capacity */
size_t size;
};
/** Ensures `min_capacity` of `a`. Return success, otherwise, `errno` will be
set: `realloc` or `ERANGE` -- tried allocating more then can fit in `size_t`
or `realloc` doesn't follow [IEEE Std 1003.1-2001
](https://pubs.opengroup.org/onlinepubs/009695399/functions/realloc.html). */
static int reserve(struct LineArray *const a, const size_t min_capacity) {
size_t c0, c1;
struct Line *data;
const size_t max_size = (size_t)-1 / sizeof(struct Line *);
assert(a);
if(!a->data) {
if(!min_capacity) return 1;
c0 = 8, c1 = 13;
} else {
if(min_capacity <= a->capacity) return 1;
c0 = a->capacity, c1 = a->next_capacity;
}
if(min_capacity > max_size) return errno = ERANGE, 0;
assert(c0 < c1); /* Fibonacci: c0 ^= c1, c1 ^= c0, c0 ^= c1, c1 += c0. */
while(c0 < min_capacity) {
size_t temp = c0 + c1; c0 = c1; c1 = temp;
if(c1 > max_size || c1 < c0) c1 = max_size;
}
if(!(data = realloc(a->data, c0 * sizeof *a->data)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data;
a->capacity = c0;
a->next_capacity = c1;
return 1;
}
/** Adds one to the size of `a` and returns it (`push_back`.) Exceptional
return null and `errno` is `realloc` or `ERANGE`. */
static struct Line *new_line(struct LineArray *const a) {
assert(a);
if(a->size >= (size_t)-1) { errno = ERANGE; return 0; } /* Unlikely. */
if(!reserve(a, a->size + 1)) return 0; /* (Less) unlikely. */
return a->data + a->size++;
}
/** Destructor. */
static void linearray_(struct LineArray *const a) {
assert(a);
free(a->data);
a->data = 0, a->capacity = a->next_capacity = a->size = 0;
}
#include <string.h>
#include <stdio.h>
int main(void)
{
struct LineArray storage = { 0, 0, 0, 0 };
struct Line *s, *s_end;
size_t l = 0, line_len;
char line[7] = "";
int success = EXIT_FAILURE;
/* `line` must be capable of storing the "*[,5]\n\0". */
assert(sizeof line == sizeof ((struct Line *)0)->str + 1);
while (fgets(line, sizeof line, stdin))
{
l++;
line_len = strlen(line);
assert(line_len && line_len < sizeof line);
/* Too long. */
if(line[line_len - 1] != '\n') { errno = ERANGE; goto catch; }
/* Cut off the trailing new-line. */
line[line_len-- - 1] = '\0';
/* Store `line`. */
if(!(s = new_line(&storage))) goto catch;
strcpy(s->str, line);
}
if(ferror(stdin)) goto catch;
/* Print all. */
for(s = storage.data, s_end = s + storage.size; s < s_end; s++)
printf("stored: %s\n", s->str);
success = EXIT_SUCCESS;
goto finally;
catch:
perror("Error");
fprintf(stderr, "On line %lu: \"%s\".\n", (unsigned long)l, line);
finally:
linearray_(&storage);
return success;
}
Upvotes: 0
Reputation: 67713
your allocation is wrong
int main (int argc, char *argv[])
{
FILE* fp;
fp = fopen(argv[1], "r");
size_t numberLines = 0;
char **storage = NULL;
char line [8];
while (fgets(line, sizeof(line),fp))
{
storage = realloc(storage, (numberLines + 1) * sizeof(*storage));
storage[numberLines] = malloc(8);
strcpy(storage[numlines++], line);
}
/* ... */
}
you need to allocate space for the pointers, then space for the strings. It is demo only and you should implement the correct error handling (memory and file).
Upvotes: 1