// Struct for Country Data
typedef struct
{
char name[50]; // Country name
char code[3]; // Country code
int population; // Country Population
double lifeExp; // Country Life expectancy
} CountryData;
// Struct for Dir File
typedef struct
{
char code[50];
int offSet;
} DirData;
// Function Declarations
void fillCountryStructs(CountryData **dataPtr, int nLines, int fd);
void fillDirectoryStructs(CountryData **dataPtr, DirData **director, int nLines,int fd2);
void sortStructs(DirData **director, int nLines);
int verifyString(char *s1, char *s2);
// Main Function
// - This function starts the program, get the number of lines as a
// parameter, fills the structs and writes the data to the Country
// File and the Directory file.
int main(int argc, char *argv[]) // Always remember to pass an argument while executing
{
// Some variables
int nLines; // The number of lines
char *pEnd; // For String functions
FILE *Fin,*Fout; // File pointers
int fd;
int fd2;
nLines = strtod(argv[1], &pEnd);
CountryData **countryDataPtr; // Array of structs
CountryData **tempStruct;
DirData **director;
char buffer[15];
// Allocate memory for the struct pointers
countryDataPtr = calloc(nLines, sizeof(CountryData*));
director = calloc(nLines, sizeof(DirData*));
// File Stream for "AllCountries.dat"
if((fd = open("AllCountries.dat", O_RDWR|O_CREAT)) ==-1)
err_sys("File not found...\n");
// File Stream for "RandomStruct.bin"
if ((fd2 = open("RandomStruct.bin",O_RDWR|O_CREAT)) == -1)
err_sys("Failed to open binary\n");
// Filling the Country stucts
fillCountryStructs(countryDataPtr, nLines, fd);
close (fd);
//fclose(Fin); // Closing the file "AllCountries.dat"
// Writing Binary File
write(fd2, (countryDataPtr[0]->name[0]), sizeof(CountryData));
int c=0;
int n=0;
int counter=0;
int tempn=0;
for (c=0;c<nLines;c++)
{
write(fd2, (countryDataPtr[c]->name),50);
write(fd2, (countryDataPtr[c]->code),4);
sprintf(buffer, "%d", countryDataPtr[c]->population); // Function from String.h
n = strlen(buffer);
write(fd2, buffer, n);
if (n<15)
{
for (counter=0;counter<(15-n);counter++)
{
write(fd2," ",1);
}
}
//printf("n: %i n-15: %i\n",n,(15-n));
//sprintf(buffer, "%0.1f", countryDataPtr[c]->lifeExp); // Function from String.h
//n=((int)(countryDataPtr[c]->lifeExp));
tempn=((int)(countryDataPtr[c]->lifeExp));
sprintf(buffer, "%d", tempn); // Function from String.h
n = strlen(buffer);
write(fd2, buffer,n);
write(fd2,"\n",1);
}
close (fd2);
printf("prueba\n");
//fclose(Fout);
printf("RandomStruct.bin written Sucessfully\n");
// Filling the Directory File
// File Stream for "RandomStructDir.dir"
if ((fd2 = open("RandomStructDir.dir",O_RDWR|O_TRUNC)) != -1)
err_sys("Failed to open binary\n");
printf("holla0\n");
fillDirectoryStructs(countryDataPtr, director, nLines, fd2);
printf("holla\n");
sortStructs(director, nLines); // Sorting the structs
printf("holla2\n");
// Write the number of lines in the FIRST LINE
// of the Directory File
write(fd2, &nLines, sizeof nLines);
// Writing Directory File after the number of lines was written
//write(fd2,(director[0]->code[0]), sizeof(DirData));
for (c=0;c<nLines;c++)
{
write(fd2, (director[c]->code),4);
}
close (fd2);
//fclose(Fout);
printf("RandomStructDir.dir written Sucessfully\n\n");
exit(0);
}
// Filling the Country structs
// - This function extracts the data from the file using strtok
// and fills all the structs with their corresponding values.
void fillCountryStructs(CountryData **dataPtr, int nLines, int fd)
{
int curLine = 0; // Current line
int index = 0; // The index
char buf[BUFSIZE]; // The Buffer with the size of BUFSIZE
char *tok; // Token
char *pEnd; // For the String functions
char ch = 'a'; // The temp character
int temPop;
double temLifeExp;
int num=0;
for(curLine = 0; curLine < nLines; curLine++)
{
// Reading each line
dataPtr[curLine] = (CountryData *)calloc(1, sizeof(CountryData));
index = 0;
do
{
read(fd, &ch, 1);
buf[index++] = ch;
}
while(ch != '\n');
// Strtoking...
tok = strtok(buf, ",\n");
index = 1;
while(tok != NULL)
{
tok = strtok(NULL, ",\n");
// Get the Country Code
if(index == 1)
{
strcpy(dataPtr[curLine]->code, tok); // Copying code to the struct
//write(fd2, (tok), sizeof(CountryData[0]));
}
// Get the Country Name
if(index == 2)
{
strcpy(dataPtr[curLine]->name, tok); // Copying name to the struct
//write(fd2, (tok), sizeof(CountryData));
}
// Get the Country Population
if(index == 7)
{
temPop = (int)strtol(tok, &pEnd, 10);
dataPtr[curLine]->population = temPop; // Copying population to the struct
}
// Get the Country Life expectancy
if(index == 8)
{
num=countchar(tok);
printf ("The number of characters entered is %d\n", num);
printf ("The character entered is %s\n",tok);
temLifeExp = strtod(tok, &pEnd);
dataPtr[curLine]->lifeExp = temLifeExp; // Copying life expectancy to the struct
}
index++;
}
}
}
int countchar (char list[])
{
int i, count = 0;
for (i = 0; list[i] != '\0'; i++)
count++;
return (count);
}
// Filling the Directory Structs
// - This function fills the directory with the offset
void fillDirectoryStructs(CountryData **dataPtr, DirData **director, int nLines, int fd2)
{
int i = 0;
for(i = 0; i < nLines; i++)
{
strcpy(director[i]->code, dataPtr[i]->code); //It crashes in this Line
director[i]->offSet = 72 * (i);
}
}
// Sorting the Dir Structs
// - This function sorts the Directory Structs.
void sortStructs(DirData **director, int nLines)
{
int maxNumber;
int i;
DirData **temp;
temp = calloc(1, sizeof(DirData*));
// Sorting the array of pointers!
for(maxNumber = nLines - 1; maxNumber > 0; maxNumber--)
{
for(i = 0; i < maxNumber; i++)
{
if((verifyString(director[i]->code, director[i+1]->code)) == 1)
{
temp[0] = director[i];
director[i] = director[i+1];
director[i+1] = temp[0];
}
}
}
}
// Veryfying the strings
// - This function compares two strings and return a specific value
// accordingly.
int verifyString(char *s1, char *s2)
{
int i;
if(strcmp(s1,s2) == 0)
return(0); // They are equal
for(i = 0; s1[i] != 0; i++)
{
if(s1[i] > s2[i])
return(1); // s1 is greater
else if(s1[i] < s2[i])
return(2); // s2 is greater
}
return (2); // s2 is greater
}
So this is my code for some reason, when I call this method it crashes:
void fillDirectoryStructs(CountryData **dataPtr, DirData **director, int nLines, int fd2) ...
The exact line where it crashes:
strcpy(director[i]->code, dataPtr[i]->code); <-here
It must be something about pointers or I don't know, I'm trying to copy what is on the right side to the struct in the left side. I print dataPtr[0]->code and it shows the right value.
strcpy(director[i]->code, dataPtr[i]->code); //It crashes in this Line
At this point, director[i] doesn't point to anything. You need a director[i] = calloc(... line just like in fillCountryStructs.
Related
I am trying to read from file hw4.data and see if it has a name. The user inputs the name via a command line argument. Everything works fine but I can't get the file to be passed between the functions correctly. The assignment requires that I define the file in main and pass it between SCAN and LOAD.
#include <stdio.h>
#include <stdlib.h>
struct _data {
char name[20];
long number;
};
int SCAN(FILE *(*stream)) { // skim through the file and find how many entries there are
int size = 0;
char s_temp[100];
long l_temp;
while (1) {
fscanf(*stream, "%s %ld", s_temp, &l_temp);
if (feof(*stream)) break;
size++;
}
return size;
}
struct _data* LOAD(FILE *stream, int size) { // loop through the file and load the entries into the main data array
struct _data* d = malloc(size * sizeof(struct _data));
int i;
for (i = 0; i < size; i++) {
fscanf(stream, "%s %ld", d[i].name, &d[i].number);
}
return d;
}
void SEARCH(struct _data *BlackBox, char* name, int size) { // loop through the array and search for the right name
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\nThe name was found at the %d entry.\n*******************************************\n", i);
found = 1;
break;
}
}
if (found == 0) {
printf("*******************************************\nThe name was NOT found.\n*******************************************\n");
}
}
void FREE(struct _data* BlackBox, int size) { // free up the dynamic array
free(BlackBox);
}
int main(int argv, char* argc[]) {
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE* file = fopen("./hw4.data", "r");
int size = SCAN(&file);
struct _data* data = LOAD(&file, size);
SEARCH(data, argc[1], size);
fclose(file);
return 0;
} else {
printf("*******************************************\n* You must include a name to search for.*\n*******************************************\n");
return 0;
}
}
Here's the format of hw4.data
ron 7774013
jon 7774014
tom 7774015
won 7774016
A few issues:
In SCAN, remove the feof. Replace with: if (fscanf(*stream, "%s %ld", s_temp, &l_temp) != 2) break;
Note that after calling SCAN, you should do: rewind(file);. Otherwise, LOAD will only see [immediate] EOF.
And, as others have mentioned, just pass file to SCAN/LOAD and not &file.
Add a check for null return from fopen (e.g.) if (file == NULL) { perror("fopen"); exit(1); }
Stylistically:
If you have a comment describing a function, put it on the line above the function.
Try to keep lines within 80 chars
Here is the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _data {
char name[20];
long number;
};
// skim through the file and find how many entries there are
int
SCAN(FILE *stream)
{
int size = 0;
char s_temp[100];
long l_temp;
while (1) {
if (fscanf(stream, "%s %ld", s_temp, &l_temp) != 2)
break;
size++;
}
return size;
}
// loop through the file and load the entries into the main data array
struct _data *
LOAD(FILE *stream, int size)
{
struct _data *d = malloc(size * sizeof(struct _data));
int i;
for (i = 0; i < size; i++) {
fscanf(stream, "%s %ld", d[i].name, &d[i].number);
}
return d;
}
// loop through the array and search for the right name
void
SEARCH(struct _data *BlackBox, char *name, int size)
{
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\n");
printf("The name was found at the %d entry.\n", i);
printf("*******************************************\n");
found = 1;
break;
}
}
if (found == 0)
printf("*******************************************\n"
"The name was NOT found.\n"
"*******************************************\n");
}
// free up the dynamic array
void
FREE(struct _data *BlackBox, int size)
{
free(BlackBox);
}
int
main(int argv, char *argc[])
{
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE *file = fopen("./hw4.data", "r");
if (file == NULL) {
perror("fopen");
exit(1);
}
int size = SCAN(file);
rewind(file);
struct _data *data = LOAD(file, size);
SEARCH(data, argc[1], size);
fclose(file);
}
else
printf("*******************************************\n"
"* You must include a name to search for.*\n"
"*******************************************\n");
return 0;
}
Using realloc, we can combine SCAN and LOAD into a single function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _data {
char name[20];
long number;
};
// loop through the file and load the entries into the main data array
struct _data *
LOAD(FILE *stream, int *sizep)
{
struct _data *all = NULL;
struct _data *d;
int size = 0;
int capacity = 0;
while (1) {
if (size >= capacity) {
capacity += 10;
all = realloc(all,sizeof(*all) * capacity);
if (all == NULL) {
perror("realloc");
exit(1);
}
}
d = &all[size++];
if (fscanf(stream, "%s %ld", d->name, &d->number) != 2)
break;
}
// trim to size actually used
all = realloc(all,sizeof(*all) * size);
*sizep = size;
return all;
}
// loop through the array and search for the right name
void
SEARCH(struct _data *BlackBox, char *name, int size)
{
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\n");
printf("The name was found at the %d entry.\n", i);
printf("*******************************************\n");
found = 1;
break;
}
}
if (found == 0)
printf("*******************************************\n"
"The name was NOT found.\n"
"*******************************************\n");
}
// free up the dynamic array
void
FREE(struct _data *BlackBox, int size)
{
free(BlackBox);
}
int
main(int argv, char *argc[])
{
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE *file = fopen("./hw4.data", "r");
if (file == NULL) {
perror("fopen");
exit(1);
}
int size;
struct _data *data = LOAD(file, &size);
SEARCH(data, argc[1], size);
fclose(file);
}
else
printf("*******************************************\n"
"* You must include a name to search for.*\n"
"*******************************************\n");
return 0;
}
I had to use rewind() in order to reset the file so that LOAD() would read from the start of the file and give good data.
I'm building a polling command for my Twitch Chatbot, but i'm having problems
in handling the votes. I need to count the occurrences of a vote in a text file.
For now, i did this, but it doesn't seem to work properly:
struct VoteData GetMostVote(FILE * fp)
{
char * buffer = (char *)malloc(sizeof(char)*MAX_BUFFER);
int lines = GetLines(fp);
struct VoteData data[lines];
int i = 0;
while(fgets(buffer, MAX_BUFFER, fp) != NULL)
{
if(strcmp(data[i].word, buffer) == 0)
{
data[i].freq += 1;
}
else
{
strcpy(data[i].word, buffer);
}
i++;
}
int c = 0, index = 0;
for(int j = 0; j < sizeof(data)/sizeof(struct VoteData); j++)
{
if(data[j].freq > c)
{
index = j;
c = data[j].freq;
}
}
free(buffer);
return data[index];
}
where the structure VoteData has this form:
struct VoteData
{
char word[128];
int freq;
};
The function GetMostVote() should return a structure VoteData containing the word with more occurrences in the text file and the frequency for that word.
But my chatbot replies with a string which i'm using in another function...and that is strange.
EDIT1:
I suppose is necessary to also post the function where GetMostVote() gets called:
void poll_handler(int sock, int * status, int * vote_count)
{
FILE * fp;
int res;
char * string = (char *)malloc(sizeof(char)*MAX_BUFFER);
struct VoteData vote;
sleep(300);
*status = 0;
*vote_count = 0;
if(!(fp = fopen("polls/votes.txt", "r")))
{
fprintf(stderr, "\nError in reading file\n");
exit(EXIT_FAILURE);
}
vote = GetMostVote(fp);
strcpy(string, "PRIVMSG #st3ver0nix : Polling terminated, the majority voted: ");
strcat(string, vote.word);
strcat(string, "\r\n");
do{
res = write(sock, string, strlen(string));
}while(res < strlen(string));
fclose(fp);
free(string);
}
the parameters are: the socket of the irc channel, the status and the vote_count pointers to int which are used for handling the votes.
EDIT2:
I'm posting also the function where poll_handler() gets called:
void CreatePoll(int sock, char * message, char * poll_name, int * status, int * vote_count)
{
pid_t pid;
char * name = (char *)malloc(sizeof(char)*MAX_BUFFER);
GetPollName(message, name);
strcpy(poll_name, name);
if((pid = fork()) == -1)
{
fprintf(stderr, "\nError in fork\n");
exit(EXIT_FAILURE);
}
if(pid == 0)
{
poll_handler(sock, status, vote_count);
}
free(name);
}
return data[index] attempts to return a local variable.
That is no problem at all, since the value of data[index] is returned, not its address.
The main error in GetMostVote() is that in the while loop the vote line just read is compared to the uninitialized data[i].word. Correct is to compare the current vote to the previously stored vote data until there are no more or the current vote is found among them:
while (fgets(buffer, MAX_BUFFER, fp) != NULL)
for (int j = 0; ; ++j)
if (j == i)
{ // store new word
data[i].freq = 1;
strcpy(data[i++].word, buffer);
break;
}
else
if (strcmp(data[j].word, buffer) == 0)
{ // count known word
data[j].freq += 1;
break;
}
A second error is in the for(int j = 0; j < sizeof(data)/sizeof(struct VoteData); j++): Rather than going through all allocated vote data, of which the posterior may well be uninitialized, only the used data shall be regarded:
for (int j = 0; j < i; j++)
My code below is taking a text file and hashing it, keys will be English words, values will be the Spanish translation.
My code crashes when it goes into the function insertHash, specifically the while loop below
while(hashTable[hashIndex].marker == 1)
{
printf("%d loop of hash index \n", i);
hashIndex = (hashIndex+1)%tableSize;
i++;
}
Please ignore the i++ and printf, those are for debugging purposes.
The function itself does not even go through one iteration, just crashes.
Any help is appreciated, code is below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int numofEle = 0;
const int STEPSIZE = 100;
//Hash table structure
struct node *hashTable;
struct node{
int marker;
char *key;
char *value;
};
//function declaration
char **loadfile(char *filename, int *len);
//Hash function for strings;
unsigned long hash(unsigned char *str)
{
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
//Function to insert elements in hash table
void insertHash(int len, char *words){
printf("Just entered insert hash function \n");
int hashKey, hashIndex;
int tableSize = len;
printf("before hashing\n");
hashKey = hash(words);
printf("after hashing\n");
hashIndex = hashKey % tableSize;
printf("After hash index \n");
if(numofEle == tableSize)
{
printf("The hash table is full\n.");
return;
}
printf("before checking for marker\n");
while(hashTable[hashIndex].marker == 1)
{
hashIndex = (hashIndex+1)%tableSize;
}
printf("After checking index \n");
strcpy(hashTable[hashIndex].key, strtok(words, "\t"));
strcpy(hashTable[hashIndex].value, strtok(NULL, "\t"));
hashTable[hashIndex].marker = 1;
printf("After assigning variables to table \n");
numofEle++;
return;
}
//Function to search for element
void searchElement(char *key, int len, int *totalProbe)
{
int count = 0;
int probe = 0;
int flag = 0;
char search;
int hashKey = hash(key);
int tableSize = len;
int hashIndex = hashKey % tableSize;
if(numofEle == 0)
{
printf("Hash Table is empty\n.");
return;
}
printf("Probes| keys\n");
printf(" %d| 0\n", probe);
while(hashTable[hashIndex].marker != 0 && count <= tableSize)
{
probe++;
if(hashTable[hashIndex].key == key)
{
printf("The word %s ", key);
printf("translate to %s.\n", hashTable[hashIndex].value);
flag = 1;
break;
}
else
{
printf(" %d| %d\n", probe, hashIndex);
}
hashIndex = (hashIndex+1) % tableSize;
}
if(!flag)
{
printf("Given data is not here\n.");
}
*totalProbe = probe;
return;
}
int main(int argc, char *argv[])
{
//Check for readable input
if(argc == 1)
{
fprintf(stderr, "Must supply a file name to read.\n");
exit(1);
}
int i, totalProbe;
int len = 0;
//Function to read text into array
char **words = loadfile(argv[1], &len);
printf("After loading files\n ");
//allocating memory for hash table and elements
struct node *hashTable = malloc(sizeof(struct node)* len);
printf("After struct allocation\n");
for(i = 0; i < len; i++)
{
hashTable[i].key = malloc(300 * sizeof(char));
hashTable[i].value = malloc(300 * sizeof(char));
hashTable[i].marker =(int*)malloc(sizeof(int));
}
for(i = 0; i < len; i++)
{
hashTable[i].marker = 0;
}
printf("After allocation for table elements\n");
//Loop to hash and insert elements
int totalHash = len;
for(i = 0; i < len; i++)
{
insertHash(len, words[i]);
totalHash--;
}
printf("After hashing words");
char input[100];
printf("Hash Table:\n");
printf("total NOT hashed: %d out of %d\n", totalHash, len);
printf("Please enter key: \n");
scanf("%s", input);
searchElement(input, len, &totalProbe);
printf("max_run of probes = %d\n", totalProbe);
return (EXIT_SUCCESS);
}
//Reads large files into array
char **loadfile(char *filename, int *len)
{
FILE *fp = fopen(filename, "r");
if(!fp)
{
fprintf(stderr, "Can't open %s for reading.\n", filename);
return NULL;
}
int arrlen = STEPSIZE;
//Allocate space for 100 char pointers.
char **lines = (char**)malloc(arrlen*sizeof(char*));
char buff[10000];
int i = 0;
while(fgets(buff, 10000, fp))
{
//Check for full array, if so, extend the array
if(i == arrlen)
{
arrlen += STEPSIZE;
char **newlines = realloc(lines, arrlen*sizeof(char*));
if(!newlines)
{
fprintf(stderr, "Can't reallocate\n");
exit(1);
}
lines = newlines;
}
// trim off \0 at the end
buff[strlen(buff)-1] = '\0';
//get length of buffer
int blen = strlen(buff);
char *str = (char*)malloc((blen+1)*sizeof(char));
//copy string from buff to structure
strcpy(str, buff);
lines[i] = str;
i++;
}
*len = i; //Set length of char pointers
return lines;
}
Two things :
1.You need to declare struct node *hashTable below the structure definition,
//Hash table structure
//struct node *hashTable; <--- incorrect
struct node{
int marker;
char *key;
char *value;
};
struct node *hashTable;
2.You are re-declaring struct node *hashTable in main().
struct node *hashTable = malloc(sizeof(struct node)* len);
The scope of this pointer will be local.
You have already declared struct node *hashTable globally.
Use :
hashTable = (struct node *)malloc(sizeof(struct node)* len);
3.why are you dynamically allocating memory for marker? It is an int member of struct node. It has sizeof(int) bytes of memory.
hashTable[i].marker =(int*)malloc(sizeof(int)); ---> Not needed. You can comment it.
I am novice to C programming and I have written a code to a requirement specification but I am consistently getting Segmentation Fault and unable to proceed ahead.
If the file name is 'code.c' and it runs with an error of not passing the argument (filename). But if the filename is passed, we land in Segmentation Fault.
Any help/suggestions will be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
int ch = 0, lines = 0;
while (!feof(*stream))
{
ch = fgetc(*stream);
if (ch == '\n')
{
lines++;
}
}
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int size)
{
int i;
size_t chrCount;
char *text, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(size, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
rewind(stream);
for (i = 0; i < size; i++)
{
getline(&text, &chrCount, stream);
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char *name, int size, int inputs)
{
int i;
int found = 0;
char *search = " ";
char *firstName;
char *lastName;
if (inputs == 2)
{
firstName = strtok(name, search);
lastName = strtok(NULL, search);
}
printf("*******************************************\n");
if (inputs == 2)
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) && !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
else
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) || !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
if (found == 0)
{
printf("The name was NOT found.\n");
}
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int size)
{
int i;
for (i = 0; i < size; i++)
{
free(BlackBox[i].firstName);
free(BlackBox[i].lastName);
}
free(BlackBox);
BlackBox = NULL;
}
// MAIN
int main(int argv, char **argc)
{
int size;
FILE *stream;
struct _data *BlackBox;
// argv == 1 WORKS, Below message is printed.
if (argv == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argv == 2 DOES NOT WORK, Segmentation Fault.
if (argv == 2)
{
size = SCAN (&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[1], size, 1);
}
if (argv == 3)
{
size = SCAN(&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[2], size, 2);
}
return 0;
}
You have a problem in this code:
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
...
BlackBox[i].number = atol(number);
The second strtok() call should pass NULL as its first argument. As it is, the third strtok() call is certain to return NULL because the first call modifies text in such a way that the second consumes the whole thing (when tokenizing again from the beginning, as it erroneously does). You do not test for that, however, and as a result, atol() attempts to dereference a null pointer.
Update:
Additionally, as #chqrlie and later #JamesWilkins observed, you do not allocate sufficient space for BlackBox[i].firstName and BlackBox[i].lastName, as you need room for the string terminators as well. This is an entirely separate problem that could also produce a segfault. I like #chqrlie's suggestion to switch to strdup(), but it would be sufficient to just increase each allocation by one unit.
Update 2:
Furthermore, you have an issue with this line:
getline(&text, &chrCount, stream);
You do not initialize variable text before the first call, so it contains a junk value. The function allocates a buffer only when its first argument points to a NULL pointer; otherwise it writes the line to the buffer pointed to by the pointer obtained by dereferencing the first argument. Writing to a random location in memory certainly produces undefined behavior, which in practice often manifests as a segfault.
Moreover, unless you can rely on no line of the file being longer than the first, you also need to free the text pointer at the end of each loop iteration AND reset its value to NULL, so that getline() allocates a fresh buffer on the next iteration. If you do not free it on each iteration, then you need instead to free it after the end of the loop; else you will leak memory.
Try this (though I'm using Visual Studio on Windows). I added code to check for a missing '\n' on the last line, and also allowed for a variable number of search terms. I also increased the memory allocation for strings by 1 to account for the null terminating character. I noticed you are using getline(const char*..., which I think is GNU (Linux?), so I change that to fgets() just so I could compile and test it in VS (so you can change it back if you like). I put in various null checks as well, to be safer.
#include <iostream>
using namespace std;
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
if (*stream == NULL)
{
perror("Error opening file");
return 0;
}
char ch = 0, lines = 0, linesize = 0;
while ((ch = fgetc(*stream)) != EOF)
{
if (ch == '\n')
{
lines++;
linesize = 0;
}
else linesize++;
}
if (linesize > 0)
lines++; // (last line doesn't have '\n')
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int lineCount)
{
int i;
size_t chrCount = 256;
char text[256], *result, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(lineCount, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
else memset(BlackBox, 0, sizeof(struct _data) * lineCount); // (make sure all data members are null to begin)
rewind(stream);
for (i = 0; i < lineCount; i++)
{
result = fgets(text, chrCount, stream);
if (result == NULL)
break; // (EOF)
firstName = strtok(text, " ");
lastName = strtok(NULL, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char **names, int lineCount, int inputs)
{
int i, l;
int found = 0;
printf("*******************************************\n");
for (i = 0; i < inputs; ++i)
{
for (l = 0; l < lineCount; ++l)
{
if (BlackBox[l].firstName != NULL && !_stricmp(names[i], BlackBox[l].firstName)
|| BlackBox[l].lastName != NULL && !_stricmp(names[i], BlackBox[l].lastName))
{
printf("The name was found on line %d.\n", 1 + l);
found = 1;
break;
}
}
if (found) break;
}
if (!found)
printf("The name was NOT found.\n");
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int lineCount)
{
int i;
for (i = 0; i < lineCount; i++)
{
if (BlackBox[i].firstName != NULL)
free(BlackBox[i].firstName);
if (BlackBox[i].lastName != NULL)
free(BlackBox[i].lastName);
}
free(BlackBox);
}
// MAIN
int main(int argc, char **argv)
{
int lineCount;
FILE *stream;
struct _data *BlackBox;
// argc == 1 WORKS, Below message is printed.
if (argc == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argc == 2 DOES NOT WORK, Segmentation Fault.
if (argc > 1)
{
lineCount = SCAN(&stream);
if (lineCount > 0)
{
BlackBox = LOAD(stream, lineCount);
SEARCH(BlackBox, argv + 1, lineCount, argc - 1);
FREE(BlackBox, lineCount);
}
}
return 0;
}
Tested it on the command line, and it works.
The problem is the argv and argc. argc is supposed to be an int (think argument count), while argv is meant to be char**. You have them mixed up in your main.
I have a program that reads the words of two files (the first a wordlist, and the second an ebook from the Gutenberg project ) into two char *arrays.
I am trying to add all the unique words from the second char *array that don't appear in
the first char *array into a third char *array then print them.
This program adds the correct words, but is adding them more than once.
The error occurs in findOdds().
Note when I use a non-binary search method this program works correctly, but takes a long time.
What is the problem with my program? I apologize for my English.
#include <stdio.h>
#include <stdlib.h> /* for malloc() */
#include <ctype.h>
#include <string.h>
#define MAXCHAR 24
#define MAXLINES 150000
int add2array(FILE *fp, char *lineptr[]);
int findOdds(char *lineptr[], char *lineptr1[], int nlines, int nlines1);
int binsearch1(char *val, char *lineptr[], int nlines);
char *lineptr2[MAXLINES]; /* The unique words not in the word list */
int main(int argc, char *argv[])
{
FILE *my_stream, *my_stream1;
char *lineptr[MAXLINES], *lineptr1[MAXLINES];
int i, nlines, nlines1, nlines2;
/* Load the wordlist. */
my_stream = fopen("words.txt","r");
if(my_stream == NULL) {
printf("error: Couldn't open file\n");
return 2;
} else {
nlines = add2array(my_stream, lineptr);
fclose(my_stream);
}
if(nlines==-1) {
printf("error: Epic Failure to copy words to char *lineptr[]\n");
return -1;
}
/* Load the ebook. */
my_stream1 = fopen("horsemanship.txt","r");
if(my_stream1 == NULL) {
printf("error: Couldn't open file\n");
return 2;
} else {
nlines1 = add2array(my_stream1, lineptr1);
fclose(my_stream1);
}
if(nlines1==-1) {
printf("error: Epic Failure to copy words to char *lineptr[]\n");
return -1;
}
/* Find and print the unique words from the ebook not in the wordlist */
nlines2 = findOdds(lineptr, lineptr1, nlines, nlines1);
for(i=0; i<nlines2; i++)
printf("%s\n",lineptr2[i]);
return 0;
}
/* add2array: read the words from the file into char *lineptr[] */
int add2array(FILE *fp, char *lineptr[])
{
int nlines=0, c=0, pos=0;
char temp[MAXCHAR];
char *p;
while((c = getc(fp)) != EOF) {
if(isalpha(c))
temp[pos++] = tolower(c);
else if(!isalpha(c)) {
temp[pos] = '\0';
pos = 0;
if(isalpha(temp[0])){
if((p = malloc(sizeof(temp)))==NULL)
return -1;
strcpy(p, temp);
lineptr[nlines++] = p;
}
}
}
return nlines;
}
/* Add the unique words from lineptr1 not in lineptr to lineptr2 */
int findOdds(char *lineptr[], char *lineptr1[], int nlines, int nlines1)
{
char *p;
char temp[MAXCHAR];
int i, nlines2=0;
for(i=0; i<nlines1; i++) {
if(binsearch1(lineptr1[i], lineptr, nlines)==-1) {
if(binsearch1(lineptr1[i], lineptr2, nlines2)==-1) {
if((p = malloc(sizeof(temp)))==NULL)
return -1;
strcpy(p, lineptr1[i]);
lineptr2[nlines2++] = p;
}
}
}
return nlines2;
}
int binsearch1(char *val, char *lineptr[], int nlines)
{
int pos;
int start = 0;
int end = nlines-1;
int cond = 0;
while(start <= end){
pos=(start + end)/2;
if((cond = strcmp(lineptr[pos],val)) == 0)
return pos;
else if(cond < 0)
start = pos+1;
else
end = pos-1;
}
return -1;
}
Arrays must be sorted if you want to use binary search, as stated above by n.m.
in main() ...
shellsort1(lineptr1, nlines1);
/* Find and print the unique words from the ebook not in the wordlist */
nlines2 = findOdds(lineptr, lineptr1, nlines, nlines1);
...
int shellsort1(char *v[], int n)
{
int gap, i, j;
char temp[MAXCHAR];
char *p;
for(gap=n/2; gap>0; gap/=2)
for(i=gap; i<n; i++)
for(j=i-gap; j>=0 && strcmp(v[j],v[j+gap])>0; j-=gap) {
if((p = malloc(sizeof(temp)))==NULL)
return -1;
p = v[j];
v[j] = v[j+gap];
v[j+gap] = p;
}
return 0;
}