1

3 文字の文字列 (常にアルファベット) を 2 つの整数の 2 バイト [] 配列にエンコードする必要がありました。これは、スペースとパフォーマンス上の理由から節約するために行われました。

現在、要件が少し変更されています。String は可変長になります。長さ 3 (上記のように) または長さ 4 で、先頭に 1 つの特殊文字があります。特殊文字は固定されています。つまり、@ を選択すると、常に @ になり、常に先頭になります。したがって、文字列の長さが 3 の場合、アルファベットのみが含まれ、長さが 4 の場合、最初の文字は常に「@」であり、その後に 3 つのアルファベットが続くことが確実です。

だから私は使うことができます

charsAsNumbers[0] = (byte) (locationChars[0] - '@');

それ以外の

charsAsNumbers[0] = (byte) (chars[0] - 'A');

3 文字または 4 文字を 2 バイト配列にエンコードしてデコードすることはできますか? もしそうなら、どのように?

4

5 に答える 5

2

直接の答えではありませんが、エンコードを行う方法は次のとおりです。

   public static byte[] encode(String s) {
      int code = s.charAt(0) - 'A' + (32 * (s.charAt(1) - 'A' + 32 * (s.charAt(2) - 'A')));
      byte[] encoded = { (byte) ((code >>> 8) & 255), (byte) (code & 255) };
      return encoded;
   }

