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?
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.
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;
}
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++;
}
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.