在 Java 中实现 writeBit 方法

Implementing a writeBit method in Java

所以我知道在 Java 中你不能将单个位写到文件中,你必须使用 writeByte。我有一些理解,有一种方法可以实现 writeBit 方法,该方法通过在 8 'bits' 连接在一起后调用 writeByte 来使用 writeByte。我希望像这样实现:

public void writeBit(char bit) {
    try {
    //functionality here
    } catch (IOException e) {
        System.out.println(e);
    }
}

但我似乎无法开始。我知道我可能应该有一些属性来跟踪我连接了多少位,但除此之外我不知道如何实现它。

我想我的大问题是如何在不丢失连接的位字符串的情况下连续调用 writeBit,如果要使用 writeByte,writeBit 的实现会是什么样子?

附带说明一下,如果不清楚,我在这里使用 DataOutputStream。

用两个实例变量创建一个class,第一个变量是你到目前为止已经累积的位数,第二个是你累积了多少位数。使用移位和或操作向缓冲区中插入一个位(初始化为零),并增加位数。一旦你有八位,写入缓冲区,然后将缓冲区和计数归零。

最后,如果计数不为零,您需要通过将缓冲区写入一个字节来清除所有剩余的位,即使它包含的位少于八位。位序列的格式需要能够处理这种可能性,除非它确保始终写入八位的倍数。

我注意到有几个人谈到了使用两个实例变量,一个用于在您向其添加位时存储字节,另一个用于跟踪到目前为止已添加了多少位。虽然这是一个非常好的方法,但我想说明为什么您不需要第二个实例变量。

理论

无需跟踪到目前为止已添加了多少位。我们唯一需要的信息是“这个字节已经填满了吗?”。不要将字节初始化为 0,而是尝试将其初始化为值 1。然后每次添加一位时,将字节的位向左移动一位(使用位移运算符 <<),然后在最右边添加新位。

实际上,它看起来像这样,其中 X 是新添加的位:

将字节初始化为值1:00000001

要插入一个新位,将位向左移动:00000010

并在最右边添加新位X0000001X

左移:000001X0

添加新位:000001XX

最终,您将从您的方法中写入 7 位,最左边的位将是 11XXXXXXX

因此在您的方法中,您可以在每次调用时检查最左边的位是否已设置。如果是,那么您就知道您已准备好在此迭代中将字节写入文件。你会开始做同样的事情,向左移动然后添加新的位,所以现在你有 XXXXXXXX。然后将现在已满的字节写入文件,然后将字节重置为值 1,以便循环可以重新开始。

正在编写代码

首先,您需要一个实例变量来跟踪这些位。它需要是 byte 类型,我就称它为 buffer.

要将位向左移动一位,我们可以使用位移运算符<<。而且,为了让我们的生活更轻松,甚至还有位移赋值运算符 <<=,因此我们可以执行位移并将新值赋回给变量,所有操作都在一次操作中完成。这给我们留下了:

buffer <<= 1;

接下来我们需要做的是添加新位。如果您将一个值与 1 进行或运算,最右边的位将被设置,其余位将不受影响。如果您将值与 0 进行或运算,none 位会受到影响。如果新位是 1,我们可以使用这个技巧只设置最右边的位(|= 是 OR 赋值运算符):

buffer |= bit ? 1 : 0;

然后,此代码的最后一段是编写 if 语句来检查最左边的位是否已设置。如果是,那么当我们用 10000000 与它时,我们将得到 10000000。如果没有,我们将得到 0000000010000000 是十进制的 128(或者 -128,或者 256,都可以),所以我们的表达式是:

(buffer & 128) == 128

结果

将所有这些部分放在一起,我们得到:

// Notice bit is type boolean
public void writeBit(boolean bit) {
    // If the leftmost bit in buffer is set:
    if ((buffer & 128) == 128) {

        // Shift all the bits in buffer to the left 1 place
        buffer <<= 1;

        // Add the new bit in the rightmost place
        buffer |= bit ? 1 : 0;

        // Write the now-full byte to the file
        // I'm just calling your DataOutputStream "dos" here
        try {
            dos.writeByte(buffer);
        } catch (IOException e) {
            throw new RuntimeException();
        }

        // Reset buffer to a value of 1
        buffer = 1;
    } else {
        // Shift all the bits in buffer to the left 1 place
        buffer <<= 1;

        // Add the new bit in the rightmost place
        buffer |= bit ? 1 : 0;
    }
}