-2

So, I've already search this site to see if there was any question similar to mine and I came up empty-handed. Here's the setup. I used my program to write to a file and I stored the contents within a record (using arrays). The problem is that I can't seem to find a way to search the array of records for a specific code and manipulate whatever that is linked to that code. Here is a snippet of my program:

  • Case 4 is where I print to the Stock.txt file and read the contents into variables.
  • Case 5 is where I try to search the array using typedef for a code and printing what relates to the code.

I would like to know how to properly execute case 5.

case 4: ;
    FILE *kfile ;
    //opens the file called kfile found from the path below.
    kfile= fopen("Stock.txt","w");

    //prompts the programmer to enter a command to end the input of records.
    printf("CTRL+Z' to end input\n");
    printf("===========================================");
    printf("\nUSE A SINGLE APACE BETWEEN EACH WORD/NUMBER");
    printf("\n===========================================\n\n");
    //while it is not the end of file, programmer still enters records of persons.

    printf("Enter Code(MAX 3 letters), Item Name, Quantity, Price: \n");
    scanf("%s %s %d %d", filecode, itemname, &quantity, &fileprice);
    while (!feof(stdin) ){
        fprintf(kfile,"%s\t%s  \t%d\t%d\n", filecode, itemname, quantity, fileprice);
        //prints to the file 
        printf("Enter Code(MAX 3 letters), Item Name, Quantity, Price: \n");
        scanf("%s %s %d %d", filecode, itemname, &quantity, &fileprice);
    }   
    fclose(kfile);              
    break;

case 5: ;   
    FILE *mfile;
    if ((mfile = fopen("Stock.txt","r")) == NULL) {
        perror("Error while opening file");
    }else {
       while (!feof(mfile)) {
          fscanf(mfile, "%s\t%s  \t%d\t%d", filecode, itemname,  &quantity, &fileprice);
          array[x].itemname1 = strdup(itemname);
          array[x].code1 = strdup(filecode);
          array[x].quantity1 = quantity;
          array[x].price1 = fileprice;
       }
       fclose(mfile);
    }

    printf("Please enter Code: ");
    scanf("%s", &codenew);
    printf("\n");
    printf("\nCode\tItem Name\tQuantity\tPrice\n");
    for (x = 1; x <= 100; x++) {
         if (strcmp(array[x].code1, codenew)==0) {
           // print the record
          printf("%s\t%s  \t%d\t\t%d\n", array[x].code1,array[x].itemname1, array[x].quantity1,array[x].price1);

        }
    }
    break;
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Michael
  • 1
  • 1
  • please, format your code. – yulian Feb 28 '15 at 01:48
  • 3
    Welcome to Stack Overflow. Please read the [About] page soon. Have you learned how to write functions other than `main()` yet? If so, your code should be using a function for the body of each of the cases. If not, you've some sort of excuse for not writing a function for each of the cases, but you really need to learn about how to write functions so you can break up monolithic hunks of code into more nearly manageable pieces. You should note that [`while (!feof(file))` is always wrong](http://stackoverflow.com/questions/5431941/). You should explicitly test the return value from `fscanf()`. – Jonathan Leffler Feb 28 '15 at 01:59
  • @JonathanLeffler I am familiar with writing functions but i can't figure out to use them in my program. Could you please show me an example? – Michael Feb 28 '15 at 02:06
  • 1
    You forgot to initialize, then increment `x` in your read loop. Incidentally, the line `for (x = 1; x <= 100; x++)` suggests you may not be aware that in C indexing an array of capacity *n* starts at `0` and ends at `n-1`. – Jongware Feb 28 '15 at 02:23

1 Answers1

1

Note that the empty statement after the case label is aconventional and unnecessary (but not technically wrong, except in that it suggests someone who is not comfortable with C as yet). You can write the code for case 5 so that it simply calls a function.

case 5:
    read_and_match("Stock.txt");
    break;

though it would be better if the file name were itself in a variable. I'm assuming that the data is stored in a structure type such as:

struct Item
{
    char *itemname1;
    char *code1;
    int   quantity1;
    int   price1;
};

The function itself looks like:

void read_and_match(const char *file)
{
    FILE *mfile;
    if ((mfile = fopen(file, "r")) == NULL)
    {
        fprintf(stderr, "Error while opening file %s (%d: %s)\n",
                file, errno, strerror(errno));
        return;
    }

    char filecode[40];
    char itemname[40];
    int quantity;
    int fileprice;
    int num_items;
    struct Item array[100];
    for (num_items = 0; num_items < 100; num_items++)
    {
        if (fscanf(mfile, "%39s %39s %d %d",
                   filecode, itemname, &quantity, &fileprice) != 4)
            break;
        array[num_items].itemname1 = strdup(itemname);
        array[num_items].code1 = strdup(filecode);
        array[num_items].quantity1 = quantity;
        array[num_items].price1 = fileprice;
    }
    fclose(mfile);

    printf("Please enter Code: ");
    char codenew[40];
    if (scanf("%39s", codenew) != 1)
        return;
    printf("\n");
    printf("\nCode\tItem Name\tQuantity\tPrice\n");
    for (int x = 0; x < num_items; x++)
    {
         if (strcmp(array[x].code1, codenew) == 0)
         {
             // print the record
             printf("%s\t%s\t%d\t\t%d\n",
                    array[x].code1, array[x].itemname1, array[x].quantity1, array[x].price1);
        }
    }
}

