Domanda

avevo un requisito di codifica una stringa di 3 caratteri (sempre alfabeti) in 2 byte [] array di 2 numeri interi. Questo doveva essere fatto per salvare motivi di spazio e di prestazioni.

Ora il requisito è cambiato un po '. La stringa sarà di lunghezza variabile. Questo viene eventualmente di lunghezza 3 (come è sopra) o sarà di lunghezza 4 ed avrà 1 carattere speciale all'inizio. Il carattere speciale è fissato vale a dire se scegliamo @ sarà sempre @ e sempre all'inizio. Così siamo sicuri che se la lunghezza della stringa è 3, si avrà solo alfabeti e se la lunghezza è 4, il primo carattere sarà sempre '@' seguito da 3 alfabeti

Così posso usare

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

anziché

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

Posso ancora codificare i 3 o 4 caratteri a 2 byte e li decodifica di nuovo? Se sì, come?

È stato utile?

Soluzione

Si, è possibile codificare un extra po 'di informazioni, pur mantenendo la codifica precedente per 3 valori di carattere. Ma dal momento che la codifica originale non lascia belle strisce pulite di numeri liberi nel set di uscita, la mappatura del set supplementare di stringhe introdotte con l'aggiunta di quel personaggio in più non può fare a meno di essere un po 'discontinuo.

Di conseguenza, ritengo che sarebbe difficile trovare con funzioni di mappatura che gestiscono queste discontinuità senza essere sia scomodo e lento. Concludo che una mappatura basato su tabelle è l'unica soluzione sensata.

ero troppo pigro per riprogettare il vostro codice di mappatura, così ho incorporato nel codice di inizializzazione tabella di mine; questo elimina anche molte opportunità per errori di traduzione :) Il tuo metodo encode() è quello che io chiamo OldEncoder.encode().

Ho eseguito un piccolo programma di test per verificare che NewEncoder.encode() si presenta con gli stessi valori di OldEncoder.encode(), ed è inoltre in grado di codificare le stringhe con una primaria 4 ° carattere. NewEncoder.encode() non importa quello che il personaggio è, va in base alla lunghezza della stringa; per decode(), il carattere utilizzato può essere definita utilizzando PREFIX_CHAR. Ho anche controllato bulbo oculare che i valori array di byte per Archi prefissati non duplicare qualsiasi di quelle per le stringhe non prefissati; e, infine, quello codificato prefisso stringhe possono infatti essere riconvertito le stesse stringhe prefissati.

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

}

Ho lasciato un paio di espressioni costanti intricati nel codice, in particolare la roba poteri-del-26. Il codice è terribilmente misteriosa altrimenti. Puoi lasciare quelli così come sono, senza perdere le prestazioni, come il compilatore li piega come Kleenex.


Aggiornamento:

Mentre l'orrore di approcci X-mas, sarò in viaggio per un po '. Spero che troverete questa risposta e il codice in tempo per fare buon uso di esso. A sostegno della quale sforzo mi butto nel mio piccolo programma di test. Esso non controlla direttamente roba, ma stampa i risultati di conversioni in tutti i modi significativi e consente di controllare loro occhio e la mano. I giocherellava con il mio codice (piccole modifiche una volta ho avuto l'idea di base verso il basso) fino a quando tutto sembrava OK lì. Si consiglia di testare più meccanicamente e in modo esauriente.

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

}

Altri suggerimenti

Non direttamente una risposta, ma ecco come avrei fatto la codifica:

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

La prima riga utilizza schema di Horner per assemblare aritmeticamente 5 bit di ciascun carattere in un numero intero. Fallirà orribilmente se uno dei tuoi caratteri di ingresso rientrano nell'intervallo [A-`].

La seconda riga assembla una matrice di 2 byte dalla parte anteriore e posteriore byte del numero intero.

La decodifica potrebbe essere fatto in un modo simile, con i passaggi invertiti.


Aggiorna con il codice (mettere il mio piede dove la mia bocca è, o qualcosa di simile):

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

}

Nel vostro alfabeto, si utilizza solo il 15 dei 16 bit disponibili dell'uscita. Quindi, si può solo impostare il MSB (bit più significativo) se la stringa è di lunghezza 4 in quanto il carattere speciale è fisso.

L'altra opzione è quella di utilizzare una tabella di traduzione. Basta creare una stringa con tutti i caratteri validi:

String valid = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ";

L'indice di un carattere di questa stringa rappresenta la codifica in uscita. Ora creare due array:

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

Ora si può cercare i valori per ciascuna direzione negli array e aggiungere qualsiasi carattere che ti piace in qualsiasi ordine.

Si potrebbe trovare questo molto più facile se hai appena usato la classe java.nio.charset.CharsetEncoder per convertire i tuoi personaggi per byte. Sarebbe anche lavorare per caratteri diversi da ASCII. Anche String.getBytes sarebbe molto meno codice per lo stesso effetto di base.

Se il "carattere speciale" è fisso e sei sempre consapevole del fatto che una stringa di 4 caratteri inizia con questo carattere speciale, quindi il carattere stesso fornisce alcuna informazione utile.

se la stringa è di 3 caratteri di lunghezza, poi fare quello che hai fatto prima; se si tratta di 4 caratteri, eseguire il vecchio algoritmo su sottostringa della stringa che inizia con il 2 ° carattere.

sto pensando troppo semplice o stai pensando troppo duro?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top