Base64 readme ========================================
Base64 是比较常用的一种能将二进位数据以可视字符串表达出的编码方式, 使用了 64 个可见字符来编码, 每个字节只利用了 6bits 信息, 即 2^6=64 种状态对应 64 个可见字符. 二进制转到字符称为编码, 由字符转换到二进制称为解码. 转换时, 3 个二进位字节分拆成四组 6bits 的数据, 按取值索引找到码表对应的字符即可, 当数据最后一组不足 3 字节, 就使用 padding 填充, 确保转换后的 Base64 编码数量是 4 字节的整数倍. Base64 不只一种编码方案, 基础的方案中使用了除号, 编码后的内容不能用于文件名. 在 URL 中,+/= 三个符号要对应转换成 + / =, 这会占用有长度要求 URL. 所以, 后来推出有兼容 URL 与文件名的编码方案 Base64url, 这个方案中使用了 - 和 _ 替换了基础方案中使用的 + 号和 / 号, 对于 = 这个符号的处理, 有些实现会省略, 有些则以圆点替换.
除 Base64 外, 还有 Base16 即 Hex 十六进编码也是使用较多的一种, 这种编码刚好用两个字节编码一个二位字节数据.
Base64 编码 - https://en.wikipedia.org/wiki/Base64
- base64url in RFC 4648 - https://tools.ietf.org/html/rfc4648
- Base32 - RFC 4648 alphabet - https://en.wikipedia.org/wiki/Base32
MIME 编码 - https://en.wikipedia.org/wiki/MIME
- Base16 Hexadecimal - https://en.wikipedia.org/wiki/Hexadecimal
- (code point +:43 /:47 0:48 =:61 A:65 a:97)
Java 的 Base64 工具类提供了一套静态方法获取下面三种 BASE64 编解码器:
static Base64.Decoder getDecoder() 解码使用基本型 base64 编码方案.
static Base64.Encoder getEncoder() 编码使用基本型 base64 编码方案.
static Base64.Decoder getMimeDecoder() 解码使用 MIME 型 base64 编码方案.
static Base64.Encoder getMimeEncoder() 编码使用 MIME 型 base64 编码方案.
static Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) 可以指定每行的长度及行的分隔符.
static Base64.Decoder getUrlDecoder() 解码使用 URL 和文件名安全型 base64 编码方案.
static Base64.Encoder getUrlEncoder() 编码使用 URL 和文件名安全型 base64 编码方案.
基本: 输出被映射到一组字符 A-Za-z0-9+/, 编码不添加任何行标, 输出的解码仅支持 A-Za-z0-9+/.
URL: 输出映射到一组字符 A-Za-z0-9+_
MIME: 输出隐射到 MIME 友好格式. 输出每行不超过 76 字符, 并且使用 \r\n 作为分割. 编码输出最后没有行分割.
Base64 的 Java 语言实现
http://www.runoob.com/java/java8-base64.html
第三方实现 Base64 的 API 有 Apache Commons Codec library 的 org.apache.commons.codec.binary.Base64, 还有 Google Guava 库里的 com.google.common.io.BaseEncoding.base64() 这个静态方法. 最后一个, 号称 Base64 编码速度最快的 MigBase64 而且是 10 年前的实现.
这里贴的是坚果的实现, 好久不写 Java, 找个练习题做做以免荒废了:
- import java.util.Base64;
- import java.nio.charset.Charset;
- public class coding {
- static public void main(String args[]) throws Exception {
- String a = new String("Base64 的 Java 语言实现");
- String cp = Charset.defaultCharset().name();
- log("Default CodePage"+cp);
- String b = Base64.getEncoder().encodeToString(a.getBytes());
- String c = new String( Base64.getDecoder().decode(b) );
- String d = Basee64encode(a.getBytes());
- String e = new String(Basee64decode(d));
- log("encode =>"+a+"=>"+b+"=="+d+"?"+(b.equals(d)?"PASS":"FAIL"));
- log("decode =>"+c+"=="+e+"?"+(c.equals(e)?"PASS":"FAIL"));
- }
- static public String Basee64encode(byte[] bin){
- int i = bin.length%3;
- int g = bin.length - i;
- StringBuffer s = new StringBuffer();
- String fix = new String();
- if( i==1 ){
- int b = (0x3f & (bin[g]<<4));
- fix += map64(0x3F & bin[g]>>2)+""+map64(b)+"==";
- }else if( i==2 ){
- int b = (0x03 & bin[g])<<4 | (0xf0 & bin[g+1])>>4;
- int c = (0x0f & bin[g+1])<<2; // !!! 最后四 bits 移到最高位
- fix += map64(0x3f & bin[g]>>2) +""+ map64(b) +""+ map64(c)+'=';
- }
- for (i=0; i<g; i+=3 ) {
- int a = (0xff & bin[i] )>>2; // bigger first
- int b = (0x03 & bin[i] )<<4 | (0xF0 & bin[i+1])>>4;
- int c = (0x0f & bin[i+1])<<2 | (0xC0 & bin[i+2])>>6;
- int d = (0x3f & bin[i+2]);
- s.append(map64(a));
- s.append(map64(b));
- s.append(map64(c));
- s.append(map64(d));
- }
- return s.toString()+fix;
- }
- static public byte[] Basee64decode(String msg){
- byte[] bytes = msg.getBytes();
- byte[] res = new byte[bytes.length*3/4];
- for(int i=0; i<bytes.length; i+=4){
- byte a = unmap64(bytes[i]);
- byte b = unmap64(bytes[i+1]);
- byte c = unmap64(bytes[i+2]);
- byte d = unmap64(bytes[i+3]);
- res[i*3/4+0] = (byte)(a<<2 | b>>4);
- res[i*3/4+1] = (byte)(b<<4 | c>>2);
- res[i*3/4+2] = (byte)(c<<6 | d);
- }
- int l = bytes.length;
- int pad = bytes[l-2]=='='? 2:bytes[l-1]=='='? 1:0;
- if( pad>0 ){
- byte[] ret = new byte[res.length-pad];
- System.arraycopy(res, 0, ret, 0, res.length-pad);
- return ret;
- }
- return res;
- }
- static public char map64(int i){
- // +:43 /:47 0:48 =:61 A:65 a:97
- if( i>63 ) return '=';
- byte code = (byte)(i==62?'+':i==63?'/':i<26?'A'+i:i<52?'a'+i-26:'0'+i-52);
- return (char)code;
- }
- static public byte unmap64(byte i){
- // +:43 /:47 0:48 =:61 A:65 a:97
- if( i=='=' ) return 0;
- byte index = (byte)(i=='+'?62:i=='/'?63:i<'A'?i-'0'+52:i<'a'?i-'A':i-'a'+26);
- return (byte)index;
- }
- static public void log(String t){
- System.out.print(t+"\n");
- }
- }
字符串转字节处理
String.getBytes() 方法可以将字串的字节数组导出, 但特别要注意的是, 本方法将返回该操作系统默认的编码格式的字节数组. 在不同平台上, 系统默认的代码页可能不一致, 英文系统一般使用 iso-8859-1, 中文系统有 GBK, 不考虑到这一点软件就会有问题. 例如如下示例代码, 通过指定 UTF-16/UTF-8 来获取到字串的字节数据. 注意 UTF-16 输出多了两个字节 0xFE 0xFF, 这是 BOM 信息, 字节顺序标记 Byte Order Mark, 它有两个字节, 值大的在后表示 BigEnding 大尾编码方式, 通过 UTF-16BE/UTF-16LE 指定大端小端来去除这两额外的 BOM 字节. 关于大尾小尾, 前者表高有效位先编码, 后者表示低有效位先编码, 即对一个两字节的汉字来说, 大尾表示高位的那个字节先编码输出.
- String a = new String("的 J");
- byte[] as = a.getBytes("UTF-16");
- for( int i=0; i<as.length; i++){
- int c = as[i] & 0xFF;
- log( "Byte Code"+c+ "0x"+Integer.toHexString(c) + "0b"+Integer.toBinaryString(c) );
- }
- // UTF-16 的 字使用 4 个字节编码, 英文字母 2 个字节
- Byte Code 254 0xfe 0b11111110
- Byte Code 255 0xff 0b11111111
- Byte Code 118 0x76 0b1110110
- Byte Code 132 0x84 0b10000100
- Byte Code 0 0x0 0b0
- Byte Code 74 0x4a 0b1001010
- // UTF-8 的 字使用 3 个字节编码, 英文字母 1 个字节
- Byte Code 231 0xe7 0b11100111
- Byte Code 154 0x9a 0b10011010
- Byte Code 132 0x84 0b10000100
- Byte Code 74 0x4a 0b1001010
不同编码的字节顺序标记的表示编辑
编码 表示 (十六进制) 表示 (十进制)
UTF-8 EF BB BF 239 187 191
UTF-16 大端序 FE FF 254 255
UTF-16 小端序 FF FE 255 254
UTF-32 大端序 00 00 FE FF 0 0 254 255
UTF-32 小端序 FF FE 00 00 255 254 0 0
复习 Java 运算符优先级
优先级 描述 运算符
括号 () []
正负号 + -
自增自减非 ++ -- !
乘除取余 */ %
加减 + -
移位运算 <>>>>>
大小关系 >>= <<=
相等关系 = !=
按位与 &
按位异或 ^
按位或 |
逻辑与 &&
逻辑或 ||
条件运算 ?:
赋值运算 = += -= *= /= %=
位赋值运算 = |= <<=>>=>>>=
复习 Java 基础数据类型
- Primitive type Size Minimum Maximum Wrapper type
- boolean - - - Boolean
- char 16 bits Unicode 0 Unicode 2^16-1 Character
- byte 8 bits -128 +127 Byte
- short 16 bits -2^15 +2^15-1 Short
- int 32 bits -2^31 +2^31-1 Integer
- long 64 bits -2^63 +2^63-1 Long
- float 32 bits IEEE754 IEEE754 Float
- double 64 bits IEEE754 IEEE754 Double
- void - - - Void
范围大的向范围小的数据类型转换时, 需要考虑符号位的影响. 如无符号整数就不能直接转为 byte, 值 > 127 的正数都不行, 视为负数. Java 所有数值都是带符号的, 没有无符号数值. 反过来, 处理 byte 数据时, 如何无符号化处理? 按补码的规律, byte 数据如果为负数, 可以 + 256 来实现, 正数不用处理, 也可以和 0xFF 进行位与运算, 这个位运算操作可以去掉转换后的数值的符号位. 例如, 下例中 byte 的 - 1 转换到 int 时, 通过与运算将 int 的符号位清零, 这样实现 byte 数据的无符号化.
- byte myByte =(byte)0xff;
- int myInt = myByte & 0xFF;
- sytem.out.println( ""+(by & 0xff));
- sytem.out.println( ""+(by + 256) );
取得某正数的负数补码表达规则是, 按位取反加 1.1 的负数是 - 1, 补码就是对正 1 的十进位 00000001 取反得到 11111110, 加 1 就得到 11111111, 用 16 进位表示就是 0xFF.
1 的二进位表示 00000001 128 的二进位表示 10000000
1 的二进位取反 11111110 128 的二进位取反 01111111
二进位取反加一 11111111 二进位取反后加一 10000000
即得到 1 的对应负数补码 0xFF 128 的对应负数 - 128 的补码为 0x80, 注意和 128 一样
计算机在读取数据进行运算时, 会根据最高位即符号位来应用加减法则进行计算. 现在根据补码值 0x81 反解出这个原值, 字面上如果无称号处理 0x81 就是 129, 按减 1 求反得出负数对应的正值, 即 127, 所以 0x81 这个补码对应的负数就是 - 127.
- byte by = (byte)129;
- System.out.println(""+(by));
- System.out.println(""+(by&0xff));
- System.out.println(""+(by+256));
Base16 码表
- Index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- Encode 0 1 2 3 4 5 6 7 8 9 A B C D E F
Base64 码表
- Index Char Index Char Index Char Index Char
- 0 A 16 Q 32 g 48 w
- 1 B 17 R 33 h 49 x
- 2 C 18 S 34 i 50 y
- 3 D 19 T 35 j 51 z
- 4 E 20 U 36 k 52 0
- 5 F 21 V 37 l 53 1
- 6 G 22 W 38 m 54 2
- 7 H 23 X 39 n 55 3
- 8 I 24 Y 40 o 56 4
- 9 J 25 Z 41 p 57 5
- 10 K 26 a 42 q 58 6
- 11 L 27 b 43 r 59 7
- 12 M 28 c 44 s 60 8
- 13 N 29 d 45 t 61 9
- 14 O 30 e 46 u 62 +
- 15 P 31 f 47 v 63 /
- padding =
- URL and Filename safe Base 64 Alphabet
- Value Encoding Value Encoding Value Encoding Value Encoding
- A 17 R 34 i 51 z
- B 18 S 35 j 52 0
- C 19 T 36 k 53 1
- D 20 U 37 l 54 2
- E 21 V 38 m 55 3
- F 22 W 39 n 56 4
- G 23 X 40 o 57 5
- H 24 Y 41 p 58 6
- I 25 Z 42 q 59 7
- J 26 a 43 r 60 8
- K 27 b 44 s 61 9
- L 28 c 45 t 62 - (minus)
- M 29 d 46 u 63 _ (underline)
- N 30 e 47 v
- O 31 f 48 w
- P 32 g 49 x
- Q 33 h 50 y (pad) .
Multipurpose Internet Mail Extensions (MIME) 是 Base64 的另一种编码方案, 广泛应用于文件的编码, MIME 消息能包含文本, 图像, 音频, 视频以及其他应用程序专用的数据, 如 IE 保存网页单文件 MHT 方式就是使用的 MIME,Multipart message 编码信息参考:
- MIME-Version: 1.0
- Content-Type: multipart/mixed; boundary=frontier
- This is a message with multiple parts in MIME format.
- --frontier
- Content-Type: text/plain
- This is the body of the message.
- --frontier
- Content-Type: application/octet-stream
- Content-Transfer-Encoding: base64
- PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg
- Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==
- --frontier--
Base32 码表 - RFC 4648 alphabet
- Value Symbol Value Symbol Value Symbol Value Symbol
- 0 A 8 I 16 Q 24 Y
- 1 B 9 J 17 R 25 Z
- 2 C 10 K 18 S 26 2
- 3 D 11 L 19 T 27 3
- 4 E 12 M 20 U 28 4
- 5 F 13 N 21 V 29 5
- 6 G 14 O 22 W 30 6
- 7 H 15 P 23 X 31 7
- padding =
PS: 编程工具用的是 Sublime Text 3, 编译命令通过 Build Tools 调用 Java SDK 完成, 很轻便的工具.
Sublime Text with Java Programming
编译工具配置参考:
- {
- // "shell_cmd": "javac.exe \"$file\"| java.exe \"$file_base_name\"",
- // "shell_cmd": "ECHO Compiling $file_base_name.java & javac -encoding UTF-8 \"$file\"& java \"$file_base_name\"",
- "shell_cmd": "ECHO Compiling $file_base_name.java && javac -encoding UTF-8 \"$file\"&& java \"$file_base_name\"",
- "file_regex": "^(...*?):([0-9]*):?([0-9]*)",
- "working_dir": "${file_path}",
- "selector": "source.java",
- "encoding":"gbk",
- "variants":[
- {
- "name":"编译",
- "shell_cmd": "ECHO Compiling $file_base_name.java & javac -d . -encoding UTF-8 \"$file\"",
- },
- {
- "name":"运行当前类",
- "shell_cmd":"java \"$file_base_name\" "
- },
- {
- "name":"cmd 中运行",
- "shell_cmd":"start cmd /c \"javac -encoding UTF-8 \"$file\" & java \"$file_base_name\" & pause \""
- }
- ]
- }
来源: http://www.jianshu.com/p/79ecc613ed21