How to read data as characters from a text file and then divide each character by an int

£可爱£侵袭症+ 提交于 2019-12-11 17:06:24

问题


I am trying to read the data in from a text file, and then divide each value by 3, before printing the output value for each new value.

An example of the format of the text file can be seen below:

0.00707946 -0.0241935 23.9401 0 0.307334 0.2046

As can be seen from the above example, every value is seperated by a space, the number of significant figures vary, and numbers can be positive or negative. I can successfully read through and print out the values to cmd as characters, however, my aim is to divide every single value by the number 3 (an integer) which I am struggling to do.

Is my issue the choice of format specifier I have chosen in my printf statement? Or the choice of explicit casting to float (which I chose to do as some of the numbers are floating point values)

What I have tried so far:

   #define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char file_name[25];
    int current_value = 0; 
    int new_value; 


    FILE *fp;   //file handle 

    printf("Enter filename: \n");
    gets(file_name);

    fp = fopen(file_name, "r"); // read mode

    //error handling
    if (fp == NULL)
    {
        perror("Error while opening the file.\n");
        getchar(); 
        exit(EXIT_FAILURE);
    }

    while (fscanf(fp, "%d", &current_value) != EOF)     //while end of file has not been detected 
    {
        new_value = current_value / 3; 
        printf("%d ", new_value);

    }

    fclose(fp);
    getchar(); 
    return 0;
}

回答1:


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.



来源:https://stackoverflow.com/questions/55996112/how-to-read-data-as-characters-from-a-text-file-and-then-divide-each-character-b

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!