Как я могу сложить числа в файле? - PullRequest
5 голосов
/ 22 января 2020

У меня большой текстовый файл. В этом файле есть несколько чисел, которые я хочу сложить вместе.

То, что я пробовал:

int sum = 0, i = 0;
file = fopen(filename, "r");
while ((i = fgetc(file)) != EOF) {
    if (isdigit(i)) {
        sum++;
    }
}
printf("Sum of numbers is: %i", sum);
fclose(file);

Но это isdigit(i) просто счетчик количества цифр в этом файле , а не какова сумма чисел.

Ввод: "This text 15 is very 19 nice."
Результат должен быть: Sum of numbers is: 34

Ответы [ 6 ]

5 голосов
/ 23 января 2020

Рассмотрим десятичное размещение

Недостающая часть в коде вопроса накапливает цифры (в отличие от подсчета цифр с помощью sum++;) И умножает на десять предыдущее накопленное число перед добавлением следующего ди git.

Ответ: number = number * 10 + i - '0';

Часть - '0' преобразует ASCII di git в число.

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

популярный спрос (комментарии были удалены) Я добавил простую, но работающую проверку переполнения:

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <error.h>
#include <limits.h>

int main(int argc, char* argv[]) {
  int sum = 0, state = 0, i = 0, dir = 1;
  unsigned int number = 0, check;
  if (argc < 2) {
    fprintf(stderr, "Missing filename\n");
    return EXIT_FAILURE;
  }
  char* filename = argv[1];
  FILE* file = fopen(filename, "r");
  if (!file) {
    perror(filename);
    return EXIT_FAILURE;
  }
  while (i != EOF) {
    i = fgetc(file);
    if (isdigit(i)) {
      if (dir) {
        state = 1;
        check = number;
        number = number * 10 + i - '0';
        if (check > number || number > INT_MAX) {
          fprintf(stderr, "Single number overflow error\n");
          fclose(file);
          return EXIT_FAILURE;
        }
      }
    } else {
      if (state && dir) {
        check = number;
        if (dir < 0 && sum < 0)
          check -= sum;
        else if (dir > 0 && sum > 0)
          check += sum;
        if (check > INT_MAX) {
          fprintf(stderr, "Sum overflow error\n");
          fclose(file);
          return EXIT_FAILURE;
        }
        sum += number * dir;
        number = 0;
      }
      state = 0;
      dir = i == '-' ? -1 : i == '.' ? 0 : 1;
    }
  }
  printf("Sum of numbers is: %i\n", sum);
  fclose(file);
  return EXIT_SUCCESS;
}

Тестовый запуск:

$ cat opString.txt 
This text 15 is very 19 nice.
$ ./test2 opString.txt 
Sum of numbers is: 34
$ 

И на всякий случай, если вы на 64-битной linux и требует гораздо более высокой производительности (вы упомянули большой файл), приведенный ниже код отобразит весь файл (даже файл больше, чем память, ядро ​​прекрасно с этим справится) и не вызовет библиотеку для каждого символа. В моих тестах isdigit() и strtol() значительно замедляют его.

#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <limits.h>
#include <sys/mman.h>

int addToSum(unsigned int* number, int* sum, int dir, FILE* file) {
  unsigned int check;
  check = *number;
  if (dir < 0 && *sum < 0)
    check -= *sum;
  else if (dir > 0 && *sum > 0)
    check += *sum;
  if (check > INT_MAX) {
    fprintf(stderr, "Sum overflow error\n");
    fclose(file);
    exit(EXIT_FAILURE);
  }
  *sum += *number * dir;
  *number = 0;
}

