使用C中的fread从stdin缓冲读取

N 1*_*1.1 9 c buffering fread

我试图stdin通过setvbuf在`_IOFBF~模式下使用来有效地读取.我是新来的缓冲.我正在寻找有用的例子.

输入以两个整数(n,k)开头.下一n行输入包含1个整数.目的是打印可以整除的整数数k.

#define BUFSIZE 32
int main(){
  int n, k, tmp, ans=0, i, j;
  char buf[BUFSIZE+1] = {'0'};
  setvbuf(stdin, (char*)NULL, _IONBF, 0);
  scanf("%d%d\n", &n, &k);
  while(n>0 && fread(buf, (size_t)1, (size_t)BUFSIZE, stdin)){
    i=0; j=0;
    while(n>0 && sscanf(buf+j, "%d%n", &tmp, &i)){
    //printf("tmp %d - scan %d\n",tmp,i); //for debugging
      if(tmp%k==0)  ++ans;
      j += i; //increment the position where sscanf should read from
      --n;
    }
  }
  printf("%d", ans);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

问题是如果数为界,缓冲区 buf将读取23距离2354\n,当它应该已经读过2354(它不能),或者什么都没有.

我该如何解决这个问题?


编辑
现已解决(带分析).

编辑
完整的问题规范

N 1*_*1.1 2

版本 1:getchar_unlocked按照 R Samuel Klatchko 的建议使用(参见评论)

#define BUFSIZE 32*1024
int main(){
  int lines, number=0, dividend, ans=0;
  char c;
  setvbuf(stdin, (char*)NULL, _IOFBF, 0);// full buffering mode
  scanf("%d%d\n", &lines, ÷nd);
  while(lines>0){
    c = getchar_unlocked();
    //parse the number using characters
    //each number is on a separate line
    if(c=='\n'){
      if(number % dividend == 0)    ans += 1;
      lines -= 1;
      number = 0;
    }
    else
      number = c - '0' + 10*number;
  }

  printf("%d are divisible by %d \n", ans, dividend);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

版本 2:用于fread读取块并从中解析数字。

#define BUFSIZE 32*1024
int main(){
int lines, number=0, dividend, ans=0, i, chars_read;
char buf[BUFSIZE+1] = {0}; //initialise all elements to 0
scanf("%d%d\n",&lines, &dividend);

while((chars_read = fread(buf, 1, BUFSIZE, stdin)) > 0){
  //read the chars from buf
  for(i=0; i < chars_read; i++){
    //parse the number using characters
    //each number is on a separate line
    if(buf[i] != '\n')
      number = buf[i] - '0' + 10*number;
    else{
      if(number%dividend==0)    ans += 1;
      lines -= 1;
      number = 0;
    }       
  }

if(lines==0)  break;
}

printf("%d are divisible by %d \n", ans, dividend);
return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果:(测试 1000 万个数字能否被 11 整除)

运行 1:(不带 setvbuf 的版本 1)0.782 秒
运行 2:(带 setvbuf 的版本 1)0.684 秒
运行 3:(版本 2)0.534

PS - 每次运行都使用 -O1 标志用 GCC 编译