3

プロジェクトの場合、バイナリ文字列を(配列の)バイトに変換し、それをバイナリのファイルに書き出す必要があります。

ハフマン符号化を使用して文をコード文字列に変換したとします。たとえば、文が次の場合: "hello" h = 00 e = 01、l = 10、o = 11

その場合、文字列表現は0001101011になります。

どうすればそれをバイトに変換できますか?<-その質問が意味をなさない場合、それはビット/バイトのビット単位のシフトについてほとんど知らないためであり、それは1と0の操作に関係しています。

4

4 に答える 4

1

これは単純ですが、おそらく非効率的な実装です。

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配列インデックスに追加するのを忘れました。

于 2010-06-13T09:23:18.263 に答える
0

なぜ最初に「バイナリ文字列」に変換する必要があるのですか?出力としてバイトを書き込むだけです。

概念的にはbyte、を埋めるまで、ビットをに書き込みますbyte。これはビットシフトで行われます。値の下部に1ビットを追加するには、次のようにします。

b = (b << 1) | 1;

次に、1バイトを埋めたら、出力byte[]を増やして、完了するまで別のバイト用のスペースを確保する必要があります。ByteArrayOutputStreamそのためにも、着実に出力byteして後で取得するために使用できますbyte[]

バイトの代わりにsの配列を作成していると考えて、ビットを追加し、後で結果のバイトを取得できるクラスを紹介できますint例として使用できます。

于 2010-06-13T08:56:31.993 に答える
0

ビットの文字列表現を本当に作成したい(または作成する必要がある)場合は、文字列を長さ8のサブ文字列に分割できます(最後の文字列は必ずしも長さ8ではないことに注意してください)。

整数 には文字列表現を解析するメソッドがあり、基数=2で呼び出すことで「0」と「1」のシーケンスを解析できます。

static int parseInt(String s, int radix) 

文字列引数を、2番目の引数で指定された基数の符号付き整数として解析します。

-

編集:コメントによると、Byte.parseByteが進むべき道です。

于 2010-06-13T10:57:30.143 に答える
0

文字列表現を連結して文字列をエンコードすると、個々の文字を表すビットシーケンスがボットになり、それを再びバイトに変換することは、非常にコストのかかる方法のように思えます。

代わりに、プレオンを調べたいと思うかもしれません。Preonにはまず、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の高レベルの抽象化(preonバインディング)を使用して、これを自分で処理する必要がまったくないようにすることができます。文字列に注釈が必要なだけです。

@BoundHuffmanCoded String toBeEncoded = "hello";

...そしてプレオンが残りの面倒を見るでしょう。さて、これは理想的なケースであり、Preonにはまだこのアノテーションがないことを覚えておいてください。ただし、これにコーデックを自分で登録することは可能です。ただし、これはPreonの将来のバージョンに確実に組み込まれるものであるため、注意してください。

于 2010-08-24T10:03:01.320 に答える