int main(int argc, char* argv[]) {
  int sum = 0, state = 0, i = 0, dir = 1;
  unsigned int number = 0, check;
  if (argc < 2) {
    fprintf(stderr, "Missing filename\n");
    return EXIT_FAILURE;
  }
  char* filename = argv[1];
  FILE* file = fopen(filename, "r");
  if (!file) {
    perror(filename);
    return EXIT_FAILURE;
  }
  if (fseek(file, 0L, SEEK_END) < 0) {
    perror("fseek failed");
    fclose(file);
    return EXIT_FAILURE;
  }

  long fsize = ftell(file);
  char* fmap = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fileno(file), 0);
  if (fmap == MAP_FAILED) {
    perror("map failed");
    fclose(file);
    return EXIT_FAILURE;
  }

  long pos = 0;
  while (pos < fsize) {
    i = fmap[pos++];
    if (i >= '0' && i <= '9') {
      if (dir) {
        state = 1;
        check = number;
        number = number * 10 + i - '0';
        if (check > number || number > INT_MAX) {
          fprintf(stderr, "Single number overflow error\n");
          fclose(file);
          return EXIT_FAILURE;
        }
      }
    } else {
      if (state && dir) addToSum(&number, &sum, dir, file);
      state = 0;
      dir = i == '-' ? -1 : i == '.' ? 0 : 1;
    }
  }
  if (state && dir) addToSum(&number, &sum, dir, file);
  printf("Sum of numbers is: %i\n", sum);
  fclose(file);
  return EXIT_SUCCESS;
}
2 голосов
/ 23 января 2020

Имя входного файла: file.txt

This text 15 is very 19 nice.

Вывод:

Sum of numbers is: 34

Код:

#include <stdio.h>
#include <ctype.h>

int main()
{
int sum = 0, num, i = 0;
int state = 0;
FILE* f;

    if ((f = fopen("file.txt", "r")) == NULL)
        return -1;

    while ((i = fgetc(f)) != EOF) {
        switch(state) {
        case 0: // processing text
            if (isdigit(i)) {
                num = i - '0';
                state = 1;
            }
            break;
        case 1: // processing number
            if (isdigit(i)) {
                num *= 10;
                num += i - '0';
            }
            else {
                sum += num;
                num = 0;
                state = 0;
            }
            break;
        }
    }

    if (state == 1) {
        sum += num;
    }

    printf("Sum of numbers is: %i\n", sum);
    fclose(f);
    return 0;
}
1 голос
/ 27 января 2020

Ваша программа считает только количество цифр в файле. Вот простое решение, использующее getc() для вычисления количества всех целых чисел в файле без конечного автомата:

#include <stdio.h>

int count_numbers(const char *filename) {
    FILE *fp;
    unsigned long long total = 0, current = 0;
    int c;

    if ((fp = fopen(filename, "r")) == NULL) {
        printf("Cannot open input file %s\n", filename);
        return -1;
    }

    while ((c = getc(fp)) != EOF) {
        if (c >= '0' && c <= '9') {
            current = current * 10 + (c - '0');
        } else {
            total += current;
            current = 0;
        }
    }
    total += current; /* add the last number if at the very end of the file */
    printf("Sum of numbers is: %llu\n", total);
    fclose(fp);
    return 0;
}

Примечания:

  • этой программы не обрабатывает отрицательные числа, он игнорирует отрицательные знаки.
  • он не обрабатывает числа с плавающей запятой: обработка 1.1 даст сумму 2.
  • , очень длинные числа приведут к результат по модулю ULLONG_MAX+1 но, по крайней мере, поведение определено.
1 голос
/ 22 января 2020

Функция fgetc читает только один символ, который является потенциальным ди git. Он не читает целое число, состоящее из нескольких цифр одновременно. Если вы хотите прочитать целые числа, вы должны изменить лог c вашей программы. Вы можете выполнить одно из следующих действий:

  1. Использовать fget c, как вы делаете сейчас, и заставить вашу программу собирать отдельные цифры в число, используя ASCII-код из цифры и преобразование этого числа в число, используя арифметику c. Числовое значение отдельного символа ASCII di git может быть получено с помощью выражения ascii_code - '0'.
  2. . Вместо этого используйте fgets , чтобы сразу прочитать всю строку, а затем использовать strtol для преобразования любых найденных цифр в числа.
  3. Используйте fscanf () , чтобы проанализировать файл и извлечь число за один шаг ( не рекомендуется, если вы точно не знаете точно) что вы делаете ).
