Во-первых, Никогда, никогда, Всегда используйте 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 /?
в терминале.