Как читать данные как символы из текстового файла, а затем делить каждый символ на int - PullRequest
0 голосов
/ 05 мая 2019

Я пытаюсь прочитать данные из текстового файла, а затем разделить каждое значение на 3, прежде чем выводить выходное значение для каждого нового значения.

Пример формата текстового файла можно увидеть ниже:

0.00707946 -0.0241935 23.9401 0 0.307334 0.2046

Как видно из приведенного выше примера, каждое значение отделяется пробелом, количество значащих цифр варьируется, а числа могут быть положительными или отрицательными. Я могу успешно прочитать и распечатать значения для cmd как characters, однако моя цель - разделить каждое отдельное значение на число 3 (integer), которое я изо всех сил пытаюсь сделать.

Является ли моя проблема выбором спецификатора формата, который я выбрал в своем printf заявлении? Или выбор явного приведения к типу с плавающей точкой (который я выбрал, поскольку некоторые числа являются значениями с плавающей запятой)

Что я пробовал до сих пор:

   #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 Ответ

3 голосов
/ 05 мая 2019

Во-первых, Никогда, никогда, Всегда используйте gets(), см. Почему get () настолько опасен, что его никогда не следует использовать! . При этом вы пытаетесь разделить каждый символ на 3, а не на каждое значение с плавающей точкой. atoi - это целочисленное преобразование для строк, а не отдельных символов.

Но, кроме всего прочего, вы, по крайней мере, добросовестно пытались найти решение. Итак, давайте посмотрим, как вы можете улучшить вещи. Во-первых, не используйте магические числа , 25 в вашем коде есть магическое число , вместо этого, если вам нужна целочисленная константа, #define одна, например,

#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) {

Кроме того, не экономьте на размере буфера! В Linux константой PATH_MAX по умолчанию является 4096. 25 даже не начинает охватывать допустимые имена файлов.

Далее замените gets на fgets. Единственное предостережение в том, что теперь вы должны обрезать завершающий '\n' из буфера. Вы можете сделать это просто с помощью strcspn (который сообщит о количестве символов, не включающих символы в строке reject ). Поэтому выбор строки отклонения "\r\n" охватывает вас, а strcspn возвращает количество символов до первого из них. Вы просто обнуляете свою строку с этим индексом, перезаписывая конец строки, например

    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 */

Хорошая работа по проверке вашего файла была открыта для чтения перед использованием fp. Теперь вам просто нужно продолжить таким образом, чтобы вместо символов читались числа с плавающей точкой. Хотя я обычно рекомендую читать оставшиеся строки в буфер и затем вызывать sscanf, чтобы проанализировать значения из него, вы также можете просто использовать fscanf для чтения чисел с плавающей запятой один за другим. (все scanf преобразований, кроме "%c" и "%[...]" отбрасывают начальные пробелы)

Вы можете очень просто использовать fscanf для чтения, а затем разделить на 3 (вот где я нарушаю магическое число правило :), например,

    /* 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);

Вот и все. В целом, вы можете сделать:

#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;
}

Пример использования / Вывод

$ ./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

Компиляция из командной строки

Если причина, по которой у вас есть getchar() в конце вашего кода, заключается в том, чтобы держать окно терминала открытым после завершения вашей программы из-за использования среды IDE Visual Studio, вы можете рассмотреть возможность использования командной строки для небольших проектов. , (1) вам не нужно настраивать проект в VS, (2) вы можете скомпилировать много разных исходных файлов из одного каталога за время, необходимое для настройки другого проекта, и (3) вы узнаете, какие параметры компилятора вы нужно, чтобы вы могли сообщить IDE, как вы хотите, чтобы ваш код компилировался.

При использовании VS он предоставляет «Командную строку разработчиков VS», которая представляет собой просто cmd.exe (Командная строка) с соответствующим набором переменных пути и среды компилятора. Компилятор VS cl.exe. Все, что вам нужно сделать, чтобы скомпилировать этот код (в имени файла readdivfloats.c это:

cl /nologo /W3 /wd4996 /Ox /Fereaddivfloats readdivfloats.c

Опция /Fe просто называет итоговый исполняемый файл, поэтому здесь он будет readdivfloats.exe в том же каталоге. Как правило, мне нравится поддерживать мой исходный каталог в чистоте, поэтому я создаю подкаталоги obj и bin, чтобы поместить все объектные файлы и исполняемые файлы. Опция /Fo позволяет назвать объектный файл (или вы можете просто назвать каталог и объектные файлы будут названы с именем исходного файла). Поэтому, имея в виду, чтобы поместить объектный файл ниже подкаталога .\obj и exe в подкаталоге .\bin, вы должны использовать:

cl /nologo /W3 /wd4996 /Ox /Foobj/ /Febin/readdivfloats readdivfloats.c

/W3 включает полные предупреждения, /wd4996 отключает предупреждение 4996, (раздражающее #define _CRT_SECURE_NO_WARNINGS предупреждение), Ox включает быструю оптимизацию. Вы можете увидеть все опции, просто набрав cl /? в терминале.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...