【问题标题】:Is it possible to read bits in a loop by determining the amount to read? [closed]是否可以通过确定要读取的数量来读取循环中的位? [关闭]
【发布时间】:2019-05-24 22:12:01
【问题描述】:

有一个 DataInputStream 可供读取

我遇到了一个特定的算法:

  1. int[] nmbrs = new int[64];

  2. 读取一个 4 位无符号整数。为读取的值分配长度

  3. for (int i = 0; i

    3.1 将长度位无符号整数读取为 nmbrs[i]

可以用Java编写吗?怎么写?

【问题讨论】:

  • 是“4. 从点 1 重复”支持表示“重复步骤 3.1”,即考虑到步骤 3(for 循环)意味着完全相同的事情,它完全是多余的。跨度>
  • @Andreas 这是另一个数组。所以我想问题是如何做两次。
  • “如何做某事两次” 将逻辑放入一个方法中并调用两次:int[] nmbrs = readNumbers(dataStream); int[] nmbrs = readNumbers(dataStream);
  • 所以如果前 4 位(Length 值)是数字 3,那么您将 64 x 3 = 192 位读入 nmbrs,因此总共 4 + 192 = 196 位,即 24.5 字节,将从流中读取。这是否意味着nmbrs2Length 值位于最后一个字节的后半部分?因此,如果它也是值 3,那么您将从输入流中总共消耗 24.5 + 24.5 = 49 个字节?
  • DataInputStream 读取完整字节(或更多)。您无法读取单个位,因此如果 nmbrs 不消耗多个完整字节(请参阅我之前的评论),那么您不能只在 DataInputStream 上重复操作,否则您将丢失未使用的位处理的最后一个字节。

标签: java bit datainputstream


【解决方案1】:

可以用Java编写吗?怎么写?

Java 没有提供以小于一字节为单位执行 I/O 的机制,但您可以在面向字节的 I/O 之上实现它。您需要在读取时一次缓冲一个或多个字节,并跟踪该缓冲区中的位级位置。

还请注意,这对(逻辑)位顺序问题很敏感——即,您是从最重要到最不重要的顺序读出位还是相反?

【讨论】:

  • 我对终点有点紧张。如果不是 8 的倍数怎么办?
  • 这是你的协议的一个功能,@Shklyar。发送端在与接收端相同的每字节 I/O 约束下运行,因此您需要知道它在比特流末尾做了什么。但请注意,完成第 3 步 / 3.1 并不一定意味着您处于比特流的末尾。
【解决方案2】:

创建一个从底层DataInputStream 读取位的BitInputStream 类。

像这样:

public final class BitInputStream implements Closeable {

    private final InputStream in;
    private final ByteOrder streamBitOrder;
    private int bits;
    private byte bitsLeft;

    public BitInputStream(InputStream in) {
        this(in, ByteOrder.BIG_ENDIAN);
    }

    public BitInputStream(InputStream in, ByteOrder bitOrder) {
        Objects.requireNonNull(in);
        Objects.requireNonNull(bitOrder);
        this.in = in;
        this.streamBitOrder = bitOrder;
    }

    @Override
    public void close() throws IOException {
        this.in.close();
    }

    public int readBit() throws IOException {
        if (this.bitsLeft == 0) {
            if ((this.bits = this.in.read()) == -1)
                throw new EOFException();
            this.bitsLeft = 8;
        }
        int bitIdx = (this.streamBitOrder == ByteOrder.BIG_ENDIAN ? this.bitsLeft - 1 : 8 - this.bitsLeft);
        this.bitsLeft--;
        return (this.bits >> bitIdx) & 1;
    }

    public int readInt() throws IOException {
        return readInt(Integer.SIZE, this.streamBitOrder);
    }

    public int readInt(ByteOrder bitOrder) throws IOException {
        return readInt(Integer.SIZE, bitOrder);
    }

    public int readInt(int len) throws IOException {
        return readInt(len, this.streamBitOrder);
    }

    public int readInt(int len, ByteOrder bitOrder) throws IOException {
        if (len == 0)
            return 0;
        if (len < 0 || len > Integer.SIZE)
            throw new IllegalArgumentException("Invalid len: " + len + " (must be 0-" + Integer.SIZE + ")");
        int value = 0;
        if (bitOrder == ByteOrder.BIG_ENDIAN) {
            for (int i = 0; i < len; i++)
                value = (value << 1) | readBit();
        } else {
            for (int i = 0; i < len; i++)
                value |= readBit() << i;
        }
        return value;
    }

}

测试

public static void main(String[] args) throws Exception {
    String bitData = "0101 00001 00001 00010 00011 00101 01000 01101 10101" // 5: 1, 1, 2, 3, 5, 8, 13, 21
                  + " 0011 000 001 010 011 100 101 110 111";                // 3: 0, 1, 2, 3, 4, 5, 6, 7
    BigInteger bi = new BigInteger(bitData.replaceAll(" ", ""), 2);
    System.out.println("0x" + bi.toString(16) + " = 0b" + bi.toString(2));
    byte[] byteData = bi.toByteArray();
    try (BitInputStream in = new BitInputStream(new ByteArrayInputStream(byteData))) {
        int[] nmbrs = readNmbrs(in);
        int[] nmbrs2 = readNmbrs(in);
        System.out.println(Arrays.toString(nmbrs));
        System.out.println(Arrays.toString(nmbrs2));
    }
}
private static int[] readNmbrs(BitInputStream in) throws IOException {
    int[] nmbrs = new int[8];
    int length = in.readInt(4);
    for (int i = 0; i < nmbrs.length; i++)
        nmbrs[i] = in.readInt(length);
    return nmbrs;
}

输出

0x5084432a1b53053977 = 0b10100001000010001000011001010100001101101010011000001010011100101110111
[1, 1, 2, 3, 5, 8, 13, 21]
[0, 1, 2, 3, 4, 5, 6, 7]

【讨论】:

  • 我想另外问一下ByteOrder。 LITTLE_ENDIAN 的值表示 0b1010100100001111 将被读取为 0b1111000010010101 = 0xf095,而不是 0x0fa9
猜你喜欢
  • 1970-01-01
  • 2015-11-03
  • 2012-02-24
  • 1970-01-01
  • 2012-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多