如何将字节输入写入输出文件?

时间:2013-08-24 16:47:37

标签: c++ c++11 io ifstream ofstream

我正在尝试为一个函数编写测试程序,将一个文件中的写入信息从一个文件转移到另一个文件。我很确定我的BitOutputStream类可以工作,因为下面的代码按预期打印出一个'A'。但是当我将代码更改为下面的第二个版本时,它接受输入文件并写入输出文件,输入与输出不匹配。我不确定我是否无意中改变了我不应该做的事情,或者输入文件中有某些“隐藏”字符会导致不匹配或字节转换。我怀疑我可能没有正确使用get()。任何帮助将不胜感激。

/ * first(working)version * /

int main(int argc, char* argv[])
{
  BitOutputStream bos(std::cout);  // channel output to stdout
  bos.writeBit(1);
  bos.writeBit(0);
  bos.writeBit(0);
  bos.writeBit(0);
  bos.writeBit(0);
  bos.writeBit(0);
  bos.writeBit(0);
  bos.writeBit(1);

  // prints an 'A' as expected

  return 0;
}

/ * second(非工作)版本* /

int main(int argc, char* argv[])
{
  std::string ifileName = std::string(argv[1]);
  std::string ofileName = std::string(argv[2]);

  ofstream ofile;
  ifstream ifile;

  if(ifile)
    ifile.open(ifileName, ios::binary);
  if(ofile)
    ofile.open(ofileName, ios::binary);

  BitOutputStream bos(ofile);

  int i;
  while (ifile.good()) { 
    i = bos.writeBit(ifile.get()); // could the error be due to incorrect usage of get()?
    std::cout << i << std::endl;  // just to see how many bits have been processed
  }

    bos.flush();

    ifile.close();
    ofile.close();

    return i;
 }

我用

调用的第一个版本
./a.out

我用

打电话的第二个版本
./a.out input output

打印 1 2 3 到终端指示writeBit被调用三次,但我预计它会被调用8次'A',那么为什么只有3次呢?

输入文件中只有“A”。 在输入文件上调用hexdump会生成:

0000000 0a41
0000002

在输出文件上调用hexdump会生成:

0000000 0005
0000001

同样为什么hexdump在0a-'linefeed'和41-'A'之前生成7 0',最后是'0000002'的含义是什么?我可以在代码的第二个版本中更改什么,以便输入和输出的hexdump匹配?

编辑:这是BitOutputStream的声明/定义

#ifndef BITOUTPUTSTREAM_HPP
#define BITOUTPUTSTREAM_HPP
#include <iostream>

class BitOutputStream {

private: 
  char buf;             // one byte buffer of bits
  int nbits;            // how many bits have been written to buf
  std::ostream& out;    // reference to the output stream to use

public:
  /* Initialize a BitOutputStream that will 
   * use the given ostream for output. 
   * */
  BitOutputStream(std::ostream& os) : out(os) {
    buf = nbits = 0;    // clear buffer and bit counter
  }

  /* Send the buffer to the output, and clear it */
  void flush() {
  out.put(buf);
  buf = nbits = 0;
  }


  /* Write the least sig bit of arg into buffer */
  int writeBit(int i) {
  // If bit buffer is full, flush it.
  if (nbits == 8) 
    flush();

  int lb = i & 1;      // extract the lowest bit
  buf |= lb << nbits;  // shift it nbits and put in in buf

  // increment index
  nbits++;

  return nbits;
  }
};

#endif // BITOUTPUTSTREAM_HPP

1 个答案:

答案 0 :(得分:1)

问题是位与字节的概念。大多数函数使用的字节是位的集合。文件以字节为单位读取。您的writeBit方法写入的位不是字节。

如果你真的,真的必须写入位,你需要读取字节,转换为位并写入每个位。 (顺便说一句,大多数计算机使用较大的单位,例如字节和单词,效果更好。)

#include <cstdint>
#include <iostream>

using namespace std; // Because I'm lazy and this is an example.

int main(void)
{
    uint8_t byte;
// Open the file
//....

//  Read file as bytes.
    while (ifile.read(&byte, sizeof(byte)))
    {
        for (unsigned int i = 0;
             i < CHAR_BIT; // number of bits in a byte
             ++i)
        {
            bos.writeBit(byte & 1);
            byte = byte >> 1;
        }
    }
//...
    return EXIT_SUCCESS;
}  

有更快的方法可以将一个文件的内容复制到另一个文件。想到的第一个就是让操作系统去做。

编辑1:分析程序。
BitOutputStream类将累积传递给writeBit方法的整数的最低有效位。只要累加了8位,writeBit方法就会将一个字节写入输出流。

在第一个程序中,传递只有一个有效位的整数,或者假设整数常量只有一个有效位。

在第二个程序中,您通过istream::get()方法读取一个字节(8位)。 writeBit方法仅查看最低有效位并将该位放入BitOutputStream缓冲区。然后循环从输入文件中获取另一个字节,并且只将最低有效位附加到BitOutputStream缓冲区。

读取第8个字节后,BitOutputStream缓冲区将有8位,然后将8位字节写入流。

所以如果我的文件只有一个'A'字符,即0x41,或者是二进制MSB:0100 0001.
writeBit方法将值与0x1进行AND运算,结果为0x01或二进制1.此位置于输出缓冲区中。 {strong> writeBit 剩余比特的遗漏位置

当只有1个字符时,BitOutputStream缓冲区只有1位。此外,您的程序从不调用BitOutputStream::flush(),因此部分位永远不会输出到流。没有析构函数,因此当bos对象被破坏而没有写入输出流时,不完整的字节(只有1位)消失。

由于你的第二个程序将一个8位的整个字节传递给一个只使用1位的函数,我推断你已经混淆了位和字节的概念。请参阅我上面解决您的问题的代码片段。

编辑2:测试BitOutputStream类。
测试此类的最佳过程是从ostream派生自己的类。此类应该有一个方法,您可以将期望的已知值传递给。将此类的实例传递给BitOutputStream。该类的put方法可以将BitOutputStream预期已知值的值进行比较。