문제

3 문자열 (항상 알파벳)을 2 바이트 배열로 인코딩해야했습니다. 이것은 공간과 성능 이유를 절약하기 위해 수행되어야했습니다.

이제 요구 사항이 약간 변경되었습니다. 문자열은 길이가 변합니다. 길이 3 (위와 같이)이거나 길이 4이고 처음에는 1 개의 특수 문자가 있습니다. 우리가 @가 항상 @이고 항상 처음에 선택하면 특수 문자가 고정되어 있습니다. 그래서 우리는 줄의 길이가 3이면 알파벳 만 있고 길이가 4 인 경우 첫 번째 문자는 항상 '@', 3 개의 알파벳이됩니다.

그래서 사용할 수 있습니다

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

대신에

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

여전히 3 또는 4 숯을 2 바이트 어레이로 인코딩하여 다시 해독 할 수 있습니까? 그렇다면 어떻게?

도움이 되었습니까?

해결책

예, 그것 ~이다 3 자 값에 대한 이전 인코딩을 유지하면서 추가 정보를 인코딩 할 수 있습니다. 그러나 원래 인코딩은 출력 세트에 무료 숫자의 깨끗한 waths를 남기지 않기 때문에 추가 문자가 도움이 될 수는 없지만 조금 불연속적이지 않다고 추가하여 소개 된 추가 문자열 세트를 매핑합니다.

따라서, 나는 어색하고 느리고 느리게 이러한 불연속성을 처리하는 매핑 함수를 생각해 내기가 어렵다고 생각합니다. 테이블 기반 매핑이 유일한 제정신 솔루션이라고 결론을 내립니다.

매핑 코드를 다시 엔지니어링하기에는 너무 게으르기 때문에 테이블 초기화 코드에 통합했습니다. 이것은 또한 번역 오류에 대한 많은 기회를 제거합니다 :) 귀하의 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];
   }

}

나는 코드에 몇 가지 복잡한 표현을 남겼습니다. 코드는 그렇지 않으면 끔찍하게 신비하게 보입니다. 컴파일러가 Kleenexes처럼 접힌 것처럼 성능을 잃지 않고 그대로 두는 것입니다.


업데이트:

X-Mas의 공포가 다가 오면서 잠시 동안 길을 갈 것입니다. 이 답변과 코드를 제 시간에 찾아서 잘 활용하기를 바랍니다. 어떤 노력을 지원하기 위해, 나는 나의 작은 시험 프로그램을 던질 것이다. 그것은 직접적인 것을 확인하지는 않지만 모든 중요한 방식으로 변환 결과를 인쇄하고 눈과 손으로 확인할 수 있습니다. 나는 모든 것이 잘 보일 때까지 내 코드 (기본 아이디어를 얻었을 때 작은 조정)를 피했습니다. 더 기계적으로나 철저하게 테스트하고 싶을 수도 있습니다.

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

}

다른 팁

직접 답이 아니라 인코딩을 수행하는 방법은 다음과 같습니다.

   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 비트를 정수로 조립합니다. 입력 숯이 범위를 벗어나면 끔찍하게 실패합니다.

두 번째 줄은 정수의 선두 및 후행 바이트에서 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();
   }

}

알파벳에서는 출력의 16 개 사용 비트 중 15 개만 사용합니다. 따라서 특수 숯이 고정되어 있기 때문에 문자열이 길이 4 인 경우 MSB (가장 중요한 비트)를 설정할 수 있습니다.

다른 옵션은 번역 테이블을 사용하는 것입니다. 모든 유효한 문자로 문자열을 만듭니다.

String valid = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ";

이 문자열의 문자 색인은 출력의 인코딩입니다. 이제 두 개의 배열을 만듭니다.

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

이제 배열에서 각 방향의 값을 조회하고 원하는 문자를 순서대로 추가 할 수 있습니다.

방금 사용하면 이것을 훨씬 쉽게 알 수 있습니다. java.nio.charset.CharsetEncoder 문자를 바이트로 변환하는 클래스. ASCII 이외의 캐릭터에도 효과가 있습니다. 조차 String.getBytes 동일한 기본 효과에 대한 코드가 훨씬 적습니다.

"특별한 숯"이 고정되어 있고 항상 4 자 문자열 이이 특수 문자로 시작한다는 것을 알고 있다면, 숯 자체는 유용한 정보를 제공하지 않습니다.

문자열의 길이가 3자인 경우 이전에 한 일을하십시오. 4자인 경우 두 번째 문자부터 시작하여 문자열의 하위 문자열에서 기존 알고리즘을 실행하십시오.

나도 간단하게 생각하고 있습니까? 아니면 너무 열심히 생각하고 있습니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top