Raulito28 Raulito28 - 1 month ago 11
C Question

Program works with string literals but not with string arrays

I have a hashtable ADT which has two functions, insert and lookup. I put in to the insert function a hash table, hash table size, ID #, and book title and that inserts it into the hash table. This works fine when I pass it a string literal, i.e.

insert(...,"Hello, world!"...);
It doesn't work when I read in strings from a file, store them in an array, and try and use my insert and lookup functions.
I have all of my code here but the most important files are main.c and hash.c. Hash.c has the newHash(), hash(), insert(), and lookup() functions and main.c reads from two files, in this case test1.lib.in and test1.req.in, and from the first file will get the library id and title of a book from each line and then put it in the hash table. From the second file, it gets requests for a book title and should print the ids in its linked list.



Example of code that works.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "list.h"
#include "hash.h"

int main(){
ListHndl* temp = newHash(10);
insert(442440, "cvyaqbznxel", 10,temp);
lookup(temp,"cvyaqbznxel", 10);
return 0;
}


Code that doesn't work

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "list.h"
#include "hash.h"


int main(int argc, char * argv[]) {

if (argc != 3) {
printf("Incorrect arguments, please specify 2 files to be read\n");
return EXIT_FAILURE;
}
FILE *file = fopen( argv[1], "r");
FILE *secondFile = fopen(argv[2], "r");
if (file == 0 || secondFile == 0) {
printf("Could not open a file\n");
return EXIT_FAILURE;
}
int numDataLines2;
int numDataLines;
int hashTableSize;
//First line of first file gives number of lines in file and
//size of hash table to be made
if(fscanf(file, "%d%d", &numDataLines, &hashTableSize) < 2) {
printf("Unable to parse first line of first file\n");
return EXIT_FAILURE;
}
ListHndl* theHash = newHash(hashTableSize);
int libraryID;
char *tempString = calloc(numDataLines,41*sizeof(char));
char lineHolder[129];
//discard the new line which always shows up
fgets(lineHolder, 128, file);
for(int i = 0; i < numDataLines; i++) {
//Gets the whole line to be scanned with sscanf
fgets(lineHolder, 128, file);

//If the line consists of just a newline char, continue
if(strcmp(lineHolder, "\n") == 0 ) {
continue;
}
//Scans the line retrieved from fgets and placed in lineHolder
if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID,&tempString[i]) == 0){
printf("Unable to parse line %d of first file\n",i+2);
return EXIT_FAILURE;
}
insert(libraryID, &tempString[i], hashTableSize, theHash);
}
char String[41];
fgets(String, 40, secondFile);
numDataLines2 = atoi(String);
char *storeSecondFileStuff = calloc(numDataLines2,41*sizeof(char));
for(int i = 0; i< numDataLines2; i++) {
fgets(lineHolder, 128, secondFile);
if(strcmp(lineHolder, "\n") == 0) {
continue;
}
if(sscanf(lineHolder, "%40[^\n]",&storeSecondFileStuff[i]) == 0) {
printf("Unable to parse line %d of second file\n",i+2);
return EXIT_FAILURE;
}
lookup(theHash, &storeSecondFileStuff[i], hashTableSize);
}
printf("\n");
fclose(file);
fclose(secondFile);
return 0;
}


Thanks!

Answer

I think you have multiple problems. To start with, you might not be scanning your input line correctly. Change your line

 if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID,&tempString[i]) == 0)

to

 if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID, tempString) < 0)

that way, you will trap the situation where the sscanf function did not successfully convert both arguments - for example, if there is no comma in the input line. Note that sscanf returns the number of successful conversions; success would return a value of 2, so testing for <2 is the right way to go.

Note also that I changed &tempString[i] to tempString. The former points to some place along tempString - which only has 41 characters allocated to it. Yet you always allow up to 40 characters (plus '\0' to be written to it - so you will write past the end of the string. Since this is only a temporary variable, there is no sense in doing this. Just scan the input into the temp variable, then do whatever you need to do with it.

This means that your insert also changes, from

    insert(libraryID, &tempString[i], hashTableSize, theHash);

to

    insert(libraryID, tempString, hashTableSize, theHash);

Again, you need to do the same thing lower down in your code.

Here is an attempt at making the code work for you - see if this hits the spot. Note that all I really did was change the type of tempString and storeSecondFileStuff, and modified the way they were used in various function calls accordingly. I did not attempt to compile / run because of the complexity of the other files involved - but this should help a bit:

int main(int argc, char * argv[]) {

    if (argc != 3) {
        printf("Incorrect arguments, please specify 2 files to be read\n");
        return EXIT_FAILURE;
    }
    FILE *file = fopen( argv[1], "r");
    FILE *secondFile = fopen(argv[2], "r");
    if (file == 0 || secondFile == 0) {
        printf("Could not open a file\n");
        return EXIT_FAILURE;
    }
    int numDataLines2;
    int numDataLines;
    int hashTableSize;
    //First line of first file gives number of lines in file and 
    //size of hash table to be made
    if(fscanf(file, "%d%d", &numDataLines, &hashTableSize) < 2) {
        printf("Unable to parse first line of first file\n");
        return EXIT_FAILURE;
    } 
    ListHndl* theHash = newHash(hashTableSize);
    int libraryID;
    char **tempString = calloc(numDataLines,sizeof(char*));  // <<< ARRAY of pointers
    char lineHolder[129];
    //discard the new line which always shows up
    fgets(lineHolder, 128, file);
    for(int i = 0; i < numDataLines; i++) {
        //Gets the whole line to be scanned with sscanf
        fgets(lineHolder, 128, file);
        tempString[i] = calloc(1, 41 * sizeof(char)); // <<< space for this string
        //If the line consists of just a newline char, continue
        if(strcmp(lineHolder, "\n") == 0 ) {
            continue;
        }
        //Scans the line retrieved from fgets and placed in lineHolder
        if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID, tempString[i]) < 0){ // <<< changed
            printf("Unable to parse line %d of first file\n",i+2);
            return EXIT_FAILURE;
        }
        insert(libraryID, tempString[i], hashTableSize, theHash); // <<< changed
    }
    char String[41];
    fgets(String, 40, secondFile);
    numDataLines2 = atoi(String);
    char **storeSecondFileStuff = calloc(numDataLines2, sizeof(char*)); // changed: again char **
    for(int i = 0; i< numDataLines2; i++) {
        fgets(lineHolder, 128, secondFile);
        storeSecondFileStuff[i] = calloc(1, 41 * sizeof(char));
        if(strcmp(lineHolder, "\n") == 0) {
            continue;
        }
        if(sscanf(lineHolder, "%40[^\n]",storeSecondFileStuff[i]) == 0) {
            printf("Unable to parse line %d of second file\n",i+2);
            return EXIT_FAILURE;
        }
        lookup(theHash, storeSecondFileStuff[i], hashTableSize); // <<<< changed
    }
    printf("\n");
    fclose(file);
    fclose(secondFile);
    return 0;
}
Comments