c - 如何将文件中的数字相加?

标签 c while-loop sum numbers fopen

我有一个大文本文件。在这个文件中有一些我想加在一起的数字。

我试过的:

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

最佳答案

考虑小数点位置

问题代码中缺少的部分是累积数字(而不是用 sum++; 计算数字)并在添加下一个数字之前乘以前一个累积数字十。

答案在:number = number * 10 + i - '0';- '0'部分是将 ASCII 数字转换为数字。

下面代码中的所有其他内容都经过检查以确保没有明显的溢出和 正确 支持与数字相邻的减号,以及忽略小数点后的数字。我确信它并不完美,但这里的想法是提供一个如何完成的工作示例,而不是经过良好测试的代码并使用库调用来为您完成。

应大众要求(评论现已删除),我添加了一个简单但有效的溢出检查:

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

关于c - 如何将文件中的数字相加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59867742/

相关文章:

c - 将变量传递给一个函数到另一个函数

c - 如何删除用户在 c 中选择的二维数组的 N 行和列数

c++ - my_string(char* s) 是什么意思?

c - inotify 真的适用于文件还是仅适用于目录?

c - 在 C while 循环中,是否可以比较两个变量 while (a < b)?

Python:循环列表

python - 在 While 循环中单击鼠标(PsychoPy)

python - 获取总和嵌套字典值的列表

使用C代码计算库仑定律

MySQL - 仅当所有行都不为空时求和,否则返回空