Creating an array from a text file to call each line individually - c

I've looked at multiple other similar questions but none of them worked with my code, and I gave up trying to look things up.
I'm trying to create a program that takes each line, which has a book title, from a file into a character array, because I need to call each book later, so book[1], book[2], etc. However, I cannot figure out how to create the array with my struct.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#define Num_Book 9
typedef struct book {
char *book_name;
size_t length;
ssize_t title;
}BOOK;
int main(int argc, char* argv[])
{
BOOK *Book;
Book = (BOOK *) malloc (sizeof(BOOK));
(*Book).book_name = NULL;
(*Book).length = 0;
char title_arr[Num_Book][50];
//this is the array that I tried creating,
//but it kept giving me warnings when I tried to compile
//opening my file
FILE *f_books;
f_books = fopen("books.txt", "r");
if (f_books == NULL)
{
printf("Cannot open file for reading.\n");
}
printf("Book list\n");
while (((*Book).title = getline(&(*Book).book_name, &(*Book).length, f_books)) != -1)
{
printf("%s", (*Book).book_name);
}
If anyone has any ideas, it is greatly appreciated. Thank you!

The simplest approach is to declare an array of structs in main(), using the Num_Book macro that you defined in the preprocessor directives. Since you are using getline(), you do not even need to manually allocate memory to store the strings; instead you can let the getline() function do the work. Note that getline() is not a Standard C function, but it is POSIX, and also a GNU extension. On some systems you may need to enable this function using a feature test macro, which is included in the code below.
To take advantage of the automatic memory allocation capability of getline(), you need to pass in a null pointer for the first argument, and the second argument should be a pointer to a size_t variable containing a value of zero.
In the loop that reads the data into the structs, temporary variables are used instead of directly assigning to the struct fields. This allows the current string to be inspected before the final assignment, so that empty lines are not stored as book records.
Remember that getline() keeps the \n character, so if the current line is non-empty, the terminating newline is replaced with a \0 terminator. Then the values held by the temporary variables are assigned to the appropriate fields of the current BOOK struct, temp_name and temp_length are reset to NULL and 0, and i is incremented.
You still need to free memory allocated by getline(), so this should be done before ending the program.
Note that in the original code, while you were checking to be certain that the file books.txt was successfully opened, you did not exit() here. This would lead to problems in the event that the file failed to open, when the program went on as if it had opened. You could handle the error differently; for example, it may be appropriate to instead ask the user for a different filename.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#define Num_Book 9
typedef struct book {
char *book_name;
size_t length;
ssize_t title;
} BOOK;
int main(void)
{
BOOK books[Num_Book];
FILE *f_books;
f_books = fopen("books.txt", "r");
if (f_books == NULL)
{
fprintf(stderr, "Cannot open file for reading.\n");
exit(EXIT_FAILURE);
}
printf("Book list\n");
char *temp_name = NULL;
size_t temp_length = 0;
ssize_t temp_title;
char *find;
size_t i = 0;
while ((temp_title = getline(&temp_name, &temp_length, f_books))
!= -1 && temp_name[0] != '\n') {
/* Replace newline with '\0' */
if ((find = strchr(temp_name, '\n')) != NULL) {
*find = '\0';
}
books[i].book_name = temp_name;
books[i].length = temp_length;
books[i].title = temp_title;
temp_name = NULL;
temp_length = 0;
printf("%s\n", books[i].book_name);
++i;
}
/* Still need to free allocated memory */
for (size_t j = 0; j < i; j++) {
free(books[j].book_name);
}
if (temp_name) {
free(temp_name);
}
if (fclose(f_books) != 0) {
fprintf(stderr, "Unable to close file\n");
exit(EXIT_FAILURE);
}
return 0;
}
Program output:
Book list
The Sound and the Fury
So Long, and Thanks for All the Fish
Gargantua and Pantagruel
Foundation
If you need to dynamically allocate space for the books, you can modify the above code. Below is a version that does this, first by initializing a variable max_books to a reasonable starting value. Space is allocated and assigned to a pointer to BOOK, and when a new book is added the space is reallocated if necessary.
After all books have been added, the allocation can be trimmed to exact size. Note the use of identifiers instead of explicit types in the sizeof arguments. This is less error-prone and easier to maintain if types change in future iterations of the code. Also note that realloc() will return a null pointer in the event of an allocation error. Here, direct assignment to books would result in the loss of previously stored data, and a memory leak. For this reason, the address of the new allocation is first stored in temp; the value of temp is assigned to books only if it is not NULL.
The same allocations must be freed as before, but in addition, the dynamically allocated array must also be freed.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
typedef struct book {
char *book_name;
size_t length;
ssize_t title;
} BOOK;
int main(void)
{
FILE *f_books;
f_books = fopen("books.txt", "r");
if (f_books == NULL)
{
fprintf(stderr, "Cannot open file for reading.\n");
exit(EXIT_FAILURE);
}
char *temp_name = NULL;
size_t temp_length = 0;
ssize_t temp_title;
char *find;
size_t i = 0;
BOOK *books;
BOOK *temp;
size_t max_books = 10;
size_t num_books = 0;
if ((books = malloc((sizeof *books) * max_books)) == NULL) {
fprintf(stderr, "Unable to allocate books\n");
exit(EXIT_FAILURE);
}
while ((temp_title = getline(&temp_name, &temp_length, f_books))
!= -1 && temp_name[0] != '\n') {
++num_books;
/* Replace newline with '\0' */
if ((find = strchr(temp_name, '\n')) != NULL) {
*find = '\0';
}
/* Reallocate books if more space is needed */
if (num_books == max_books) {
max_books *= 2;
if ((temp = realloc(books, (sizeof *books) * max_books)) == NULL) {
fprintf(stderr, "Unable to reallocate books\n");
exit(EXIT_FAILURE);
}
books = temp;
}
/* Store book data */
books[i].book_name = temp_name;
books[i].length = temp_length;
books[i].title = temp_title;
temp_name = NULL;
temp_length = 0;
++i;
}
/* If you need books to be trimmed to minimum size */
if ((temp = realloc(books, (sizeof *books) * num_books)) == NULL) {
fprintf(stderr, "Unable to trim books allocation\n");
exit(EXIT_FAILURE);
}
books = temp;
/* Display list of books */
printf("Book list\n");
for (i = 0; i < num_books; i++) {
printf("%s\n", books[i].book_name);
}
/* Still need to free allocated memory */
for (i = 0; i < num_books; i++) {
free(books[i].book_name);
}
free(books);
if (temp_name) {
free(temp_name);
}
if (fclose(f_books) != 0) {
fprintf(stderr, "Unable to close file\n");
exit(EXIT_FAILURE);
}
return 0;
}

Related

Why does my C program crash when i add any statement to the main function?

I'm reasonably new to C - I've made a program that reads a text file and through several functions processes the text by using malloc() and realloc() and formats an output. At the moment I only have the following in my main():
1. initializing the structs
line_arr.max_size = 0;
line_arr.num_lines = 0;
line_arr.line = NULL;
Word_Array word_arr;
word_arr.max_size_ = 0;
word_arr.num_words = 0;
word_arr.word = NULL;
Word_Array ex_words;
ex_words.max_size_ = 0;
ex_words.num_words = 0;
ex_words.word = NULL;
Line_Array.line and Word_Array.word are both pointers to structs Line and Word, respectively. Line and Word are structs that hold a char array and an int that keeps track of what line number the line is or the word occurs in.
2. calling the functions that process the input text files
get_lines(&line_arr, argv[1]);
get_words(&line_arr, &word_arr);
sort_keywords(&word_arr);
All of my functions return void.
All the output and formatting occurs within the functions and any space I have malloc'd I have subsequently freed without any error. The program produces the desired output, however if I add any statement to the main() even a print statement, I get a bus10 error or a segmentation fault 11 error.
I have tried gdb but I haven't used it before, and it seems as though gdb hangs and freezes when I try to run the program anyway.
I'm wondering if anyone knows why this is the case? / is there some fundamental logic I'm not understanding about C or malloc()?
Thanks for your expertise!
edit
structs:
typedef struct Line Line;
struct Line{
char line[MAX_LINE_LEN];
};
typedef struct Word{
char word[MAX_WORD_LEN];
int line_number;
}Word;
typedef struct Word_Array{
//will point to the base in memory where the list of words begins.
Word *word;
int max_size_;
int num_words;
}Word_Array;
typedef struct Line_Array{
//line is a variable that stores an address of a line object.
Line *line;
int max_size;
int num_lines;
}Line_Array;
get_lines():
void get_lines(Line_Array *la, char const *filename){
Line *line_ptr;
char *buffer;
size_t buffer_len;
FILE *fp = fopen(filename, "r");
if(fp == NULL){
printf("Can't read file. \n");
exit(1);
}
while (getline(&buffer, &buffer_len, fp) > 0) {
buffer[strlen(buffer)-1] = '\0';
if (la->line == NULL) {
la->line = (Line *) malloc(sizeof(Line));
if (la->line == NULL) {
exit(1);
}
la->max_size = 1;
la->num_lines = 0;
}
else if (la->num_lines >= la->max_size) {
line_ptr = (Line *) realloc(la->line, (2*la->max_size) * sizeof(Line));
if (line_ptr == NULL) {
exit(1);
}
la->max_size *= 2;
la->line = line_ptr;
}
strncpy(la->line[la->num_lines].line, buffer, MAX_LINE_LEN);
la->num_lines++;
}
fclose(fp);
}
I haven't freed the memory in this method since I make use of it later, but even when other functions aren't being run the same problem exists where if I add something to the main before or after calling get_lines, I receive bus 10 error as my only output. However if i only call get_lines() and other functions the program produces the right output.
A least one problem:
Variables need to be initialized before getline() usage. #Weather Vane
//char *buffer;
//size_t buffer_len;
char *buffer = NULL;
size_t buffer_len = 0;
Notes:
After the while (getline(... , code should free with free(buffer);
strncpy(la->line[la->num_lines].line, buffer, MAX_LINE_LEN); does not insure la->line[la->num_lines].line is a string. Why is strncpy insecure?

Segmentation fault due to string copy

In the code below the code crashes after executing while loop for 4th time, and it crashes in strcpy
strcpy(userver_info[location].Tag_Name, field);
When debugging it gave following error
Can't find a source file at "strcpy-sse2-unaligned.S"
I am not able to figureout what is the issue which is causing crash here, after facing the above issue i did structure padding thinking that may be the issue and even that is not working out, can anyone please tell me why is the code crashing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 1024
#define NUM_OF_UNDERLYING_SERVER 0
#define SHM_ADDR_LEN 15
#define SERVER_NAME_LEN 25
#define TAG_NAME_LEN 25
#define TAG_DESC_LEN 30
#define ACCESS_LEVEL_LEN 5
#define DATA_TYPE_NAME_LEN 10
typedef struct {
char SHM_Location[SHM_ADDR_LEN];
char Server_ID[SERVER_NAME_LEN];
char Tag_Name[TAG_NAME_LEN];
int Tag_ID;
char Tag_Description[TAG_DESC_LEN];
char Access_Level[ACCESS_LEVEL_LEN];
char Data_Type[DATA_TYPE_NAME_LEN];
}u_server_info;
typedef struct {
int num_of_tags;
u_server_info *underlying_server_data;
}opcua_u_server;
void parse_underlying_opcua_server(char * buffer, u_server_info *userver_info) {
static int location = 0;
char *field;
/* get SHM_Location */
field = strtok(buffer,",");
strcpy(userver_info[location].SHM_Location, field);
/* get Server_ID */
field=strtok(NULL,",");
strcpy(userver_info[location].Server_ID, field);
/* get Tag_Name */
field = strtok(NULL,",");
strcpy(userver_info[location].Tag_Name, field);
/* get Tag_ID */
field=strtok(NULL,",");
userver_info[location].Tag_ID = atoi(field);
/* get Tag_Description */
field = strtok(NULL,",");
strcpy(userver_info[location].Tag_Description, field);
/* get Access_Level */
field = strtok(NULL,",");
strcpy(userver_info[location].Access_Level, field);
/* get Data_Type */
field = strtok(NULL,",");
strcpy(userver_info[location].Data_Type, field);
/* display the result */
printf("%s\t %s\t %s\t %d\t %s\t %s\t %s\n",
userver_info[location].SHM_Location,
userver_info[location].Server_ID,
userver_info[location].Tag_Name,
userver_info[location].Tag_ID,
userver_info[location].Tag_Description,
userver_info[location].Access_Level,
userver_info[location].Data_Type);
location++;
}
int get_line_count(char *filename) {
FILE *fp;
int line_count = 0;
char c;
fp = fopen(filename, "r");
if (fp == NULL){
printf("Unable to open file '%s' to count lines\n",filename);
return -1;
}
for (c = getc(fp); c != EOF; c = getc(fp))
if (c == '\n') // Increment count if this character is newline
line_count = line_count + 1;
fclose(fp);
return line_count;
}
int main(int argc, char **argv)
{
char filename[] = "underlying_server.csv";
char buffer[BUFFER_SIZE];
FILE *fp;
int tags_count = 0;
opcua_u_server *ua_server = NULL;
/* open the CSV file */
fp = fopen(filename,"r");
if( fp == NULL) {
printf("Unable to open file '%s'\n",filename);
exit(EXIT_FAILURE);
}
tags_count = get_line_count(filename);
/* Allocate memmory for each Underlying server */
ua_server= malloc(sizeof(opcua_u_server));
if (ua_server == NULL) {
printf("Malloc failed");
exit(EXIT_FAILURE);
}
/* Allocate memory for each underlying server tag information */
ua_server[NUM_OF_UNDERLYING_SERVER].underlying_server_data =
malloc(tags_count * sizeof(ua_server->underlying_server_data));
if (ua_server[NUM_OF_UNDERLYING_SERVER].underlying_server_data == NULL) {
printf("Malloc failed");
exit(EXIT_FAILURE);
}
/* process the data */
while(fgets(buffer,BUFFER_SIZE,fp)) {
parse_underlying_opcua_server(buffer,
ua_server[NUM_OF_UNDERLYING_SERVER].underlying_server_data);
}
ua_server->num_of_tags = tags_count;
/*Reset the value for next server*/
tags_count = 0;
/* close file */
fclose(fp);
return(0);
}
My underlying_server.csv is like this
1000,Server_1,Tag_1,3000,Created_tag_3000,AR,BOOL
1001,Server_1,Tag_2,3001,Created_tag_3001,AR,BOOL
1002,Server_1,Tag_3,3002,Created_tag_3002,AR,BOOL
1003,Server_1,Tag_4,3003,Created_tag_3003,AR,BOOL
1004,Server_1,Tag_5,3004,Created_tag_3004,AR,REAL
1005,Server_1,Tag_6,3005,Created_tag_3005,AR,REAL
1006,Server_1,Tag_7,3006,Created_tag_3006,AW,REAL
1007,Server_1,Tag_8,3007,Created_tag_3007,AW,REAL
1008,Server_1,Tag_9,3008,Created_tag_3008,AW,REAL
1009,Server_1,Tag_10,3009,Created_tag_3009,AW,REAL
1010,Server_1,Tag_11,3010,Created_tag_3010,AW,REAL
1011,Server_1,Tag_12,3011,Created_tag_3011,AW,DWORD
1012,Server_1,Tag_13,3012,Created_tag_3012,AW,DWORD
1013,Server_1,Tag_14,3013,Created_tag_3013,AR,DWORD
1014,Server_1,Tag_15,3014,Created_tag_3014,AR,DWORD
1015,Server_1,Tag_16,3015,Created_tag_3015,AR,DWORD
1016,Server_1,Tag_17,3016,Created_tag_3016,AR,DWORD
1017,Server_1,Tag_18,3017,Created_tag_3017,AR,DWORD
1018,Server_1,Tag_19,3018,Created_tag_3018,AR,DWORD
1019,Server_1,Tag_20,3019,Created_tag_3019,AR,DWORD
1020,Server_1,Tag_21,3020,Created_tag_3020,AR,DWORD
1021,Server_1,Tag_22,3021,Created_tag_3021,AW,BOOL
1022,Server_1,Tag_23,3022,Created_tag_3022,AW,BOOL
1023,Server_1,Tag_24,3023,Created_tag_3023,AW,BOOL
1024,Server_1,Tag_25,3024,Created_tag_3024,AW,BOOL
1025,Server_1,Tag_26,3025,Created_tag_3025,AW,BOOL
1026,Server_1,Tag_27,3026,Created_tag_3026,AW,DWORD
1027,Server_1,Tag_28,3027,Created_tag_3027,AR,DWORD
1028,Server_1,Tag_29,3028,Created_tag_3028,AR,DWORD
1029,Server_1,Tag_30,3029,Created_tag_3029,AR,DWORD
1030,Server_1,Tag_31,3030,Created_tag_3030,AR,REAL
I'm sure there is a dup for this, you are allocating the second buffer based on the sizeof the pointer, not the object being pointed to:
/* Allocate memory for each underlying server tag information */
ua_server[NUM_OF_UNDERLYING_SERVER].underlying_server_data =
malloc(tags_count * sizeof(ua_server->underlying_server_data));
becomes:
/* Allocate memory for each underlying server tag information */
ua_server[NUM_OF_UNDERLYING_SERVER].underlying_server_data =
malloc(tags_count * sizeof(*(ua_server->underlying_server_data)));
Note the actual code *(ua_server->underlying_server_data is bad at this moment, but sizeof is compile-time and is just using the code as a template to get the object size.
I sometimes wish there was a compiler warning for "You asked for the sizeof a pointer"
Never use strcpy, it's insecure, because it doesn't perform any form of bounds constraining and will just soldier on copying until it hits a 0 byte in the source buffer. If the source buffer isn't (properly) 0-terminated, or the contents of the source buffer are too long to fit into the destination, it will create undefined behavior (crash or be vulnerable to attack).
Putting aside the problem with fixed size fields, it is absolutely necessary to check the size limits of buffer operations. The standard function you'd use (although it still isn't perfect) is strncpy (note the extra n). The caveat is, that strncpy will omit the terminating 0 byte if it doesn't fit into the destination.
Since your fields are fixed size you can use it like this
strncpy(userver_info[location].SHM_Location, field, SHM_ADDR_LEN-1);
userver_info[location].SHM_Location[SHM_ADDR_LEN-1] = 0;
i.e. it copies up to SHM_ADDR_LEN-1 bytes and will always put a terminating 0 at the very end of the buffer.

Do i need to use 2D arrays for an array of strings in C?

I want my program to read N words from a text file and save them in an array. My question is, do i need a 2D Array e.g: char **wordList or is the 1D Array in the example below sufficient? The output is correct except from the last string which as you can see is weird. Also, am i allocating sufficient memory for the array and why does the last output string come out wrong?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void populateWordsArray(int);
FILE *file;
char *word;
char **wordList;
/*
* Function populateWordsArray: reads N words from
* the given file and creates an array with them.
* Has one argument: int N - the number of words to read.
*/
void populateWordsArray(int N) {
int i = 0;
while(!feof(file) && i < N) {
fscanf(file,"%s",&word[i]);
printf("%s\n",&word[i]);
i++;
}
}
int main(int argc,char *argv[]) { // argv[1] = op argv[2] = name
int N = 0;
file = fopen(argv[2],"r");
if(file == (FILE *) NULL) { // check if the file opened successfully
fprintf(stderr,"Cannot open file\n");
}
fscanf(file,"%d",&N); // get the N number
word = malloc(N * sizeof(char));
populateWordsArray(N);
// write a switch method for the various ops
// call the appropriate function for each operation
free(word);
fclose(file);
return 0;
}
Output:
this
is
a
test!
with
files.
new
line,
here.
ere.
text file content:
10 this is a test! with files.
new line, here.
Your example is wrong. When executing the line fscanf(file,"%s",&word[i]);, the third argument should be the address where the function will write the read data. In your case, word[i] is the i-th element of the array and &word[i] is its address. So, the word will be stored with the first character at the word[i]. Your code only prints something because you print it immediately. Also, you don't get a segfault by pure chance.
If you want to read a string into a buffer, you first need to allocate the space for the buffer.
By using char **, you can make it into a 2D array by first allocating sufficient space for the array of pointers and then allocate sufficient space for each of the pointers to hold an address to a string.
I have rewritten your program for you:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STRING_LENGTH 100
void populateWordsArray(int);
FILE *file;
char **wordList;
void populateWordsArray(int N)
{
int i = 0;
while(i < N && fscanf(file,"%s", wordList[i]) == 1) // fscanf returns the number of successfully read items. If it's not 1, the read failed. You can also check if the return value is not EOF but, in this situation, it's the same.
{
printf("%s\n", wordList[i]); // i-th element is an address to a buffer that contains 100 bytes
i++;
}
}
int main(int argc,char *argv[])
{
int N = 0, i;
file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.
if(file == NULL) // No need to cast NULL into a specific type.
{
fprintf(stderr,"Cannot open file\n");
return 1; // You might want to end the program here, possibly with non-zero return value.
}
fscanf(file,"%d",&N);
wordList = malloc(N * sizeof(char*)); // Allocating space for pointers
for(i=0; i<N; i++)
{
wordList[i] = malloc(MAX_STRING_LENGTH); // Allocating space for individual strings
}
populateWordsArray(N);
for(i=0; i<N; i++)
{
free(wordList[i]);
}
free(wordList);
fclose(file);
return 0;
}
I'd also advise against using global variables here.
EDIT: As the comments may suggest, this code is not the best solution. First, all the words might not fit into a 100 byte buffer. To alleviate this issue, allocate a large, fixed-size buffer, read every word into it, then allocate corresponding number of bytes for wordList[i] (don't forget the terminating null byte) and copy the data from the fixed-size buffer into wordList[i].
Also, the code has some missing error checks. For instance, the file may exist but is empty, in which case fscanf(file,"%d",&N); will return EOF. Also, the number at the beginning of the file may not be corresponding to the number of the lines that follow or N might be a negative number (the code allows for it by specifying it to be int).
EDIT2: As #bruno suggested, I made a version that I think is more bulletproof than the previous one. It's possible that I omitted something, I'm in a bit of a hurry. If so, let me know below.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STRING_LENGTH 512
char line_buffer[MAX_STRING_LENGTH]; // A line of the maximum size that can occur.
char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines);
char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines)
{
*readLines=0;
char** wordList;
// Allocating space for pointers
wordList = malloc(wantedLines * sizeof(char*));
if(!wordList)
{
fprintf(stderr,"Cannot allocate sufficient space for the pointers.\n");
exit(EXIT_FAILURE); // You may return NULL here and check it afterwards. The same goes for all the error checking inside this function
}
while(*readLines < wantedLines && fscanf(file,"%s", line_buffer) == 1)
{
wordList[*readLines] = malloc(strlen(line_buffer)+1);
if(!wordList[*readLines])
break;
if(NULL == (wordList[*readLines]=strdup(line_buffer)))
break;
(*readLines)++;
}
return wordList;
}
int main(int argc,char *argv[])
{
unsigned N = 0, i, M;
char **wordList;
FILE *file;
file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.
if(file == NULL) // No need to cast NULL into a specific type.
{
fprintf(stderr,"Cannot open file\n");
return 1; // You might want to end the program here, possibly with non-zero return value.
}
if(fscanf(file,"%d",&N) != 1)
{
fprintf(stderr,"Cannot read the number of lines. Empty file?\n");
return 1;
}
wordList = populateWordsArray(N, file, &M);
printf("Printing the read lines:\n");
for(i=0; i<M; i++)
{
printf("%s\n", wordList[i]);
}
for(i=0; i<M; i++)
{
free(wordList[i]);
}
free(wordList);
fclose(file);
return 0;
}

strstr() causing a segmentation fault error

The objective here is to take a whole text file that I dump into a buffer and then use the strcasestr() function to find the pointer of the word I am looking for within my buffer. It constantly gives me the segmentation fault error. At first, I thought it may be size so I tried with smaller sizes but it doesn't work either. The function only works with strings I create inside the actual code (ex : char * bob = "bob"; char * bobsentence = "bob is cool"; strstr(bobsentence, bob);). Which leads me to believe it has something to do with the fgets(). Any help is appreciated, really stuck on this one.
#define _GNU_SOURCE //to use strcasestr
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void textEdit(char *path, char *word){
printf("%s\n", path);
FILE *textFile;
//FILE *locationFile;
//FILE *tempFile;
char counter[1024];
int count = 0;
textFile = fopen(path, "r+");
//locationFile = fopen(path, "r+");
//opens file to read and write and opens temp file to write
if( textFile == NULL){ //|| tempFile == NULL || locationFile == NULL) ) {
printf ("\nerror\n");
return;
}
// SECTION : ALLOCATES MEMORY NEEDED FOR COPY TEXT IN ARRAY
// finds number of lines to estimate total size of array needed for buffer
while((fgets(counter, sizeof(counter), textFile)) != NULL){
count++;
}
fclose(textFile);
FILE *tempFile = fopen(path, "r+");
count *= 1024;
printf("%d %zu\n",count, sizeof(char));
char *buffer = malloc(count); //1024 is the max number of characters per line in a traditional txt
if(buffer == NULL){ //error with malloc
return;
}
// SECTION : DUMPS TEXT INTO ARRAY
if(fgets(buffer, count, tempFile) == NULL){
printf("error");
} //dumps all text into array
printf("%s\n", buffer);
char * searchedWord;
while((searchedWord = strcasestr(buffer, word)) != NULL){
}
fclose(tempFile);
//fclose(locationFile);
free(buffer);
}
It looks that you forgot to initialize count variable to 0:
int count = 0;
You increment it and it can contain any random value, even negative.
Also, note that your utilization of strstr doesn't look correct. The function returns the pointer to first occurrence that matches. Note, that it doesn't remember already found matches, so if match exists it should loop forever in this loop. Instead it should look like:
char *pos = buffer;
while((pos = strcasestr(pos, word)) != NULL){
searchedWord = pos;
/* do something with searchedWord but remember that it belongs to
allocated buffer and can't be used after free() */
pos++;
}

String / char * concatinate, C

Am trying to open a file(Myfile.txt) and concatenate each line to a single buffer, but am getting unexpected output. The problem is,my buffer is not getting updated with the last concatenated lines. Any thing missing in my code?
Myfile.txt (The file to open and read)
Good morning line-001:
Good morning line-002:
Good morning line-003:
Good morning line-004:
Good morning line-005:
.
.
.
Mycode.c
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[])
{
/* Define a temporary variable */
char Mybuff[100]; // (i dont want to fix this size, any option?)
char *line = NULL;
size_t len=0;
FILE *fp;
fp =fopen("Myfile.txt","r");
if(fp==NULL)
{
printf("the file couldn't exist\n");
return;
}
while (getline(&line, &len, fp) != -1 )
{
//Any function to concatinate the strings, here the "line"
strcat(Mybuff,line);
}
fclose(fp);
printf("Mybuff is: [%s]\n", Mybuff);
return 0;
}
Am expecting my output to be:
Mybuff is: [Good morning line-001:Good morning line-002:Good morning line-003:Good morning line-004:Good morning line-005:]
But, am getting segmentation fault(run time error) and a garbage value. Any think to do? thanks.
Specify MyBuff as a pointer, and use dynamic memory allocation.
#include <stdlib.h> /* for dynamic memory allocation functions */
char *MyBuff = calloc(1,1); /* allocate one character, initialised to zero */
size_t length = 1;
while (getline(&line, &len, fp) != -1 )
{
size_t newlength = length + strlen(line)
char *temp = realloc(MyBuff, newlength);
if (temp == NULL)
{
/* Allocation failed. Have a tantrum or take recovery action */
}
else
{
MyBuff = temp;
length = newlength;
strcat(MyBuff, temp);
}
}
/* Do whatever is needed with MyBuff */
free(MyBuff);
/* Also, don't forget to release memory allocated by getline() */
The above will leave newlines in MyBuff for each line read by getline(). I'll leave removing those as an exercise.
Note: getline() is linux, not standard C. A function like fgets() is available in standard C for reading lines from a file, albeit it doesn't allocate memory like getline() does.

Resources