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[]
потом.
Я могу указать вам на класс, который позволяет вам добавлять биты, а затем получать полученные байты позже, подумав, что он создает массив int
s вместо байтов. Вы можете использовать это в качестве примера.