Frage

hatte ich eine Anforderung zum Codieren eines 3 Zeichenkette (immer Alphabete) in ein 2 byte [] Array von 2 ganze Zahlen sind. Das war getan werden, um Platz und Performance-Gründe zu speichern.

Nun hat die Forderung ein wenig verändert. Der String wird mit variabler Länge sein. Es wird entweder die Länge 3 (wie es oben ist) oder wird bei Beginn der Länge 4 und muss 1 Sonderzeichen sein. Das Sonderzeichen ist fest das heißt, wenn wir wählen @ es immer @ sein wird und immer am Anfang. So sind wir sicher, dass, wenn die Länge von String 3 ist, wird es nur Alphabete hat und wenn die Länge 4 ist das erste Zeichen wird immer ‚@‘, gefolgt von 3 Alphabeten

So kann ich

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

statt

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

Kann ich noch die 3 oder 4 Zeichen zu 2-Byte-Array kodieren und dekodieren sie zurück? Wenn ja, wie?

War es hilfreich?

Lösung

Ja, es ist möglich, ein zusätzliches Bit an Information zu codieren, während die vorherige Codierung für 3 Zeichenwerte beibehalten. Aber da Ihre ursprüngliche Codierung nicht verläßt schön sauber Schwaden von freien Nummern in dem Ausgabesatz, Abbildung des zusätzlichen Satzes von eingeführt Strings durch fügen hinzu, dass zusätzliche Zeichen nicht helfen können, aber ein wenig unterbrochen sein.

Daher denke ich, dass es schwer sein würde, mit Mapping-Funktionen zu entwickeln, die diese Diskontinuitäten behandeln, ohne beide umständlich und langsam ist. Ich schließe daraus, dass eine tabellenbasierte Mapping die einzige vernünftige Lösung ist.

Ich war zu faul, um Ihre Mapping-Code Re-Engineering, so dass ich es in die Tabelle Initialisierungscode von mir eingebaut ist; dies viele Möglichkeiten für Übersetzungsfehler :) Ihre encode() Methode beseitigt auch nenne ich OldEncoder.encode().

Ich habe ein kleines Testprogramm läuft, dass NewEncoder.encode() kommt mit den gleichen Werten wie OldEncoder.encode(), um zu überprüfen, und ist dazu in der Lage Strings mit einem führenden 4.en Zeichen zu kodieren. NewEncoder.encode() kümmert sich nicht darum, was der Charakter ist, geht es durch Stringlänge; für decode() kann das verwendete Zeichen mit PREFIX_CHAR definiert werden. Ich habe Augapfel auch überprüft, dass die Byte-Array-Werte für gelegten Strings nicht irgendwelche von denen Duplikat für Nicht-Präfix Strings; und schließlich, dass der Präfix kodierten Strings kann in der Tat zurück zu dem gleichen Präfix Strings umgewandelt werden.

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];
   }

}

Ich habe ein paar komplizierten konstanten Ausdrücke im Code links, vor allem der Kräfte-of-26 Sachen. Der Code sieht schrecklich sonst rätselhaft. Sie können diejenigen lassen, wie sie sind, ohne Leistung zu verlieren, wie der Compiler sie faltet sich wie Kleenexes auf.


Update:

Wie die Schrecken des X-mas Ansätze, ich werde für eine Weile auf der Straße sein. Ich hoffe, dass Sie diese Antwort und Code in der Zeit finden, um guten Gebrauch davon zu machen. Deren Unterstützung Anstrengung werde ich in meinem kleinen Testprogramm werfen. Es ist nicht direkt Sachen zu überprüfen, aber die Ergebnisse der Conversions in allen wichtigen Punkten druckt und ermöglicht es Ihnen, sie von Auge und Hand zu überprüfen. Ich spielte mit meinem Code (kleine Verbesserungen, sobald ich die Grundidee nach unten bekam), bis alles in Ordnung dort aussah. Sie möchten mehr mechanisch und erschöpfend testen.

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();
   }

}

Andere Tipps

nicht direkt eine Antwort, aber hier ist, wie ich würde die Codierung tun:

   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;
   }

Die erste Zeile verwendet Horner-Schema des arithmetisch in eine Ganzzahl 5 Bits jedes Zeichens zu montieren. Es wird nicht schrecklich, wenn alle Ihre Eingabe Zeichen außerhalb des Bereichs fallen [A-`].

Die zweite Zeile assembliert einen 2-Byte-Array von der Vorder- und den Hinter Byte der Ganzzahl.

Die Decodierung in ähnlicher Weise durchgeführt werden kann, mit den Schritten rückgängig gemacht.


UPDATE mit dem Code (Putting meinen Fuß, wo mein Mund ist, oder so ähnlich):

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();
   }

}

In Ihrem Alphabet verwenden Sie nur 15 der 16 verfügbaren Bits des Ausgangs. So konnte man nur das MSB (höchstwertige Bit), wenn die Zeichenfolge der Länge 4, da das spezielle Zeichen festgelegt ist.

Die andere Option ist eine Übersetzungstabelle zu verwenden. Erstellen Sie einfach einen String mit allen gültigen Zeichen:

String valid = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ";

Der Index eines Zeichens in dieser Zeichenfolge ist die Kodierung in der Ausgabe. Erstellen Sie nun zwei Arrays:

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;
}

Jetzt können Sie die Werte Nachschlag für jede Richtung in den Arrays und fügen Sie alle Zeichen, die in beliebiger Reihenfolge mögen.

würden Sie finden diese viel einfacher, wenn Sie nur die java.nio.charset.CharsetEncoder Klasse verwendet, um Ihre Charaktere zu Bytes zu konvertieren. Es wäre auch für andere Zeichen als ASCII arbeiten. Auch würde String.getBytes viel weniger Code auf den gleichen grundlegenden Effekt sein.

Wenn die „special char“ festgelegt ist, und Sie sind immer bewusst, dass ein 4-stelliges String beginnt mit diesem speziellen Zeichen, dann die Zeichen selbst liefert keine nützlichen Informationen.

Wenn der String 3 Zeichen lang, dann tun, was Sie zuvor; wenn es 4 Zeichen ist, den alten Algorithmus auf der Teilzeichenstring läuft mit dem zweiten Zeichen beginnen.

Bin ich zu einfach zu denken oder denken Sie zu hart?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top