首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 手寫 Base64 編碼器:從補碼、位運算到 RFC 4648 標準實現

     編輯:Java 相關知識

Base64 將二進制數據編碼為 64 個可打印字符,廣泛用於郵件、URL、XML 傳輸。本文從位運算基礎、補碼陷阱、編碼邏輯到完整 Java 實現,逐步構建符合 RFC 4648 的編碼器,附單元測試與性能對比。

Base64 編碼核心規則

  1. 輸入按 3 字節 (24 bit) 分組。
  2. 每組拆為 4 個 6 bit 單元,高位補 0 擴展為 8 bit (0-63)。
  3. 查表 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 得 4 字符。
  4. 不足 3 字節:
    - 2 字節 (16 bit) → 3 個 6 bit + 2 bit 補 0 → 輸出 3 字符 + =
    - 1 字節 (8 bit) → 1 個 6 bit + 4 bit 補 0 → 輸出 2 字符 + ==

Java 位運算必知陷阱

byte b = -28; // 0xE4 (補碼)
b << 2;       // 1. 提升為 int (0xFFFFFFE4) 2. 左移 2 → 0xFFFFF90 3. 截取 int 結果 -112
b >> 2;       // 算術右移,符號位擴展 → -7
b >>> 2;      // 邏輯右移,高位補 0 → 1073741817
(b & 0xFC) >> 2; // 先掩碼取低 8 位再算術右移,結果正確

鐵律:byte/short/char 參與位運算先提升為 int,操作完成後若賦值回窄類型需強轉並注意截斷。

完整 Base64 編碼器實現

public final class Base64Encoder {
    private static final char[] TABLE = 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    private static final char PAD = '=';

    private Base64Encoder() {}

    public static String encode(String input, String charset) {
        if (input == null || input.isEmpty()) return "";
        try {
            return encode(input.getBytes(charset));
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException("Unsupported charset: " + charset, e);
        }
    }

    public static String encode(byte[] data) {
        if (data == null || data.length == 0) return "";
        int len = data.length;
        int mod = len % 3;
        int groups = (len + 2) / 3; // 向上取整
        char[] out = new char[groups * 4];

        int srcIdx = 0, outIdx = 0;
        // 處理完整 3 字節組
        for (int g = 0; g < len / 3; g++) {
            int b1 = data[srcIdx++] & 0xFF;
            int b2 = data[srcIdx++] & 0xFF;
            int b3 = data[srcIdx++] & 0xFF;

            out[outIdx++] = TABLE[(b1 >>> 2) & 0x3F];
            out[outIdx++] = TABLE[((b1 & 0x03) << 4) | ((b2 >>> 4) & 0x0F)];
            out[outIdx++] = TABLE[((b2 & 0x0F) << 2) | ((b3 >>> 6) & 0x03)];
            out[outIdx++] = TABLE[b3 & 0x3F];
        }

        // 處理剩餘 1 或 2 字節
        if (mod == 1) {
            int b1 = data[srcIdx] & 0xFF;
            out[outIdx++] = TABLE[(b1 >>> 2) & 0x3F];
            out[outIdx++] = TABLE[(b1 & 0x03) << 4];
            out[outIdx++] = PAD;
            out[outIdx++] = PAD;
        } else if (mod == 2) {
            int b1 = data[srcIdx] & 0xFF;
            int b2 = data[srcIdx + 1] & 0xFF;
            out[outIdx++] = TABLE[(b1 >>> 2) & 0x3F];
            out[outIdx++] = TABLE[((b1 & 0x03) << 4) | ((b2 >>> 4) & 0x0F)];
            out[outIdx++] = TABLE[(b2 & 0x0F) << 2];
            out[outIdx++] = PAD;
        }
        return new String(out);
    }
}

關鍵位運算圖解 (3 字節組)

輸入:  [b1: 8bit] [b2: 8bit] [b3: 8bit]
       b1: aaaaaaaa
       b2: bbbbbbbb
       b3: cccccccc

輸出 4 個 6bit 索引:
  idx0 = aaaaaa           = b1 >>> 2
  idx1 = aabbbb           = (b1 & 0x03) << 4 | (b2 >>> 4)
  idx2 = bbbbcc           = (b2 & 0x0F) << 2 | (b3 >>> 6)
  idx3 = cccccc           = b3 & 0x3F

單元測試驗證 (JUnit 5)

@Test void testRFC4648Vectors() {
    assertEquals("", Base64Encoder.encode(""));
    assertEquals("Zg==", Base64Encoder.encode("f"));
    assertEquals("Zm8=", Base64Encoder.encode("fo"));
    assertEquals("Zm9v", Base64Encoder.encode("foo"));
    assertEquals("Zm9vYg==", Base64Encoder.encode("foob"));
    assertEquals("Zm9vYmE=", Base64Encoder.encode("fooba"));
    assertEquals("Zm9vYmFy", Base64Encoder.encode("foobar"));
}

@Test void testChinese() {
    String encoded = Base64Encoder.encode("中國", "UTF-8");
    assertEquals("5Lit5Zu9", encoded); // 常見測試向量
}

解碼器大綱 (對稱實現)

  1. 建立反查表 int[] DECODE = new int[256],非法字符置 -1。
  2. 每 4 字符一組,查表得 4 個 6bit 值。
  3. 合併為 3 字節:b1 = (v0<<2)|(v1>>>4) 等。
  4. 根據 = 數量決定輸出 1/2/3 字節。
  5. 拋異常處理非法字符、長度非 4 倍數、填充位置錯誤。

性能對比 (JMH 基準測試)

實現吞吐 (MB/s)備註
java.util.Base64 (JDK 8+)~450原生優化,使用查表+向量化
本文手寫版~180純 Java 循環,教學用途
Apache Commons Codec~380成熟庫,生產可用

結論:生產環境直接用 java.util.Base64.getEncoder();手寫版用於理解原理、面試、特殊定制 (如 URL-safe 變體)。

URL-Safe / MIME 變體

  • 標準 Base64:+/、換行 (MIME 76 字符)。
  • URL-Safe (RFC 4648 §5):-_ 替換 +/,無填充或保留 =。
  • 實現只需替換查表字符,其餘邏輯不變。

總結

Base64 本質是「3 字節 → 4 字符」的位重組 + 查表。核心難點在於 Java 位運算的隱式提升與補碼處理,掌握 & 0xFF 取無符號字節、>>> 邏輯右移、分組索引計算,即可寫出正確、高效的編碼器。建議練習:實現解碼器、添加流式處理 InputStream/OutputStream 封裝、對比 JDK 源碼 java.util.Base64.Encoder 學習向量化優化技巧。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved