【问题标题】:Converting binary representation of integers to ASCII in Java Card在 Java Card 中将整数的二进制表示转换为 ASCII
【发布时间】:2019-02-20 08:07:30
【问题描述】:

我想将以二进制格式表示的任意长度的整数转换为 ASCII 格式。

一个例子是整数33023,十六进制字节是0x80ff。我想将0x80ff 表示为33023 的ASCII 格式,它的十六进制表示为0x3333303233

我正在开发一个无法识别 String 类型的 Java Card 环境,因此我必须通过二进制操作手动进行转换。

解决这个问题的最有效方法是什么,因为 16 位智能卡上的 Java Card 环境非常受限制。

【问题讨论】:

  • 如果你没有String,你至少有char数组和/或byte数组吗?
  • 基本原语只有byte、byte[]、int、int[]、short、short[]类型。
  • 太棒了! short[] 在紧要关头可以很好地替代 String;即使是byte[] 也适用于 ASCII。所以只要我们在shorts(或bytes)上同时有/(除法)和%(余数)运算符,我们就是黄金(;->)
  • 是的,你可以同时使用 / 和 % 。
  • intint[] 在 Java Card 平台上通常不受支持。指定数字格式时,您仅显示一个示例:0x80FF。这可能是一个无符号的大端格式,由两个字节组成,0x800xFF。您已完全指定输出格式,请同时提醒自己指明输入格式。

标签: java binary integer javacard


【解决方案1】:

这比您想象的要复杂,因为它需要基数转换,并且基数转换是在整个数字上执行的,使用大整数运算。

这当然并不意味着我们不能专门为此目的创建所述大整数运算的有效实现。这是一个用零填充的实现(通常在 Java Card 上需要)并且不使用额外的内存(!)。如果你想保留它,你可能必须复制大端数的原始值 - 输入值被覆盖。强烈建议将其放入 RAM。

此代码只是将字节除以新的基数(小数为 10),然后返回余数。余数是下一个最低位。由于输入值现在已被除,下一个余数是仅比前一个位置高一个位置的数字。它一直在除法并返回余数,直到值为零并且计算完成。

算法的棘手部分是内部循环,它将值除以 10,同时使用字节尾除法​​返回余数。它每次运行提供一个余数/十进制数字。这也意味着函数的顺序是 O(n) 其中 n 是结果中的位数(将尾除定义为单个操作)。请注意,n 可以通过ceil(bigNumBytes * log_10(256)) 计算:其结果也存在于预先计算的BCD_SIZE_PER_BYTES 表中。 log_10(256) 当然是一个恒定的十进制值,高于2.408

这是经过优化的最终代码(参见不同版本的编辑):

/**
 * Converts an unsigned big endian value within the buffer to the same value
 * stored using ASCII digits. The ASCII digits may be zero padded, depending
 * on the value within the buffer.
 * <p>
 * <strong>Warning:</strong> this method zeros the value in the buffer that
 * contains the original number. It is strongly recommended that the input
 * value is in fast transient memory as it will be overwritten multiple
 * times - until it is all zero.
 * </p>
 * <p>
 * <strong>Warning:</strong> this method fails if not enough bytes are
 * available in the output BCD buffer while destroying the input buffer.
 * </p>
 * <p>
 * <strong>Warning:</strong> the big endian number can only occupy 16 bytes
 * or less for this implementation.
 * </p>
 * 
 * @param uBigBuf
 *            the buffer containing the unsigned big endian number
 * @param uBigOff
 *            the offset of the unsigned big endian number in the buffer
 * @param uBigLen
 *            the length of the unsigned big endian number in the buffer
 * @param decBuf
 *            the buffer that is to receive the BCD encoded number
 * @param decOff
 *            the offset in the buffer to receive the BCD encoded number
 * @return decLen, the length in the buffer of the received BCD encoded
 *         number
 */
public static short toDecimalASCII(byte[] uBigBuf, short uBigOff,
        short uBigLen, byte[] decBuf, short decOff) {

    // variables required to perform long division by 10 over bytes
    // possible optimization: reuse remainder for dividend (yuk!)
    short dividend, division, remainder;

    // calculate stuff outside of loop
    final short uBigEnd = (short) (uBigOff + uBigLen);
    final short decDigits = BYTES_TO_DECIMAL_SIZE[uBigLen];

    // --- basically perform division by 10 in a loop, storing the remainder

    // traverse from right (least significant) to the left for the decimals
    for (short decIndex = (short) (decOff + decDigits - 1); decIndex >= decOff; decIndex--) {

        // --- the following code performs tail division by 10 over bytes

        // clear remainder at the start of the division
        remainder = 0;

        // traverse from left (most significant) to the right for the input
        for (short uBigIndex = uBigOff; uBigIndex < uBigEnd; uBigIndex++) {

            // get rest of previous result times 256 (bytes are base 256)
            // ... and add next positive byte value
            // optimization: doing shift by 8 positions instead of mul.
            dividend = (short) ((remainder << 8) + (uBigBuf[uBigIndex] & 0xFF));

            // do the division
            division = (short) (dividend / 10);

            // optimization: perform the modular calculation using
            // ... subtraction and multiplication
            // ... instead of calculating the remainder directly
            remainder = (short) (dividend - division * 10);

            // store the result in place for the next iteration
            uBigBuf[uBigIndex] = (byte) division;
        }
        // the remainder is what we were after
        // add '0' value to create ASCII digits
        decBuf[decIndex] = (byte) (remainder + '0');
    }

    return decDigits;
}

/*
 * pre-calculated array storing the number of decimal digits for big endian
 * encoded number with len bytes: ceil(len * log_10(256))
 */
private static final byte[] BYTES_TO_DECIMAL_SIZE = { 0, 3, 5, 8, 10, 13,
        15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39 };

要扩展输入大小,只需计算下一个十进制大小并将其存储在表中...

【讨论】:

  • 它适用于实际的 Java Card :) 。非常感谢您的帮助。
  • 不客气。添加了带有 cmets 的优化版本。删除了中间版本...再次:请在 JC 上测试!
猜你喜欢
  • 2016-10-02
  • 1970-01-01
  • 1970-01-01
  • 2019-02-03
  • 1970-01-01
  • 2014-08-16
  • 2013-02-12
  • 1970-01-01
  • 2014-03-24
相关资源
最近更新 更多