0

When the user enters the character "Z" into the program, followed by a file name, I want the compiler to understand to open that file.

For example, a user enters: "Z test.txt" I want the compiler to understand to open "test.txt"..

From my understanding of my code below, the Z is ignored, and the string following the "Z" ("%s), is then put into fileName. Which is then used to actually open the file. Any suggestions how I can make this work?

   int main()
   {
      int infiniteLoop = 0;
      char input[MAXC], fileName[MAXC];
      FILE* fp = NULL;
      char buff[255];

        while (infiniteLoop != 1)
        {
          printf("Enter Student's Grade(s) >>> ");
          fgets(input, MAXC, stdin);
          parseUserInput(input);

            if (*input == '\n')
            {
               break;
            }

            if (sscanf(input, "Z %127s", fileName) == 1)
            {
               fp = fopen(fileName, "r");
            }
            if (fp == NULL)
            {
                printf("File I/O Error...\n");
                return -1;
            }

            while (fscanf(fp, "%s", buff) == 1)
            {
                parseUserInput(buff);
            }
            fclose(fp);
        }
    }
}
Joey
  • 41
  • 5
  • You have provided only one format specifier to `sscanf` and two variables to "fill". Also you are giving it `input` as both the input string and one of the outputs. Anyway, since you are already having your input as a string, checking its first character is as easy as `input[0]=='Z'` – Eugene Sh. Jun 04 '21 at 21:23
  • @EugeneSh. As "Z" initiates the file I/O process for my program, does that mean using a `sscanf` will not work here? – Joey Jun 04 '21 at 21:25
  • `sscanf` does not care what your program is doing, it is working according to its specification. – Eugene Sh. Jun 04 '21 at 21:26
  • 1
    I think you want: `sscanf(input, "Z%s", &fileName)` – Craig Estey Jun 04 '21 at 21:27
  • @CraigEstey I tried implementing this, but doesn't seem to work. Would the space between the "Z" and the inputted files name mess anything up? – Joey Jun 04 '21 at 21:31
  • Or you can use `"Z %s"` if more readable. But note: you must also include the *field-width* modifier when using `"%s"` with a value of 1-less than the size of the array to protect your array bounds. So if you have `char name[64];` you must use `"Z %63s"`. Failure to include the field width with `"%s"` makes it no safer than `gets()`. See [Why gets() is so dangerous it should never be used!](https://stackoverflow.com/q/1694036/3422102) – David C. Rankin Jun 04 '21 at 21:32
  • A [MCVE](http://stackoverflow.com/help/mcve) would be nice `:)` – David C. Rankin Jun 04 '21 at 21:39
  • 1
    `fgets(input, sizeof input, stdin);` - stop right there ! :) `sizeof input` is the size of the pointer, not the allocated space. – Eugene Sh. Jun 04 '21 at 21:41
  • @EugeneSh. I've updated the code to show input, am I incorrect for using char* for input? – Joey Jun 04 '21 at 21:41
  • This functionality should go into `parseUserInput(input);` by the way, judging by the name. – Eugene Sh. Jun 04 '21 at 21:42
  • @EugeneSh. So the file I want to open is supposed to go into parseUserInput, which then determines which overloaded function to call, so if the file contained an "A+", that would go into that parsing function would would output a grade like 97.00 – Joey Jun 04 '21 at 21:43
  • @DavidC.Rankin Oh I thought by putting sizeof would make sure I never have any overflow as the size would be determined by the sizeof the input – Joey Jun 04 '21 at 21:46
  • 1
    Since you're using `malloc` for `fileName`, you should use `fileName` as the argument to `scanf` instead of `&fileName`. – William Pursell Jun 04 '21 at 21:47
  • It would be much cleaner to write it as `char fileName[100]; ...; if( sscanf(input, "Z %99s", fileName ))...` – William Pursell Jun 04 '21 at 21:49
  • 1
    IOW, get rid of the `&`. – William Pursell Jun 04 '21 at 21:51
  • @Joey `char *input = (char*)malloc(100);` declares `input` as a pointer to `char` and allocates a block of memory 100-bytes in size assigning the beginning address for that block of memory as the address held (pointed to) by `input`. There is no array involved at all. So when you take `sizeof (input)`. you are taking `sizeof (a_pointer)` which is fixed by your system at either 8-bytes (or 4-bytes) generally. The `sizeof (array)` will return the number of bytes in an array, but a **pointer** is not an **array**, so it does not report the allocated size when provided a pointer. – David C. Rankin Jun 04 '21 at 22:08
  • Oops, there was an extra `&`, so try: `sscanf(input,"Z%s",fileName)`. Or: `if (input[0] == 'Z') { sscanf(&input[1],"%s",fileName); ...` – Craig Estey Jun 04 '21 at 22:26

2 Answers2

0

It is a mistake to write:

  char *fileName = (char*)malloc(100);
  ...
  if (sscanf(input, "Z %s", &fileName ))

With the above, &fileName is the address of the variable in which you have stored a memory location. If you attempt to write data from the input string to that location, you are overwriting the only copy you have of the address...but you are not writing to the allocated location. (In other words, if the input string is Z abc , then the scanf is essentially equivalent to fileName = 0x616263, and you have a memory leak.) If you want to write to the location that you have allocated, use:

sscanf(input, "Z %99s", fileName)
William Pursell
  • 204,365
  • 48
  • 270
  • 300
0

After seeing why it is easy to miss the fact that fileName is already a pointer and there is NO & before it in the sscanf() argument list, it appears you want to do something similar to:

#include <stdio.h>

#define MAXC 128

int main (void) {
    
    char input[MAXC], fileName[MAXC];
    
    for (;;) {
        fputs ("\nEnter Student's Grade(s) >>> ", stdout);
        if (!fgets (input, MAXC, stdin)) {
            puts ("(user canceled input)");
            break;
        }
        if (*input == '\n')
            break;
        
        if (sscanf (input, "Z %127[^\n]", fileName) == 1)
            printf ("filename: %s\n", fileName);
        else
            puts ("not filename input");
    }
}

(by using "%[^\n]" (with field-width) you can handle filenames with spaces included, but note while "Z%s" would work because "%s" discards leading whitespace, "%[..]" does not -- so the spaces before it is mandatory in this circumstance)

Note, there is no need to allocate for input or fileName (though I would suggest a minimum of 1024 for each, the 128 above covers you if you are on an embedded system)

Example Use/Output

To end input, simply press Enter alone on an empty-line:

$ ./bin/zfilename

Enter Student's Grade(s) >>> 10, 30, 90, 20
not filename input

Enter Student's Grade(s) >>> Z george.txt
filename: george.txt

Enter Student's Grade(s) >>> Z file with spaces.txt
filename: file with spaces.txt

Enter Student's Grade(s) >>>

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • First off I appreciate your help. I've updated my code with the knowledge you've brought, but I'm running into a few problems. My code accepts user input, then I parse that user input into the `parseUserInput` function to determine if it's a `float, a char, or an integer array`. Which then calls the correct overloaded function, ultimately displaying a final grade as a floating-point number. In main I have special conditions such as "X" to exit the program, and "Z" to open a file. – Joey Jun 05 '21 at 01:48
  • Let's say I have "A+" on one line (in the text file), I want to read that and send that "A+" to my `parseUserInput` function. I'm confused as I thought my `while(fgets(line, sizeof(line), fp) != NULL)` reads each line, and then using `strcpy` to copy the line being read into temp, to use to send to my `parseUserInput` function. But it seems this does not work. – Joey Jun 05 '21 at 01:48
  • 1
    No problem, Just send `line` to `parseuserInput()` right after the check for empty line (`if (*input == '\n')`.), the `sscanf()` call will only process text with `"Z..."` so you can `parseuserInput()` and then call `sscanf()` without any conflict. Just make sure your `parseuserInput()` doesn't do anything with lines that start with `"Z..."`. – David C. Rankin Jun 05 '21 at 01:59
  • I've updated the code above again, and now it works! The only problem I face now is what you told me to be careful about, I believe the code is doing something with `"Z..."`. Now to my knowledge, I can't simply ignore that input correct? – Joey Jun 05 '21 at 16:37
  • You have an `if {..} if {..} while {..}` ordering problem that should be `if { if { ) while {..} }`. For instance if there is no `"Z filename"` input you still try and `while (fscanf (fp, ...`. So you need to reorder the flow similar to `if (sscanf (intput...) == 1) { fp = fopen(); if (fp == NULL) {..} while (fscanf(..) == 1) {} fpclose() }` Give that a go and I suspect you will see things improve. In other words, it should all be inside the `if (sscanf(input, "Z %127s", fileName) == 1) { ... }` block. Because if the input isn't `"Z filename"`, you don't need to mess with the file. – David C. Rankin Jun 05 '21 at 20:58