سؤال

كان لدي شرط لترميز سلسلة الأحرف 3 (الحروف الهجائية دائما) في صفيف 2 بايت [] من الأعداد الصحيحة 2. كان يجب القيام بذلك لتوفير الأسباب المساحة والأداء.

الآن قد تغيرت الشرط قليلا. ستكون السلسلة ذات طول متغير. سيكون إما طوله 3 (كما هو موضح أعلاه) أو سيكون طوله 4 وسيكون له شخصية خاصة في البداية. تم إصلاح الشخصية الخاصة أي إذا اخترنا @ سيكون دائما @ ودائما في البداية. لذلك نحن متأكدون من أنه إذا كان طول السلسلة 3، فسيحتوي عليه الحروف الهجائية فقط وإذا كان الطول هو 4، فإن الطابع الأول سيكون دائما "@" تليها 3 حروف تحظى

حتى أتمكن من استخدام

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

بدلا من

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

هل لا يزال بإمكاني أن أشعر بترميز الأحرف الثلاثة أو 4 إلى صفيف بايت وشفوضها إذا كان الأمر كذلك، كيف؟

هل كانت مفيدة؟

المحلول

نعم انها يكون من الممكن ترميز جزء إضافي من المعلومات مع الحفاظ على الترميز السابق لقيم الأحرف 3. ولكن نظرا لأن الترميز الأصلي الخاص بك لا يغادر مسافات نظيفة لطيفة من الأرقام المجانية في مجموعة الإخراج، فإن تعيين مجموعة إضافية من السلاسل المقدمة عن طريق إضافة تلك الشخصية الإضافية لا يمكن أن تساعد ولكن تكون غير متسقة قليلا.

وفقا لذلك، أعتقد أنه سيكون من الصعب التوصل إلى وظائف رسم الخرائط التي تتعامل مع هذه الأقطار دون أن تكون محرجة وبطيئة. أختتم إلى أن التعيين القائم على الطاولة هو الحل العاقل الوحيد.

كنت كسول للغاية لإعادة مهندس رمز رسم الخرائط الخاص بك، لذلك أدرجت ذلك في رمز تهيئة الجدول لي؛ هذا أيضا يلغي العديد من الفرص لأخطاء الترجمة :) الخاص بك encode() الطريقة هي ما أسميه OldEncoder.encode().

لقد قمت بتشغيل برنامج اختبار صغير للتحقق من ذلك NewEncoder.encode() يأتي مع نفس القيم كما OldEncoder.encode(), ، بالإضافة إلى ذلك قادرة على تشفير السلاسل ذات الشخصية الرابعة الرائدة. 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. يبدو الرمز غامضا بشكل فظيع خلاف ذلك. يمكنك ترك تلك لأنها دون خسارة الأداء، حيث أن المترجم يطوي لهم مثل kleenexes.


تحديث:

كمرور من نهج X-MAS، سأكون على الطريق لفترة من الوقت. آمل أن تجد هذه الإجابة ورمز في الوقت المناسب للاستفادة من ذلك. لدعم الجهد الذي سأرمي في برنامج الاختبار الصغير. لا تحقق من الأشياء مباشرة ولكن طباعت نتائج التحويلات بجميع الطرق الهامة وتتيح لك التحقق منها بالعين واليد. أنا fiddled مع التعليمات البرمجية (القرص الصغيرة بمجرد أن حصلت على الفكرة الأساسية لأسفل) حتى بدا كل شيء موافق هناك. قد ترغب في اختبار المزيد من ميكانيكيا وممتازة.

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

يستخدم الخط الأول مخطط هورنر لتجميع 5 بت من كل حرف في عدد صحيح. ستفشل بشكل فظيع إذا سقطت أي من أجراه المدخلات خارج النطاق [A-`].

يتجمع السطر الثاني مجموعة 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();
   }

}

في الحروف الأبجدية الخاصة بك، تستخدم 15 فقط من 16 بت متاحا من الإخراج. لذلك يمكنك فقط تعيين MSB (الأهميات الأكثر أهمية) إذا كانت السلسلة بطول 4 لأن السحر الخاص ثابت.

الخيار الآخر هو استخدام جدول الترجمة. ما عليك سوى إنشاء سلسلة مع جميع الأحرف الصحيحة:

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 سيكون أقل رمز كثيرا لنفس التأثير الأساسي.

إذا تم إصلاح "Char Special Char" وأنت تدرك دائما أن سلسلة 4 أحرف تبدأ في هذا السحر الخاص، فستوفر Char نفسه معلومات مفيدة.

إذا كانت السلسلة بطول 3 أحرف، فقم بذلك ما فعلته من قبل؛ إذا كانت 4 أحرف، قم بتشغيل الخوارزمية القديمة على فرعية السلسلة التي تبدأ بالحرف الثاني.

هل أفكر أيضا ببساطة أم أنك تفكر بشدة؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top