I'm trying to open a file with fopen, but I don't want a static location so I am getting the string in from the user when he/she runs the program.
However if a user does not enter one a default file is specified.
Can I just put the malloc var in to the fopen path parameter?
char *file_path_mem = malloc(sizeof(char));
if (file_path_mem != NULL) //Null if out of memory
{
printf("Enter path to file, if in current directory then specify name\n");
printf("File(default: marks.txt): ");
while ((c = (char)getchar()) != '\n')
{
file_path_mem[i++] = c;
file_path_mem = realloc(file_path_mem, i+1 * sizeof(char));
}
file_path_mem[i] = '\0';
if (i == 0 && c == '\n')
{
file_path_mem = realloc(file_path_mem, 10 * sizeof(char);
file_path_mem = "marks.txt";
}
}
else
{
printf("Error: Your system is out of memory, please correct this");
return 0;
}
if (i==0)
{
FILE *marks_file = fopen("marks.txt", "r");
}
else
{
FILE *marks_file = fopen(file_path_mem, "r");
}
free(file_path_mem);
As you might have guess I am a c novice so if I have done something horrible wrong, then sorry.
This is not doing what you think it is:
file_path_mem = realloc(file_path_mem, 10 * sizeof(char);
file_path_mem = "marks.txt";
What you want to do is change that second line to copy the default name into the allocated buffer:
strcpy(file_path_mem, "marks.txt");
You CAN pas the char* returned from malloc straight into fopen. Just make sure it contains valid data. Though make sure you strcpy the new string in as R Samuel points out.
Btw, your use of realloc will give pretty awful performance. Realloc works by seeing if there is enogh space to grow the memory (This is not guranteed, realloc must just return a block that is the newSize containing the old data). If there isn't enough space, it allocs a new block of the new size and copies the old data ot the new block. Obviously this is suboptimal.
You are better off allocating a block of, say, 16 chars and then , if you need more than 16, realloc'ing a further 16 chars. There will be a bit of memory wastage but you are not, potentially, copying anything like as much memory around.
Edit: Further to that. For a string that contains 7 character. Using your realloc every byte scheme you will generate the following processes.
alloc 1 byte
alloc 2 bytes
copy 1 byte
free 1 byte
alloc 3 bytes
copy 2 bytes
free 2 bytes
alloc 4 bytes
copy 3 bytes
free 3 bytes
alloc 5 bytes
copy 4 bytes
free 4 bytes
alloc 6 bytes
copy 5 bytes
free 5 bytes
alloc 7 bytes
copy 6 bytes
free 6 bytes
alloc 8 bytes
copy 7 bytes
This is 8 allocations, 7 frees and a copy of 28 bytes. Not to mention the 8 bytes you assign to the array (7 characters + 1 null terminator).
Allocations are SLOW. Frees are SLOW. Copying is much slower than not copying.
For this example using my allocate 16 bytes at a time system you alloc once, assign 8 bytes. No copies and the only free is when you've finished with it. You DO waste 8 bytes though. But ... well ... 8 bytes is nothing in the grand scheme of things...
realloc is relatively more expensive than the rest of your loop, so you might as well just start with a 128 byte buffer, and realloc another 128 bytes if you fill that up.
I suggest some of the following changes:
define your default location at the top of the file
char* defaultLocation = 'myfile.txt';
char* locationToUse;
use constants instead of hard coding numbers in your code (like the 10 you have in there)
int DEFAULT_INPUT_BUFFER_SIZE = 128;
char* userInputBuffer = malloc(sizeof(char) * DEFAULT_INPUT_BUFFER_SIZE) );
int bufferFillIndex = 0;
only reallocate infrequently, and not at all if possible (size 128 buffer)
while ((c = (char)getchar()) != '\n')
{
file_path_mem[bufferFillIndex++] = c;
if (bufferFillIndex % DEFAULT_INPUT_BUFFER_SIZE == 0)
{
realloc(file_path_mem, (bufferFillIndex + DEFAULT_INPUT_BUFFER_SIZE) * sizeof(char);
}
}
Should not be an issue here but for realocation you should state the new size like that
file_path_mem = realloc(file_path_mem, (i+1) * sizeof(char));
You could simplify this greatly with getline(), assuming it is available on your system:
printf("Enter path to file, if in current directory then specify name\n");
printf("File(default: marks.txt): ");
int bufSize = 100;
char* fileNameBuf = (char*) malloc(bufSize + 1);
int fileNameLength = getline(&fileNameBuf, &bufSize, stdin);
if(fileNameLength < 0)
{
fprintf(stderr, "Error: Not enough memory.\n")
exit(1);
}
/* strip line end and trailing whitespace. */
while(fileNameLength && fileNameBuf[fileNameLength - 1] <= ' ')
--fileNameLength;
fileNameBuf[fileNameLength] = 0;
if(!fileNameLength)
{
free(fileNameBuf); /* Nothing entered; use default. */
fileNameBuf = "marks.txt";
}
FILE *marks_file = fopen(fileNameBuf, "r");
if(fileNameLength)
free(fileNameBuf);
I don't know if your C supports in-block declarations, so I'll let you fix that if needed.
EDIT: Here's a getline tutorial.
As a learning experience I would second the suggestions of others to expand your buffer by more than one byte. It won't make any difference if it's not in a loop but calling realloc() on each byte would be unacceptable in other contexts.
One trick that works fairly well is to double the size of the buffer every time it fills up.
On the other hand, if you really just want to open a file and don't care about writing the world's greatest name loop, then standard procedure is to simplify the code even at the price of a limit on file name length. There is no rule that says you have to accept silly pathnames.
FILE *read_and_open(void)
{
char *s, space[1000];
printf("Enter path to file, if in current directory then specify name\n");
printf("File(default: marks.txt): ");
fgets(space, sizeof space, stdin);
if((s = strchr(space, '\n')) != NULL)
*s = 0;
if (strlen(space) == 0)
return fopen("marks.txt", "r");
return fopen(space, "r");
}
Related
I am trying to read a line from stdin in C and at the same time dynamically allocate memory for the string using the code snippet from below. The problem is that when I run this code, calling something like strlen(msg) results in an Conditional jump or move depends on uninitialised value(s) in valgrinds output.
I don't understand how to get past this problem because I can't properly initialize it if I am dynamically allocating it. I've spent a really long time on this now and can't seem to figure it out... any help would be much appreciated.
char* msg = NULL;
int c;
// set initial size for dynamic allocation
msg = malloc(sizeof(char)*10+1);
int idx = 0;
char* temp = NULL;
size_t size = 10;
while (1){
c = getchar();
if(c == EOF || c == '\n'){
break;
}else if(!isalpha(c)){
free(msg);
exit(100);
}
// dynamically reallocate memory if input too large
if(size <= idx){
size += size;
temp = realloc(msg, size);
msg = temp;
}
msg[idx++] = (char)c;
}
printf("%ld", strlen(msg));
You never terminate the string you create in msg.
After the loop (but before you use msg as a null-terminated string with e.g. strlen(msg)) add the terminator:
msg[idx] = '\0';
There's another problem though, which might crop up unexpectedly: When you reallocate the memory you don't add space for the terminator.
Use-case for this problem: You enter a string with exactly 20 characters.
You will then have reallocated once, making the size exactly 20 bytes (characters). The last character will be put at index 19, then idx is increased to 20 (which is out of bounds). Next iteration of the loop you detect the newline, and break out, and will then set the terminator on the 21:st character in your 20-character array.
Add 1 to the size when calling realloc to solve this:
temp = realloc(msg, size + 1);
I have a file which contains information about films like this:
Film code
Name
Year of release
Movie length(in minutes)
The film producer
I have to read this info from a file and store that info into pointers. My code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct filmiab
{
int koodpk;
char *nimed;
int aasta;
int kestus;
char *rezi;
} filmiab;
int main()
{
filmiab *db;
FILE *f1;
f1 = fopen("filmid.txt", "r");
db->nimed = (char*)malloc(sizeof(db->nimed) * sizeof(char));
db->rezi = (char*)malloc(sizeof(db->rezi) * sizeof(char));
while(1)
{
fscanf(f1, "%d ", &db->koodpk);
fgets(db->nimed, 100, f1);
db->nimed = (char*)realloc(db->nimed, sizeof(char) * sizeof(db->nimed)); //gets more memory to store the strings
fscanf(f1, "%d %d ", &db->aasta, &db->kestus);
fgets(db->rezi, 100, f1);
db->rezi = (char*)realloc(db->rezi, sizeof(char) * sizeof(db->rezi));
printf("Filmi kood: %d\nFilmi nimi: %sAasta: %d\nKestus minutites: %d\nFilmi rezis66r: %s\n",
db->koodpk, db->nimed, db->aasta, db->kestus, db->rezi);
printf("\n");
}
return 0;
}
It just goes into an infinte loop and only prints the last 5 lines. I know that when using fgets it replaces all the strings with the last 5 lines.
But what can I do so it would store all the info and that so I could print them out (or just use them) in another function. And why does it go into an infinite loop ?
EDIT:
I have to use only the pointers that are created in the struct.
EDIT2:
Now both these lines
fgets(db->nimed, 100, f1);
fgets(db->rezi, 100, f1);
store the required info and the blank spaces. What to do so it only stores the names of the films and the producers.
It just goes into an infinte loop
That's because it is an infinite loop. You have a while(1) with no break condition. It should break after it can no longer read any lines.
Every time you work with a file, that means fopen, fgets, and fscanf, you need to check whether the operation succeeded. If it fails, the code will continue with whatever garbage is the result.
This is especially a problem with fscanf because if it fails, it leaves the file pointer where it was, and might continuously rescan the same line over and over again. In general, avoid scanf and fscanf. Instead, fgets the whole line, to ensure it gets read, and scan it with sscanf.
The other problem is how you're allocating memory isn't right.
filmiab *db;
This puts a pointer on the stack, but it points to garbage. No memory has been allocated for the actual struct.
db->nimed = (char*)malloc(sizeof(db->nimed) * sizeof(char));
sizeof(db->nimed) is not the length of the string in db->nimed, but the size of the pointer. Probably 4 or 8. So you've only allocated 4 or 8 bytes.
fgets(db->nimed, 100, f1);
Then you read up to 100 bytes into it with fgets, probably causing a buffer overflow.
db->nimed = (char*)realloc(db->nimed, sizeof(char) * sizeof(db->nimed));
Then you reallocate too little, too late. Again, same as before, this is allocating only 4 or 8 bytes. It's probably doing nothing because the memory was already this size.
To fix this, start by putting the whole struct on the stack.
filmiab db;
Then allocate the necessary space for its strings. Note that since sizeof(char) is always 1 there's no need to include it. There's also no need to cast the result of malloc.
db.nimed = malloc(100);
db.rezi = malloc(100);
Now there's no need to realloc, you've got you 100 bytes of memory and can write to it with fgets.
For future reference, here's how I'd rework this.
int main() {
filmiab db;
char file[] = "filmid.txt";
FILE *f1 = fopen(file, "r");
if( f1 == NULL ) {
fprintf( stderr, "Could not open %s for reading: %s", file, strerror(errno) );
}
char line[1024];
int state = 0;
while(fgets(line, 1024, f1) != NULL) {
switch(state % 5) {
case 0:
sscanf(line, "%d", &db.koodpk);
break;
case 1:
db.nimed = strdup(line);
break;
case 2:
sscanf(line, "%d", &db.aasta);
break;
case 3:
sscanf(line, "%d", &db.kestus);
break;
case 4:
db.rezi = strdup(line);
printf("Filmi kood: %d\nFilmi nimi: %sAasta: %d\nKestus minutites: %d\nFilmi rezis66r: %s\n",
db.koodpk, db.nimed, db.aasta, db.kestus, db.rezi);
printf("\n");
break;
default:
// Should never get here
assert(0);
break;
}
state++;
}
return 0;
}
There's a single, large line buffer which gets reused, it's 1K but it's only 1K once. strdup duplicates strings, but only allocates enough memory to hold the string. This eliminates the need to predict how big lines are, and it also avoids fragmenting memory with a lot of reallocs.
In this particular case, since db is being reused, it would be more optimal to just allocate 1024 each for db.nimed and db.rezi, but I wanted to demonstrate the more general case where the stuff read in will stick around.
while(fgets(line, 1024, f1) != NULL) ensures I'll read until the end of the file. Then line is processed using the switch statement depending on what sort of line is coming next. This separates the process of reading from a file, which can be unpredictable and needs a lot of error checking, from processing the data which is a bit easier. Technically I should be checking if those sscanfs succeeded, but I got lazy. :)
I am using this code to read a file:
char* fs_read_line(FILE* file)
{
if (file == NULL) {
return "CFILEIO: Error while reading the file: Invalid File";
}
long threshold = ftell(file);
fseek(file, 0, SEEK_END);
uint8_t* buffer = calloc(ftell(file)-threshold, sizeof(uint8_t));
if(buffer == NULL)
return;
int8_t _;
fseek(file, threshold, SEEK_SET);
uint32_t ct = 0;
while ((_ = (char)(fgetc(file))) != '\n'
&& _ != '\0' && _ != '\r' && _ != EOF) {
buffer[ct++] = _;
}
buffer = realloc(buffer, sizeof *buffer * (ct + 1));
buffer[ct] = '\0';
return buffer;
}
If the file is too big, I get (heap) overflow errors, probably because I initally allocate the file with the total amount of characters it contains.
an other way I tried to do this is by realloc the buffer after every iteration, but that's kinda not the approach I want.
Is there any way to dynamicly change the size of the array depending on the the current iteration without always uisng realloc ? or is there an way to determine how long the current line is by using ftell and fseek?
Code does not return a pointer to a string.
There is no null character in the returned buffer, so the calling code lacks the ability to know the length of the allocated memory. This certainly causes the calling code to error.
When re-allocating, add 1.
// buffer = realloc(buffer, ct * sizeof(uint8_t*));
// v--- no star
buffer = realloc(buffer, ct * sizeof(uint8_t ) + 1);
buffer[ct] = '\0';
// or better
size_t ct = 0;
...
buffer = realloc(buffer, sizeof *buffer * (ct + 1));
buffer[ct] = '\0';
Is there any way to dynamically change the size of the array allocated memory depending on the the current iteration without always using realloc?
Array sizes cannot change. To dynamically change the size of the allocated memory requires realloc(). Note: the amount of needed memory could be determined before a memory allocation call.
or is there an way to determine how long the current line is by using ftell and fseek?
Like this code, you have found an upper bound to the current line's length. ftell and fseek do not locate the end of line.
Code could "seek" to the end of line with fscanf(file, "%*[^\n]"); or 1 beyond with a following fgetc(file).
If your file can't fit in memory it can't fit in memory. You are allocating the memory buffer in advance, but you're making two mistakes that can cause you to allocate more than you need.
You're starting at some arbitrary position in the file, but allocate memory as if you're starting at the beginning of the file. Allocate ftell(file) - threshold bytes.
You are are allocation way too much memory. The sizeof(uint8_t *) should be sizeof(uint8_t) instead. You're allocating 4 or 8 times more memory than you should.
Other than that, what's the point of reallocating the buffer after you're done writing to it? The memory overflow has already happened. You should allocate before writing (inside the while loop). I don't see the point of reallocating at all, though, since you're allocating more than enough memory to begin with.
the following code:
cleanly compiles
performs the desired operation
properly handles error conditions
properly declares variable types
properly returns a char* rather than a uint8_t*
leaves open the question: why return 2x the needed buffer length
the error message displayed when the passed in parameter is NULL is not correct. Suggest changing to indicate passed in file pointer was NULL
the OPs posted code fails to check the returned value from each call to fseek() and fails to check the return value from each call to ftell() which it should be doing to assure the operation(s) was successful. I did not add that error checking in my answer so as to not clutter the code, however, it should be performed.
and now, the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
char* fs_read_line(FILE* file);
char* fs_read_line(FILE* file)
{
if ( !file )
{
return "CFILEIO: Error while reading the file: Invalid File";
}
// implied else, valid parameter
long threshold = ftell(file);
fseek(file, 0, SEEK_END);
char* buffer = calloc( (size_t)(ftell(file) - threshold) *2 +1, sizeof(char));
if(buffer == NULL)
return NULL;
// implied else, calloc successful
int ch;
fseek(file, threshold, SEEK_SET);
size_t ct;
while ( (ch = fgetc(file)) != '\n'
&& ch != '\0'
&& ch != '\r'
&& ch != EOF)
{
buffer[ct++] = (char)ch;
}
return buffer;
} // end function: fs_read_line
I am trying to deconstruct a document into its respective paragraphs, and input each paragraphs, as a string, into an array. However, each time a new value is added, it overwrites all previous values in the array. The last "paragraph" read (as denoted by newline) is the value of each non-null value of the array.
Here is the code:
char buffer[MAX_SIZE];
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
int pp = 0;
int i;
FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc);
while((i = fgets(buffer, sizeof(buffer), doc) != NULL)) {
if(strncmp(buffer, "\n", sizeof(buffer))) {
paragraphs[pp++] = (char*)buffer;
}
}
printf("pp: %d\n", pp);
for(i = 0; i < MAX_SIZE && paragraphs[i] != NULL; i++) {
printf("paragraphs[%d]: %s", i, paragraphs[i]);
}
The output I receive is:
pp: 4
paragraphs[0]: paragraph four
paragraphs[1]: paragraph four
paragraphs[2]: paragraph four
paragraphs[3]: paragraph four
when the program is run as follows: ./prog.out doc.txt, where doc.txt is:
paragraph one
paragraph two
paragraph three
paragraph four
The behavior of the program is otherwise desired. The paragraph count works properly, ignoring the line that contains ONLY the newline character (line 4).
I assume the problem occurs in the while loop, however am unsure how to remedy the problem.
Your solution is pretty sound. Your Paragraph array is supposed to hold each paragraph, and since each paragraph element is just a small 4 bytes pointer you can afford to define a reasonable max number of them. However, since this max number is a constant, it is of little use to allocate the array dynamically.
The only meaningful use of dynamic allocation would be to read the whole text once to count the actual number of paragraphs, allocate the array accordingly and re-read the whole file a second time, but I doubt this is worth the effort.
The downside of using fixed-size paragraph array is that you must stop filling it once you reach the maximal number of elements.
You can then re-allocate a bigger array if you absolutely want to be able to process the whole Bible, but for an educational exercise I think it's reasonable to just stop recording paragraphs (thus producing a code that can store and count paragraphs up to a maximal number).
The real trouble with your code is, you don't store the paragraph contents anywhere. When you read the actual lines, it's always inside the same buffer, so each paragraph will point to the same string, which will contain the last paragraph read.
The solution is to make a unique copy of the buffer and have the current paragraph point to that.
C being already messy enough as it is, I suggest using the strdup() function, which duplicates a string (basically computing string length, allocating sufficient memory, copying the string into it and returning the new block of memory holding the new copy). You just need to remember to free this new copy once you're done using it (in your case at the end of your program).
This is not the most time-efficient solution, since each string will require a strlen and a malloc performed internally by strdump while you could have pre-allocated a big buffer for all paragraphs, but it is certainly simpler and probably more memory-efficient (only the minimal amount of memory will be allocated for each string, though each malloc consumes a few extra bytes for internal allocator housekeeping).
The bloody awkward fgets also stores the trailing \n at the end of the line, so you'll probably want to get rid of that.
Your last display loop would be simpler, more robust and more efficient if you simply used pp as a limit, instead of checking uninitialized paragraphs.
Lastly, you'd better define two different constants for max line size and max number of paragraphs. Using the same value for both makes little sense, unless you're processing perfectly square texts :).
#define MAX_LINE_SIZE 82 // max nr of characters in a line (including trailing \n and \0)
#define MAX_PARAGRAPHS 100 // max number of paragraphs in a file
void main (void)
{
char buffer[MAX_LINE_SIZE];
char * paragraphs[MAX_PARAGRAPHS];
int pp = 0;
int i;
FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc != NULL);
while((fgets(buffer, sizeof(buffer), doc) != NULL)) {
if (pp != MAX_PARAGRAPHS // make sure we don't overflow our paragraphs array
&& strcmp(buffer, "\n")) {
// fgets awkwardly collects the ending \n, so get rid of it
if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = '\0';
// current paragraph references a unique copy of the actual text
paragraphs[pp++] = strdup (buffer);
}
}
printf("pp: %d\n", pp);
for(i = 0; i != pp; i++) {
printf("paragraphs[%d]: %s", i, paragraphs[i]);
free(paragraphs[i]); // release memory allocated by strdup
}
}
What is the proper way to allocate the necessary memory? Is the malloc on line 2 not enough?
No, you need to allocate memory for the 2D array of strings you created. The following will not work as coded.
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
If you have: (for a simple explanation)
char **array = {0}; //array of C strings, before memory is allocation
Then you can create memory for it like this:
int main(void)
{
int numStrings = 10;// for example, change as necessary
int maxLen = MAX_SIZE; //for example, change as necessary
char **array {0};
array = allocMemory(array, numStrings, maxLen);
//use the array, then free it
freeMemory(array, numStrings);
return 0;
}
char ** allocMemory(char ** a, int numStrings, int maxStrLen)
{
int i;
a = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(sizeof(char)*maxStrLen + 1, sizeof(char));
}
return a;
}
void freeMemory(char ** a, int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(a[i]) free(a[i]);
free(a);
}
Note: you can determine the number of lines in a file several ways, One way for example, by FILE *fp = fopen(filepath, "r");, then calling ret = fgets(lineBuf, lineLen, fp) in a loop until ret == EOF, keeping count of an index value for each loop. Then fclose(). (which you did not do either) This necessary step is not included in the code example above, but you can add it if that is the approach you want to use.
Once you have memory allocated, Change the following in your code:
paragraphs[pp++] = (char*)buffer;
To:
strcpy(paragraphs[pp++], buffer);//no need to cast buffer, it is already char *
Also, do not forget to call fclose() when you are finished with the open file.
I'm trying to read a file into an array of strings using getline() and realloc(). I have used very similar code in the past for tokenizing strings and everything worked correctly. I'll consider the sample input file as
1
2
3
4
5
Here's the code:
char** read_input(const char* input_file, char* line){
FILE *fp;
size_t len = 0;
size_t nums = 0;
ssize_t read;
char** res = NULL;
if ((fp = fopen(input_file, "r")) == NULL){
printf("Incorrect file\n", strerror(errno));
exit(EXIT_FAILURE);
}
while ((read = getline(&line, &len, fp)) != -1){
if ((res = realloc(res, sizeof(char*) * ++nums)) == NULL)
exit(EXIT_FAILURE);
char to_strip[sizeof(read) * sizeof(char)];
strcpy(to_strip, line);
if (line[read - 1] == '\n')
to_strip[read - 1] = 0;
else
line[read] = 0;
res[nums - 1] = to_strip;
}
free(line);
if ((res = realloc(res, sizeof(char*) * (nums + 1))) == NULL)
exit(EXIT_FAILURE);
res[nums - 1] = 0;
return res;
}
After the loop, if I print the contents of the array, I get:
5
5
5
5
5
despite the fact that if I call print inside the loop, after each assignment to res, I get the right numbers. This is really stumping me, because I can't see what could be wrong except with realloc, but I thought that realloc preserved array contents. Thanks.
You're busy invoking undefined behaviour because you're storing a pointer to to_strip in the reallocated array each time, and the pointer goes out of scope each iteration of the loop, and gets overwritten on each iteration which is why you see the same value at the end. If you printed all the values in the loop rather than just the current value, you'd see first 1, then 2 2, then 3 3 3, then 4 4 4 4 and finally 5 5 5 5 5. If you did enough work after returning from this function before printing the results, you'd see garbage as the space would be used for other purposes.
You need to make copies of the lines you store in your reallocated array. The simplest is to use strdup():
res[nums - 1] = strdup(to_strip);
Don't forget to release the strings as well as the array of pointers to the strings.
Don't forget to close the file you opened before you return.
It seems odd to pass line into the function. It must either be a null pointer or pointing to space that can be passed to realloc(). Since you then free() the space before returning, the calling function needs to know that you've released the space it passed you — and since you didn't know how big it was, you told getline() it was of size zero, so it had to be released. The interface would be cleaner without that parameter; use a local char *line = 0; at the start of the function.
This is a problem:
ssize_t read;
char to_strip[sizeof(read) * sizeof(char)];
strcpy(to_strip, line);
sizeof(read) - probably 4 or 8 - is a strange amount to allocate for a buffer that you are copying a string into. I think you meant char to_strip[ read + 1 ];. However, later on you have the line:
res[nums - 1] = to_strip;
which places a pointer to to_strip into res. However, to_strip ceases to exist at the end of the for loop, so these will be wild pointers. If your intent is to store all of the text read from the file for later access then you will need to allocate memory for each line.
Jonathan Leffler's suggestion of strdup is probably the simplest solution to this; thanks to him for clearing up my erroneous use of getline.
You could also do away with to_strip entirely, as you could just overwrite the \n directly in line.