First, Never, never, Ever use gets()
, see Why gets() is so dangerous it should never be used!. With that out of the way, you are attempting to divide each character by 3
, not each floating-point value. atoi
is an integer conversion for strings, not individual characters.
But all that aside, your at least provided a good-faith attempt at a solution. So let's look at how you can improve things. First, don't use magic-numbers, 25
in your code is a magic-number, instead if you need an integer constant, #define
one, e.g.
#define _CRT_SECURE_NO_WARNINGS //preprocessor requirement
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FNMAX 512 /* if you need a constant, define one (or more) */
int main (void) {
Also, Don't skimp on buffer size! On Linux the default PATH_MAX
constant is 4096
. 25
doesn't even begin to cover allowable filenames.
Next, replace gets
with fgets
. The only caveat is that you now must trim the trailing '\n'
from the buffer. You can do that simply with strcspn
(which will report the number of characters that do not include those in the reject string). So choosing a reject string of "\r\n"
covers you and strcspn
returns the number of character up to the first of either. You simply nul-terminate your string at that index overwriting the line-ending, e.g.
printf ("Enter filename: ");
if (!fgets (file_name, FNMAX, stdin)) { /* validate EVERY input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
file_name[strcspn(file_name, "\r\n")] = 0; /* trim '\n' from end */
Good job on validating your file was open for reading before using fp
. Now you simply need to continue in a manner that will read floating-point numbers instead of characters. While I would generally recommend reading the remaining lines into a buffer and then calling sscanf
to parse the values from it, you can also just use fscanf
to read the floating-point numbers one-after-the-other. (all scanf
conversions except "%c"
and "%[...]"
discard leading whitespace)
You can very simply use fscanf
to read, and then divide by 3
(which is where I violate the magic-number rule :)
, e.g.
/* read/print each floating-point value and value divided by 3 */
while (fscanf (fp, "%lf", &value) == 1)
printf ("\nvalue: %.4f\ndiv-3: %.4f\n", value, value / 3);
That's it. Putting it altogether, you could do:
#define _CRT_SECURE_NO_WARNINGS //preprocessor requirement
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FNMAX 512 /* if you need a constant, define one (or more) */
int main (void) {
char file_name[FNMAX];
double value;
FILE *fp; //file handle
printf ("Enter filename: ");
if (!fgets (file_name, FNMAX, stdin)) { /* validate EVERY input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
file_name[strcspn(file_name, "\r\n")] = 0; /* trim '\n' from end */
/* open/validate file open for reading */
if ((fp = fopen (file_name, "r")) == NULL) {
perror ("fopen-file");
return 1;
}
/* read/print each floating-point value and value divided by 3 */
while (fscanf (fp, "%lf", &value) == 1)
printf ("\nvalue: %.4f\ndiv-3: %.4f\n", value, value / 3);
fclose(fp); /* close file */
getchar(); /* hold terminal open on windows */
return 0;
}
Example Use/Output
$ ./bin/readdivfloats
Enter filename: dat/floats.txt
value: 0.0071
div-3: 0.0024
value: -0.0242
div-3: -0.0081
value: 23.9401
div-3: 7.9800
value: 0.0000
div-3: 0.0000
value: 0.3073
div-3: 0.1024
value: 0.2046
div-3: 0.0682
Compiling From the Command Line
If the reason you have getchar()
at the end of your code is to hold the terminal window open after your program finishes due to your using the Visual Studio IDE, you may want to consider just using the command line for small projects. (1) you don't have to set up a project in VS, (2) you can compile many different source files from the same directory in the time it takes to setup another project, and (3) you learn what compiler options you need, so you can then tell the IDE how you want your code compiled.
If using VS, it provides the "VS Developers Command Prompt", which is just a cmd.exe
(Command Prompt) with the appropriate path and compiler environment variables set. The VS compiler is cl.exe
. All you need to do to compile this code (in filename readdivfloats.c
is:
cl /nologo /W3 /wd4996 /Ox /Fereaddivfloats readdivfloats.c
The /Fe
option just names the resulting executable, so here it will be readdivfloats.exe
in the same directory. I generally like to keep my source directory clean, so I create obj
and bin
subdirectories to put all the object files and executables in. The /Fo
option let's you name the object file (or you can simply name a directory and the object files will be named with the name of the source file). So with that in mind, to put the object file below the .\obj
subdirectory and the exe
in the .\bin
subdirectory, you would use:
cl /nologo /W3 /wd4996 /Ox /Foobj/ /Febin/readdivfloats readdivfloats.c
/W3
turns on full warnings, /wd4996
disables warning 4996
, (the annoying #define _CRT_SECURE_NO_WARNINGS
warning), Ox
turns on fast optimization. You can see all options simply by entering cl /?
in the terminal.