最初の行では、Horner のスキーマを使用して、各文字の 5 ビットを算術的に組み立てて整数にします。入力文字のいずれかが [A-`] の範囲外にある場合、恐ろしく失敗します。

2 行目は、整数の先頭と末尾のバイトから 2 バイトの配列を組み立てます。

デコードは、手順を逆にして、同様の方法で行うことができます。


コードで更新します(私の口がある場所に足を置く、またはそのようなもの):

public class TequilaGuy {

   public static final char SPECIAL_CHAR = '@';

   public static byte[] encode(String s) {
      int special = (s.length() == 4) ? 1 : 0;
      int code = s.charAt(2 + special) - 'A' + (32 * (s.charAt(1 + special) - 'A' + 32 * (s.charAt(0 + special) - 'A' + 32 * special)));
      byte[] encoded = { (byte) ((code >>> 8) & 255), (byte) (code & 255) };
      return encoded;
   }

   public static String decode(byte[] b) {
      int code = 256 * ((b[0] < 0) ? (b[0] + 256) : b[0]) + ((b[1] < 0) ? (b[1] + 256) : b[1]);
      int special = (code >= 0x8000) ? 1 : 0;
      char[] chrs = { SPECIAL_CHAR, '\0', '\0', '\0' };
      for (int ptr=3; ptr>0; ptr--) {
         chrs[ptr] = (char) ('A' + (code & 31));
         code >>>= 5;
      }
      return (special == 1) ? String.valueOf(chrs) : String.valueOf(chrs, 1, 3);
   }

   public static void testEncode() {
      for (int spcl=0; spcl<2; spcl++) {
         for (char c1='A'; c1<='Z'; c1++) {
            for (char c2='A'; c2<='Z'; c2++) {
               for (char c3='A'; c3<='Z'; c3++) {
                  String s = ((spcl == 0) ? "" : String.valueOf(SPECIAL_CHAR)) + c1 + c2 + c3;
                  byte[] cod = encode(s);
                  String dec = decode(cod);
                  System.out.format("%4s : %02X%02X : %s\n", s, cod[0], cod[1], dec);
               }
            }
         }
      }
   }

   public static void main(String[] args) {
      testEncode();
   }

}
于 2009-12-09T14:27:31.673 に答える
1

アルファベットでは、出力の利用可能な 16 ビットのうち 15 ビットのみを使用します。したがって、特殊文字が固定されているため、文字列の長さが 4 の場合は、MSB (最上位ビット) を設定するだけで済みます。

もう 1 つのオプションは、変換テーブルを使用することです。すべての有効な文字で String を作成するだけです。

String valid = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ";

この文字列内の文字のインデックスは、出力のエンコーディングです。次に、2 つの配列を作成します。

byte encode[] = new byte[256];
char decode[] = new char[valid.length ()];
for (int i=0; i<valid.length(); i++) {
    char c = valid.charAt(i);
    encode[c] = i;
    decode[i] = c;
}

これで、配列内の各方向の値を検索し、好きな文字を任意の順序で追加できます。

于 2009-12-09T14:03:00.767 に答える
1

はい、以前の 3 文字の値のエンコードを維持しながら、余分な情報をエンコードすることは可能です。ただし、元のエンコーディングでは出力セットに自由な数字のきれいな帯が残らないため、その余分な文字を追加することによって導入された追加の文字列セットのマッピングは、少し不連続になるしかありません。

したがって、これらの不連続性をぎこちなく遅く処理するマッピング関数を考え出すのは難しいと思います。私は、テーブルベースのマッピングが唯一の適切なソリューションであると結論付けています。

私はあなたのマッピング コードを再設計するのが面倒だったので、それを私のテーブル初期化コードに組み込みました。これにより、翻訳エラーの多くの機会も排除されます:) あなたのencode()方法は私が呼ぶものOldEncoder.encode()です.

小さなテスト プログラムを実行しNewEncoder.encode()て、 と同じ値が得られることを確認しOldEncoder.encode()、さらに先頭の 4 番目の文字で文字列をエンコードできることを確認しました。NewEncoder.encode()文字が何であるかは気にしません。文字列の長さで決まります。の場合decode()、使用する文字は を使用して定義できますPREFIX_CHAR。また、接頭辞付き文字列のバイト配列値が、接頭辞なし文字列のバイト配列値と重複していないことも確認しました。そして最後に、エンコードされた接頭辞付き文字列は実際に同じ接頭辞付き文字列に戻すことができます。

package tequilaguy;


public class NewConverter {

   private static final String[] b2s = new String[0x10000];
   private static final int[] s2b = new int[0x10000];
   static { 
      createb2s();
      creates2b();
   }

   /**
    * Create the "byte to string" conversion table.
    */
   private static void createb2s() {
      // Fill 17576 elements of the array with b -> s equivalents.
      // index is the combined byte value of the old encode fn; 
      // value is the String (3 chars). 
      for (char a='A'; a<='Z'; a++) {
         for (char b='A'; b<='Z'; b++) {
            for (char c='A'; c<='Z'; c++) {
               String str = new String(new char[] { a, b, c});
               byte[] enc = OldConverter.encode(str);
               int index = ((enc[0] & 0xFF) << 8) | (enc[1] & 0xFF);
               b2s[index] = str;
               // int value = 676 * a + 26 * b + c - ((676 + 26 + 1) * 'A'); // 45695;
               // System.out.format("%s : %02X%02X = %04x / %04x %n", str, enc[0], enc[1], index, value);
            }
         }
      }
      // Fill 17576 elements of the array with b -> @s equivalents.
      // index is the next free (= not null) array index;
      // value = the String (@ + 3 chars)
      int freep = 0;
      for (char a='A'; a<='Z'; a++) {
         for (char b='A'; b<='Z'; b++) {
            for (char c='A'; c<='Z'; c++) {
               String str = "@" + new String(new char[] { a, b, c});
               while (b2s[freep] != null) freep++;
               b2s[freep] = str;
               // int value = 676 * a + 26 * b + c - ((676 + 26 + 1) * 'A') + (26 * 26 * 26);
               // System.out.format("%s : %02X%02X = %04x / %04x %n", str, 0, 0, freep, value);
            }
         }
      }
   }

   /**
    * Create the "string to byte" conversion table.
    * Done by inverting the "byte to string" table.
    */
   private static void creates2b() {
      for (int b=0; b<0x10000; b++) {
         String s = b2s[b];
         if (s != null) {
            int sval;
            if (s.length() == 3) {
               sval = 676 * s.charAt(0) + 26 * s.charAt(1) + s.charAt(2) - ((676 + 26 + 1) * 'A');
            } else {
               sval = 676 * s.charAt(1) + 26 * s.charAt(2) + s.charAt(3) - ((676 + 26 + 1) * 'A') + (26 * 26 * 26);
            }
            s2b[sval] = b;
         }
      }
   }

   public static byte[] encode(String str) {
      int sval;
      if (str.length() == 3) {
         sval = 676 * str.charAt(0) + 26 * str.charAt(1) + str.charAt(2) - ((676 + 26 + 1) * 'A');
      } else {
         sval = 676 * str.charAt(1) + 26 * str.charAt(2) + str.charAt(3) - ((676 + 26 + 1) * 'A') + (26 * 26 * 26);
      }
      int bval = s2b[sval];
      return new byte[] { (byte) (bval >> 8), (byte) (bval & 0xFF) };
   }

   public static String decode(byte[] b) {
      int bval = ((b[0] & 0xFF) << 8) | (b[1] & 0xFF);
      return b2s[bval];
   }

}

コード内にいくつかの複雑な定数式、特に 26 の累乗のものを残しました。それ以外の場合、コードは恐ろしく神秘的に見えます。コンパイラは Kleenex のようにそれらを折りたたむので、パフォーマンスを失うことなくそれらをそのままにしておくことができます。


アップデート:

クリスマスの恐怖が近づくにつれ、私はしばらく旅に出ます。この回答とコードが間に合うように見つかり、それを有効に活用できることを願っています。この取り組みをサポートするために、小さなテスト プログラムを投入します。直接チェックするのではなく、変換結果をすべての重要な方法で出力し、それらを目と手でチェックできるようにします。すべてが問題ないように見えるまで、コードをいじりました(基本的なアイデアを理解したら、小さな調整を加えました)。より機械的かつ徹底的にテストすることをお勧めします。

package tequilaguy;

public class ConverterHarness {

//   private static void runOldEncoder() {
//      for (char a='A'; a<='Z'; a++) {
//         for (char b='A'; b<='Z'; b++) {
//            for (char c='A'; c<='Z'; c++) {
//               String str = new String(new char[] { a, b, c});
//               byte[] enc = OldConverter.encode(str);
//               System.out.format("%s : %02X%02X%n", str, enc[0], enc[1]);
//            }
//         }
//      }
//   }

   private static void testNewConverter() {
      for (char a='A'; a<='Z'; a++) {
         for (char b='A'; b<='Z'; b++) {
            for (char c='A'; c<='Z'; c++) {
               String str = new String(new char[] { a, b, c});
               byte[] oldEnc = OldConverter.encode(str);
               byte[] newEnc = NewConverter.encode(str);
               byte[] newEnc2 = NewConverter.encode("@" + str);
               System.out.format("%s : %02X%02X %02X%02X %02X%02X %s %s %n", 
                     str, oldEnc[0], oldEnc[1], newEnc[0], newEnc[1], newEnc2[0], newEnc2[1],
                     NewConverter.decode(newEnc), NewConverter.decode(newEnc2));
            }
         }
      }
   }
   public static void main(String[] args) {
      testNewConverter();
   }

}
于 2009-12-22T23:25:58.130 に答える
0

「特殊文字」が修正されていて、4文字の文字列がこの特殊文字で始まることを常に認識している場合、文字自体は有用な情報を提供しません。

文字列の長さが3文字の場合は、前に行ったことを実行します。4文字の場合は、2番目の文字から始まる文字列のサブ文字列に対して古いアルゴリズムを実行します。

私は単純すぎますか、それとも一生懸命考えていますか?

于 2009-12-09T13:54:01.697 に答える
0

java.nio.charset.CharsetEncoderクラスを使用して文字をバイトに変換するだけであれば、これはずっと簡単です。ASCII 以外の文字でも機能します。String.getBytes同じ基本的な効果に対して、コードははるかに少なくなります。

于 2009-12-09T13:44:47.957 に答える