Java - нужна помощь с бинарной / кодовой манипуляцией

Для проекта я должен преобразовать двоичную строку в (массив) байтов и записать ее в файл в двоичном виде.

Скажем, у меня есть предложение, преобразованное в строку кода с использованием кодирования Хаффмана. Например, если предложение было: "привет" h = 00 e = 01, l = 10, o = 11

Тогда строковое представление будет 0001101011.

Как бы я преобразовал это в байт? <- Если этот вопрос не имеет смысла, это потому, что я мало знаю о битовом / байтовом смещении и все, что связано с манипулированием единицами и нулями.

4 ответа

Решение

Если вы действительно хотите (или должны) создать строковое представление битов, вы можете разбить строку на подстроки длиной 8 (остерегайтесь последней, которая не обязательно имеет длину 8).

Integer имеет метод для анализа строковых представлений, последовательность '0' и '1 можно проанализировать, вызвав radix = 2.

static int parseInt(String s, int radix) 

Разбирает строковый аргумент как целое число со знаком в основании, указанном вторым аргументом.

-

РЕДАКТИРОВАТЬ: Согласно комментариям Byte.parseByte это путь.

Вот простая, но, вероятно, неэффективная реализация:

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class BitOutputStream extends FilterOutputStream {

  private int bits = 0;
  private int n = 0;
  private long totalBits = 0;

  public BitOutputStream(OutputStream out) {
    super(out);
  }

  private void writeSingleBit(int bit) throws IOException {
    bits = (bits << 1) | (bit & 1);
    n++;
    totalBits++;
    if (n == 8) {
      super.write(bits);
      bits = 0;
      n = 0;
    }
  }

  /**
   * Writes the <i>numberOfBits</i> lower bits of <i>bitsToWrite</i> to the
   * output stream, starting with the most significant bit.
   */
  public void writeBits(int bitsToWrite, int numberOfBits) throws IOException {
    for (int i = numberOfBits - 1; i >= 0; i--) {
      int bit = bitsToWrite >> i;
      writeSingleBit(bit);
    }
  }

  @Override
  public void write(byte[] b, int off, int len) throws IOException {
    for (int i = 0; i < len; i++)
      writeBits(b[off + i], 8);
  }

  @Override
  public final void write(int b) throws IOException {
    writeBits(b, 8);
  }

  @Override
  public final void flush() throws IOException {
    writeBits(0, (8 - n) & 0x07);
  }

  /**
   * Returns the number of bits that have been written to this bitstream.
   */
  public long getTotalBits() {
    return totalBits;
  }
}

И соответствующий юнит-тест:

import static org.junit.Assert.*;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.junit.Test;

public class BitOutputStreamTest {

  @Test
  public void hello() throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    BitOutputStream bos = new BitOutputStream(baos);
    bos.writeBits(0x00, 2);
    bos.writeBits(0x01, 2);
    bos.writeBits(0x02, 2);
    bos.writeBits(0x02, 2);
    bos.writeBits(0x03, 2);
    assertEquals(10, bos.getTotalBits());
    bos.close();
    assertEquals(16, bos.getTotalBits());
    assertArrayEquals(new byte[] { 0x1A, (byte) 0xC0 }, baos.toByteArray());
  }
}

Этот код не выводит биты в желаемом строковом представлении, но когда вы хотите записать их в байтовый поток позже, это путь.

Обновление (2010-09-25): исправлена ​​ошибка в write(byte[], int, int) метод. Я забыл добавить off к индексу массива.

Encoding a String by concatenating String representations bot the bit sequences representing the individual characters, and then turning that into a byte again seems like a very expensive way of doing things.

Вы можете вместо этого заглянуть в Преон. Preon, в первую очередь, имеет абстракцию BitChannel, которая не дает вам сильно беспокоиться о том, чтобы изменить себя. Вы можете просто записать битовые последовательности в BitChannel. Он будет следить за "указателем битов" внутри и переводить все в байты дальше по потоку.

BitChannel channel = new OutputStreamBitChannel(...);
channel.write(1, 0); // 0 = 'h'
channel.write(2, 1); // 01 = 'e'
channel.write(3, 2); // 10 = 'l'
channel.write(4, 2); // 11 = '0'

Однако в идеале вы сможете использовать высокоуровневые абстракции Преона (preon-binding), которые не позволят вам вообще иметь дело с этим. Это просто потребует аннотации на вашей строке.

@BoundHuffmanCoded String toBeEncoded = "hello";

... а Преон позаботится обо всем остальном. Запомните, это идеальный случай, и у Preon пока нет этой аннотации. Но для этого можно зарегистрировать кодек. Но следите за этим, поскольку это то, что обязательно войдет в будущую версию Preon.

Почему вам нужно сначала преобразовать в "двоичную строку"? Просто перейдите прямо к записи байтов в качестве вывода.

Концептуально, что вы делаете, это пишите биты в byteДо тех пор, пока вы не заполните byte, Это сделано с битовым сдвигом. Чтобы добавить 1 бит внизу значения, вы делаете что-то вроде:

b = (b << 1) | 1;

а затем, как только вы заполнили байт, вам нужно увеличить свой вывод byte[] чтобы освободить место для другого, пока не сделаешь. Вы могли бы использовать ByteArrayOutputStream для того, чтобы стабильно выводить byte а затем получить byte[] потом.

Я могу указать вам на класс, который позволяет вам добавлять биты, а затем получать полученные байты позже, подумав, что он создает массив ints вместо байтов. Вы можете использовать это в качестве примера.

Другие вопросы по тегам