1 голос
/ 22 января 2020

Но это isdi git (i) является просто счетчиком количества цифр в этом текстовом содержимом, но не суммой чисел.

Имейте в виду, что Функция isdi git () читает один символ за вызов. Так, например, если он читает символ 9, значение sum должно увеличиться на i - '0' (или 57 - 48, или 9). И если бы в последовательности было два символа, например 92, считывающих по одному символу за раз, значение sum также увеличилось бы на 9+2 -> 11, а не 92. Учитывая, что это то, что вы хотите, вот как это сделать:

Значения, которые вы определили, являются цифрами, фактически являются ASCII значениями, поэтому при просмотре этой таблицы вы можете увидеть все цифры будет иметь значения от «0» до «9» (или в ASCII от 48 до 57). Таким образом, в вашем коде вы можете просто изменить одну строку для суммирования количества значений:

int sum = 0;
    file = fopen(filename, "r");

    while ((i = fgetc(file)) != EOF) {
        if (isdigit(i))
            sum += (i - '0');//subtract 48 from every 'i' verified as digit
                             //Sum will therefore add up values 
                             //between (48-48) to (57-48)
                             //(or between 0 to 9) 
        }
    printf(f,"Sum of numbers is: %i", sum);
fclose(file);

Однако , если вы хотите суммировать числа c значений, представленных последовательностью цифры в буфере, тогда код другой. Для чтения содержимого буфера потребуется сохранение флага.
В псевдокоде :

char accumlator[10] = {0}; max possible sequential digits (change as needed)
int found = 0;//flag
int sum = 0;

 while ((i = fgetc(file)) != EOF) 
 {
    if (isdigit(i))
    {
        accumulator[found] = i;
        found++;
    }
    else
    {
        if(found != 0)
        {
            sum += atoi(accumulator);
            found = 0;
            accumulator[0] = 0;
            }
        }             
    }
}
0 голосов
/ 23 января 2020

Если вы хотите добавить все строки чисел в этом, вам придется искать цифры, а затем преобразовать их из строки в целое число, используя strtol. Вот как это сделать:

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

#define OVERFLOW_STR "Number cannot be represented in an int!\n"

int main(int argc, char **argv)
{
    FILE *file;

    char n[12];
    char *p = n;
    int i = 0, sum = 0, tmp, old_errno;

    if(argc != 2)
    {
        fprintf(stderr, "Usage: %s file\n", argv[0]);
        return EXIT_FAILURE;
    }

    file = fopen(argv[1], "r");

    if(!file)
    {
        perror("Error opening file");
        return EXIT_FAILURE;
    }

    while(i != EOF)
    {
        while( (i = fgetc(file)) != EOF &&
               i != '-' &&
               i != '+' &&
               !isdigit(i) )
        {
            /* empty loop */ ;
        }
        if(i != EOF)
        {
            do {
                if( p == n + sizeof(n) - 1 ||
                    ((*n != '-' || *n != '+') &&
                    p == n + sizeof(n) - 2) )
                {
                    fprintf(stderr, "Error: " OVERFLOW_STR);
                    return EXIT_FAILURE;
                }
                *p++ = i;
            } while( (i = fgetc(file)) != EOF && isdigit(i) );
        }
        else
            break;

        *p = 0;

        old_errno = errno;
        errno = 0;
        tmp = strtol(n, NULL, 10);
        if( tmp == 0 && errno != 0 )
        {
            perror("Error converting string");
            break;
        }
        errno = old_errno;
        sum += tmp;
        p = n;
    }

    printf("Sum of numbers is: %d\n", sum);
    fclose(file);

    return 0;
}

Это в значительной степени проверено на наличие ошибок. Могут быть пропущены угловые случаи.

Ввод (содержится в файле, имя которого передается в качестве параметра командной строки):

Этот текст 15 очень 19 красиво.

Вывод:

Сумма чисел: 34

...