从二进制文件中读取数据

3

我正在尝试从二进制文件中计算0和1的数量。问题是,我的0的数量正确,但1的数量等于0的数量。 我的做法是逐个字符读取文件。由于最多可能有256个字符,我将结果存储在一个临时数组中,分别用于0和1,并在再次出现字符时从数组中检索。

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
void func(int* a1 ,int* a2)
{
    for(int i=0;i<256;i++)
    for(int j=0;j<8;j++)
    {
        if( (i & 1) ==1 )
        {
            a1[i]+=1;
        }
        else if( (i & 1) ==0 )
        {
            a2[i]+=1;   
        }
        i>>1;
    }
}
int main()
{
    int zero[256];
    int one[256];
    int tzero[256];
    int tone[256];
    for(int i=0;i<256;i++)
    {
        zero[i]=0;
        one[i]=0;
        tzero[i]=0;
        tone[i]=0;
    }
    func(tone,tzero);
    FILE* input;
    FILE* output;
    output=fopen("ascii.txt","w");
    input=fopen("one.bin","r");
    int c;
    while((c=fgetc(input))!=EOF)
    {
        fprintf(output,"%d\n",c);
        zero[c]+=tzero[c];
        one[c]+=tone[c];
    }
    int zeroes=0;
    int ones=0;
    for(int i=0;i<=255;i++)
    {
        zeroes+=zero[i];
        ones+=one[i];
    }
    cout<<"zeroes:"<<zeroes<<endl;
    cout<<"ones:"<<ones<<endl;
    fclose(input);

    fclose(output);

}

12
你的问题的第二句话表明你根本没有任何问题。 - High Performance Mark
你期望 c 取什么值? - Beta
3
“no of ones are coming out to be equal to no. of ones” 的意思是“1的数量等于1的数量”。 - abelenky
2
为什么这段代码这么复杂?这是一个非常简单的问题!你甚至不需要分别计算零和一的数量;'ones =(file_size_in_bytes * 8)- zeros'。还有为什么有如此巨大的数组?除了文件句柄之外,你只需要两个变量:一个计数器和一个临时变量来保存刚读取的字节。 - Sebastian Redl
3
使用“no”作为“number”的过度使用,让我真的想跳过这个问题。 - Shoe
2个回答

1

计算零和一的循环通过执行以下操作破坏了c的值

c >>= 1;

完成八个移位后,变量 c 总是为零,因此以下代码会增加错误计数:
// The value of c is always zero
tzero[c]=z;
tone[c]=o;
one[c]+=tzero[c];
zero[c]+=tzero[c];

在位计数循环之前保存c的值,并在循环结束后恢复它。

更好的方法是提前计算tzero[]tone[]的值,而不是等待它们在文件中出现。这将使您的主循环体非常简短和干净:

while((c=fgetc(input))!=EOF) {
    one[c] += tzero[c];
    zero[c] += tzero[c];
}

@user2733715,您可以编辑问题并更新代码吗? - Sergey Kalinichenko

0

如果您的目标只是在文件中逐位计算10位,您可以通过使用C++文件流而不使用查找表大大简化事情:

#include <iostream>
#include <fstream>

int main(int argc, char** argv)
{
  std::ifstream fpInput("Input.txt");
  unsigned unOnes = 0;
  unsigned unZeros = 0;
  char chTemp;

  chTemp = fpInput.get();
  while (fpInput.good())
  {
    for (unsigned i = 0; i < 8; ++i)
    {
      if (chTemp & 0x1<<i) unOnes++;
      else unZeros++;
    }

    chTemp = fpInput.get();
  }

  fpInput.close();

  std::cout << "Found " << unOnes << " ones." << std::endl;
  std::cout << "Found " << unZeros << " zeros." << std::endl;

  return 0;
}

一个好的编译器应该在你传递正确的优化标志时将中间循环展开。

@Sebastian Redl,文件大小可能会很大...因此无法避免查找表,以避免重新计算。 - Anshul
@Anshul如果速度是您的问题,那么我建议(首先出于清晰起见)用C++读取文件命令替换C读取文件命令,并将文件内容加载到缓冲区中。 您当前逐个字符读取文件的方法可能会很慢。 - ilent2
@Anshul 对于256字节的随机数据,我测量了两个程序1000次执行的时间(对你的程序进行了一些小的修改使其能够工作)。你的程序花费了1.7秒,而我的则花费了1.8秒,这非常相似,特别是考虑到我写代码时没有考虑优化。我的程序的附加优势是,我可以给它一个10GB的文件,并且它仍然可以运行,而你的程序需要进一步调整,如果你打算将整个文件内容存储在内存中,则你的程序可能无法运行。无论如何...专注于编写易于阅读的代码,优化稍后再说。 - ilent2
谢谢您的建议。但是,您能否解释一下为什么我的程序无法运行10GB的文件? - Anshul
@Anshul,你的程序需要将整个文件加载到内存中。使用当前的实现方式,你正在使用有限的堆栈内存(http://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/)。你可以重写程序来使用堆,但我建议将文件分成可管理的块(可以使用`fpInput.read(buffer, buffer_size)`或C语言等效方法)。你可以使用像我的程序中未优化的方法或者像你的程序中一样使用查找表来处理缓冲区,无论哪种方式,你都需要进行一些调整。祝你好运。 - ilent2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接