Frankly, though, there are two functions in here — a read function and a search function — in which case, you end up with:

case 5:
    {
    struct Item array[100];
    int n_items = read_items("Stock.txt", array, 100);
    if (n_items > 0)
        search_list(array, n_items);
    }
    break;

And the functions become:

int read_items(const char *file, struct Item *array, int max_items)
{
    FILE *mfile;
    if ((mfile = fopen(file, "r")) == NULL)
    {
        fprintf(stderr, "Error while opening file %s (%d: %s)\n",
                file, errno, strerror(errno));
        return 0;
    }

    char filecode[40];
    char itemname[40];
    int quantity;
    int fileprice;
    int num_items;
    for (num_items = 0; num_items < max_items; num_items++)
    {
        if (fscanf(mfile, "%39s %39s %d %d",
                   filecode, itemname, &quantity, &fileprice) != 4)
            break;
        array[num_items].itemname1 = strdup(itemname);
        array[num_items].code1 = strdup(filecode);
        array[num_items].quantity1 = quantity;
        array[num_items].price1 = fileprice;
    }
    fclose(mfile);
    return num_items;
}

void search_list(struct Item *array, int num_items)
{
    printf("Please enter Code: ");
    char codenew[40];
    if (scanf("%39s", codenew) != 1)
        return;
    printf("\n");
    printf("\nCode\tItem Name\tQuantity\tPrice\n");
    for (int x = 0; x < num_items; x++)
    {
         if (strcmp(array[x].code1, codenew) == 0)
         {
             // print the record
             printf("%s\t%s\t%d\t\t%d\n",
                    array[x].code1, array[x].itemname1, array[x].quantity1, array[x].price1);
        }
    }
}

The tabular formatting needs fixing up too, but that's an exercise for someone else.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Note that the 'big function' version does not ensure that there are any items to search through before inviting the user to search (but the 'two function' version does). I'd not make any claims that the functions are perfect. Indeed, the input operation and the search operation in `search_list()` should probably be in separate functions — separating I/O from other code is often a good way to organize the code. – Jonathan Leffler Feb 28 '15 at 16:39
  • Thanks so much, What does read_items() and search_items() do? – Michael Feb 28 '15 at 17:40
  • Oh its the name of the function, when i compile it, i get the error: search_list and read_items not declared in this scope. – Michael Feb 28 '15 at 17:47
  • It is good that you have your compiler settings such that you get those warnings. You need to have a declaration of the functions visible before the calls to the functions, or you need to define the functions before the code that calls them. The declarations could be a copy of the first line with a semicolon afterwards. Personally, I usually define functions as `static` and place the definition before the use, but there are cogent reasons for declaring them (as `static` functions) before they're used and defining them afterwards. – Jonathan Leffler Feb 28 '15 at 18:27
  • Can I send you a personal email so that you can view my complete code? I'm still having problems. – Michael Feb 28 '15 at 23:51
  • So I completed my code, now, i want to implement error checks to ensure that the user enters only the correct type of data. I want ensure that only a string of 2 characters is entered for one part, ensure that only a lowercase alphabet is entered, ensure that only an integer is entered etc. What do you suggest I do? – Michael Mar 01 '15 at 23:17
  • For strings of not more than 2 characters, you have two options. (1) Allow them to enter more but complain when they do, or (2) only read 2 characters, but then complain when what follows isn't a valid number (because they actually entered more than two characters). It's worth thinking about telling people how many characters to enter in the prompt. – Jonathan Leffler Mar 02 '15 at 00:22
  • How can I ensure that only an integer is entered? I want to know how to handle the program when a character is entered. – Michael Mar 05 '15 at 16:32
  • You can't stop people hitting the wrong keys on the keyboard. What you have to do is detect that they've done so, and handle the erroneous input appropriately. What that means depends on the context you're working in; what you do in a GUI program is different from what you do in a command-line program. In a command-line program, you typically ignore the rest of the input on the line, prompt again and get more input from the user, but you should probably only do that a limited number of times in a row (e.g. ten times) and thereafter give up with appropriate messages and the minimum damage. – Jonathan Leffler Mar 05 '15 at 18:01
  • How can i use fgets to get only single digit ? – Michael Mar 06 '15 at 02:31
  • For most purposes, you can't. If you really think you must get a single character, use `getc()` or a relative. If you must use `fgets()`, you're throwing away 99% of the usefulness of the function, but you can use `char gotten[2]; while (fgets(gotten, sizeof(gotten), stdin) != 0) { …process gotten[0] as the input character… }`. The fact that the array is of size two forces `fgets()` to read one character at a time. There's no guarantee that the user cooperated and that the character that was read is a digit. You'll have to check that yourself. – Jonathan Leffler Mar 06 '15 at 02:33
  • Okay, I want to get only 2 characters from the user (I'll do my letter/digit check), Here's what i have `char codenew[3]; fgets(codenew, sizeof(codenew), stdin);` It read more than 2 characters and also the program doesn't continue after that. – Michael Mar 06 '15 at 04:35