> 文章列表 > 有趣的 Kotlin 0x14:Base64编码

有趣的 Kotlin 0x14:Base64编码

有趣的 Kotlin 0x14:Base64编码

前言

Concise. Cross‑platform. Fun.

Kotlin 来到 1.8.20 版本, 又给开发者带来了很多更新, 今天关注下标准库中新增的 Base64 相关内容.

原理

Base64编码是一种将二进制数据转换为可打印ASCII字符的编码方式。它使用64个不同的字符(通常是A-Z、a-z、0-9和两个额外的字符 “+” 和 “/”)来表示二进制数据中的6位,从而将8位的二进制数据转换为6位的ASCII字符。

Base64编码的原理如下:

  1. 将输入的二进制数据分割成每6位一组的数据块。
  2. 每个6位的数据块表示一个整数值,范围从0到63(因为2^6=64)。
  3. 将这个整数值映射到Base64字符集中的对应字符。例如,整数值为0时对应字符集中的第一个字符(通常是"A"),整数值为1时对应字符集中的第二个字符(通常是"B"),以此类推。
  4. 对于不足6位的数据块,可以进行填充,通常使用字符 “=” 来填充。
  5. 将所有的Base64字符连接在一起,形成最终的Base64编码字符串。

解码时,按照相反的过程进行:

  1. 将Base64编码的字符串按照字符集映射关系,还原成对应的整数值。
  2. 将整数值转换为6位二进制数据。
  3. 将多个6位二进制数据组合成完整的二进制数据。
  4. 最后,根据具体的编码前的二进制数据类型,将二进制数据转换为相应的数据类型,如字符串、图片、音频等。

Base64编码常用于将二进制数据传输或储存于文本协议中,例如在电子邮件中传输二进制附件、在URL中传递二进制参数、在JSON或XML等文本格式中嵌入二进制数据等场景。

常见三种编码方案

Base64编码通常有几种不同的编码方案,也称为编码表或字符集。这些编码方案包括:

  1. 标准Base64:也称为RFC 4648中定义的Base64编码,它使用64个字符作为编码表,包括大小写字母(A-Z, a-z)、数字(0-9)以及两个特殊字符(+和/)。此外,还可能包含一个填充字符(=),用于将编码结果的长度补齐为4的倍数。

                          Table 1: The Base 64 AlphabetValue Encoding  Value Encoding  Value Encoding  Value Encoding0 A            17 R            34 i            51 z1 B            18 S            35 j            52 02 C            19 T            36 k            53 13 D            20 U            37 l            54 24 E            21 V            38 m            55 35 F            22 W            39 n            56 46 G            23 X            40 o            57 57 H            24 Y            41 p            58 68 I            25 Z            42 q            59 79 J            26 a            43 r            60 810 K            27 b            44 s            61 911 L            28 c            45 t            62 +12 M            29 d            46 u            63 /13 N            30 e            47 v14 O            31 f            48 w         (pad) =15 P            32 g            49 x16 Q            33 h            50 y
    
  2. URL安全Base64:这种编码方案在标准Base64的基础上进行了修改,用于在URL和文件名中传输数据,避免了一些特殊字符在URL中可能引起的问题。URL安全Base64使用大小写字母(A-Z, a-z)、数字(0-9)以及两个特殊字符(-和_)作为编码表,不使用+和/作为编码字符,也不使用=作为填充字符。

    Table 2: The "URL and Filename safe" Base 64 AlphabetValue Encoding  Value Encoding  Value Encoding  Value Encoding0 A            17 R            34 i            51 z1 B            18 S            35 j            52 02 C            19 T            36 k            53 13 D            20 U            37 l            54 24 E            21 V            38 m            55 35 F            22 W            39 n            56 46 G            23 X            40 o            57 57 H            24 Y            41 p            58 68 I            25 Z            42 q            59 79 J            26 a            43 r            60 810 K            27 b            44 s            61 911 L            28 c            45 t            62 - (minus)12 M            29 d            46 u            63 _13 N            30 e            47 v           (underline)14 O            31 f            48 w15 P            32 g            49 x16 Q            33 h            50 y         (pad) =
    
  3. MIME Base64:这种编码方案通常用于在邮件中传输二进制数据。MIME Base64与标准Base64编码方式相同,但可能会在编码结果的行末添加换行符,以适应邮件传输的需求。

这些不同的Base64编码方案在编码表和字符集上有所不同,但基本的编码原理和解码方式都是相同的。在使用Base64编码时,需要根据具体的应用场景和需求选择合适的编码方案。例如,在传输数据时,如果数据将用于URL或文件名中,应使用URL安全Base64编码;如果数据将用于电子邮件中,应使用MIME Base64编码。

Kotlin 手写

根据规则先自行实现 Base64 编解码.

编码

val BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
// 以参数为"Hello, World!"为示例
fun base64encode(source: String): String {val originalBytes = source.toByteArray()// originalBytes = [72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33]val binaryStrings = originalBytes.map { byte -> byte.toString(2).padStart(8, '0') }// binaryStrings = ["01001000", "01100101", "01101100", "01101100", "01101111", "00101100", "00100000", "01010111", "01101111", "01110010", "01101100", "01100100", "00100001"]val binaryString = binaryStrings.joinToString("")// binaryString = "0100100001100101011011000110110001101100011011110010110000100000010101110110111101110010011011000110110001100100"val chunks = binaryString.chunked(6) { chunk -> chunk.padEnd(6, '0') }// chunks = ["010010", "000110", "010110", "110011", "000110", "110000", "110111", "001011", "000010", "101110", "110111", "001110", "010011", "011000", "110110", "001100"]val encodedChars = chunks.map { chunk -> chunk.toString().toInt(2) }.map { index -> BASE64_CHARS[index] }.toCharArray()// encodedChars = ['S', 'G', 'V', 's', 'b', 'i', 'B', '0', 'I', 'F', 'c', 'b', 'G', '9', 'v', '4', 'E', 'h', 'd', 'A']return addPadding(String(encodedChars))// encodedString = "SGVsbG8sIFdvcmxkIQ=="
}// 补成4的倍数
fun addPadding(input: String): String {val remainder = input.length % 4return if (remainder == 0) {input} else {val padding = "=".repeat(4 - remainder)input + padding}
}

解码

fun base64decode(encodedString: String): String {val encodedChars = encodedString.toCharArray()val chunks = encodedChars.map { char -> BASE64_CHARS.indexOf(char) }.filter { index -> index != -1 }.joinToString("") { index -> index.toString(2).padStart(6, '0') }.chunked(8).filter { it.length == 8 }val binaryBytes = chunks.map { chunk ->chunk.toInt(2).toByte()}.toByteArray()return String(binaryBytes)
}

Kotlin 1.8.20 标准库

编码

val foBytes = "fo".map { it.code.toByte() }.toByteArray()
println(Base64.Default.encode(foBytes)) // "Zm8="
println(Base64.encode(foBytes)) // "Zm8="val foobarBytes = "foobar".map { it.code.toByte() }.toByteArray()
println(Base64.UrlSafe.encode(foobarBytes)) // "Zm9vYmFy"

解码

println(String(Base64.Default.decode("Zm8=")))// foBytes
println(String(Base64.decode("Zm8=")))
println(String(Base64.UrlSafe.decode("Zm9vYmFy"))) // foobarBytes

后记

https://www.rfc-editor.org/rfc/rfc4648

除了 Base64 , 还有 Base32、Base16 等各种 Base-N 编码规则, 原理类似可举一反三自行贯通. 看官方文档的时候, 还顺便帮他们改了个小错误.
在这里插入图片描述