C语言和C++从不缺少二进制处理方案,甚至C++有类二进制类 bitset。
当我们想直接操作二进制数据到输入输出流,可能性不大,因为流都被封装了,但这不妨碍我们进行更高抽象的封装。
C 和 C++的二进制流操作 read, write,最小颗粒是 char,字节,8 比特。但C语言提供了位操作,我们可以在此基础上进行每个比特的控制,然后将他们再放进流中,当然,流必须最少是8位的,我们只需要在最后一个压入流的比特进行对齐。
具体的实现是封装两个二进制标准输入和输出类,采用全静态成员及静态函数,可以控制最小一个bit的数据。
一般的用法是通过管道,进行输入输出,如果用标准输出,你可能看到的是乱码。
#include
#include
#include
#include
struct BinaryStdIn
{
//单例模式,不可实体化
BinaryStdIn() = delete;
//比特流是否为空
static auto isEmpty() -> bool
{
return buffer == EOF;
}
//读取1位数据并返回一个bool值
static auto readBool() -> bool
{
if (isEmpty())
{
throw "Reading form empty input stream";
}
N--;
bool bit = buffer[N];
if (N == 0)
{
fillBuffer();
}
return bit;
}
//读取8位数据并返回一个char值
static auto readChar() -> char
{
if (isEmpty())
{
throw "Reading form empty input stream";
}
if (N == 8)
{
auto x = buffer;
fillBuffer();
return static_cast<char>(x.to_ulong() & 0xff);
}
auto x = buffer;
x <<= (8 - N);
int oldN = N;
fillBuffer();
if (isEmpty())
{
throw "Reading form empty input stream";
}
N = oldN;
x |= (buffer >> N);
return static_cast<char>(x.to_ulong() & 0xff);
}
//读取r(1~16)位数据并返回一个char值
static auto readChar(int r) -> char
{
if (r < 1 || r > 16)
{
throw std::string("Illegal value of r = ") + std::to_string(r);
}
if (r == 8)
{
return readChar();
}
char x = 0;
for (int i = 0; i != r; ++i)
{
x <<= 1;
bool bit = readBool();
if (bit)
{
x |= 1;
}
}
return x;
}
//关闭比特流
static void close()
{
std::cin.setstate(std::ios::eofbit);
}
//静态初始化
static void begin()
{
fillBuffer();
}
private:
//读流到缓冲区
static void fillBuffer()
{
if (std::cin.good())
{
std::cin.read(reinterpret_cast<char *>(&buffer), sizeof(char));
N = 8;
}
else
{
// std::cerr << "EOF" << std::endl;
buffer = EOF;
N = -1;
}
}
//缓冲区
static std::bitset<8> buffer;
//缓冲区比特指针
static int N;
};
std::bitset<8> BinaryStdIn::buffer;
int BinaryStdIn::N;
struct BinaryStdOut
{
BinaryStdOut() = delete;
//刷新缓冲流
static void flush()
{
clearBuffer();
std::cout.flush();
}
//写入指定的比特
static void write(bool bit)
{
writeBit(bit);
}
//写入指定的8位字符
static void write(char c)
{
writeByte(c);
}
static void write(int x)
{
writeByte((x >> 24) & 0xff);
writeByte((x >> 16) & 0xff);
writeByte((x >> 8) & 0xff);
writeByte((x >> 0) & 0xff);
}
//写入指定字符的第r(1~16)位
static void write(char c, int r)
{
if (r == 8)
{
write(c);
}
if (r < 1 || r > 16)
{
throw std::string("Illegal value for r = ") + std::to_string(r);
}
for (int i = 0; i != r; ++i)
{
bool bit = ((c >> (r - i - 1)) & 1) == 1;
writeBit(bit);
}
}
//关闭比特流
static void close()
{
std::cout.flush();
}
private:
static void writeBit(bool bit)
{
buffer <<= 1;
if (bit)
{
buffer |= 1;
}
N++;
if (N == 8)
{
clearBuffer();
}
}
static void writeByte(int x)
{
assert(x >= 0 && x < 256);
char c = static_cast<char>(x);
if (N == 0)
{
std::cout.write(&c, sizeof(char));
return;
}
for (int i = 0; i != 8; ++i)
{
bool bit = ((c >> (8 - i - 1)) & 1) == 1;
writeBit(bit);
}
}
static void clearBuffer()
{
if (N == 0)
{
return;
}
if (N > 0)
{
buffer <<= (8 - N);
}
std::cout.write(reinterpret_cast<char *>(&buffer), sizeof(char));
N = 0;
buffer = 0;
}
//缓冲区
static std::bitset<8> buffer;
//缓冲区比特指针
static int N;
};
std::bitset<8> BinaryStdOut::buffer;
int BinaryStdOut::N;
void BinaryDump(char *argv[])
{
BinaryStdIn::begin();
int width = std::stoi(argv[1]);
int cnt = 0;
for (cnt = 0; !BinaryStdIn::isEmpty(); ++cnt)
{
if (width == 0)
{
continue;
}
if (cnt != 0 && cnt % width == 0)
{
std::cout << '\n';
}
if (BinaryStdIn::readBool())
{
std::cout << '1';
}
else
{
std::cout << '0';
}
}
std::cout << std::endl;
std::cout << cnt << " bits" << std::endl;
}
auto main(int /*argc*/, char *argv[]) -> int
{
// BinaryStdIn::begin();
// std::cout << BinaryStdIn::readChar(16) << std::endl;
// std::cout << BinaryStdIn::readChar() << std::endl;
// std::cout << BinaryStdIn::readBool();
// std::cout << BinaryStdIn::readBool();
// std::cout << BinaryStdIn::readBool();
// std::cout << BinaryStdIn::readBool();
// std::cout << BinaryStdIn::readBool();
// std::cout << BinaryStdIn::readBool();
// std::cout << BinaryStdIn::readBool();
// std::cout << BinaryStdIn::readBool();
BinaryDump(argv);
// for (int i = ((48 << 24) + (49 << 16) + (50 << 8) + 51);
// i != ((48 << 24) + (49 << 16) + (50 << 8) + 59); ++i)
// {
// BinaryStdOut::write(i);
// }
// BinaryStdOut::flush();
return 0;
}