Java编码算法

编码

1.编码算法

什么是编码?

ASCII码就是一种编码,字母A的编码是十六进制的0x41,字母B是0x42,以此类推:

字母 ASCII编码
A 0x41
B 0x42
C 0x43
D 0x44
... ...

因为ASCII编码最多只能有127个字符,要想对更多的文字进行编码,就需要用占用2个字节的Unicode或者3个字节的UTF-8。例如:中文的"中"字使用Unicode编码就是0x4e2d,UTF-8编码是0xe4b8ad。

汉字 Unicode编码 UTF-8编码
0x4e2d 0xe4b8ad
0x6587 0xe69687
0x7f16 0xe7bc96
0x7801 0xe7a081

因此,最简单的编码是直接给每个字符指定一个若干字节表示的整数,复杂一点的编码就需要根据一个已有的编码推算出来。

2.URL编码

URL编码是浏览器发送数据给服务器时使用的编码,它通常附加在URL的参数部分,例如:

https://www.baidu.com/s?wd=中文

之所以需要URL编码,是因为出于兼容性考虑,很多服务器只识别ASCII字符。但如果URL中包含中文、日文这些非ASCII字符怎么办?不要紧,URL编码有一套规则:

如果字符是A~Z,a~z,0~9以及-、_、.、*,则保持不变;

如果是其他字符,先转换为UTF-8编码,然后对每个字节以%XX表示。

例如:字符"中"的UTF-8编码是0xe4b8ad,因此,它的URL编码是%E4%B8%AD。URL编码总是大写。

Java标准库提供了一个URLEncoder类来对任意字符串进行URL编码:

java 复制代码
import java.net.URLEncoder;
public class Main {
    public static void main(String[] args) {
        String encoded = URLEncoder.encode("中文!", "utf-8");
		System.out.println(encoded);
    }
}

上述代码的运行结果是%E4%B8%AD%E6%96%87%21,"中"的URL编码是%E4%B8%AD,"文"的URL编码是%E6%96%87,!虽然是ASCII字符,也要对其编码为%21。

如果服务器收到URL编码的字符串,就可以对其进行解码,还原成原始字符串。Java标准库的URLDecoder就可以解码:

java 复制代码
public class Main {
    public static void main(String[] args) {
        String decoded = URLDecoder.decode("%E4%B8%AD%E6%96%87%21", "utf-8");
		System.out.println(decoded);
    }
}

要特别注意:URL编码是编码算法,不是加密算法。URL编码的目的是把任意文本数据编码为%前缀表示的文本,编码后的文本仅包含A~Z,a~z,0~9,-,_,.,*和%,便于浏览器和服务器处理。

URLEncoder类,主要进行编码

java 复制代码
String keyworld = "巴黎";
String encoderString = URLEncoder.encode(keyworld, "utf-8");
System.out.println("编码内容为:" + encoderString);

URLDecoder类,主要进行解码

java 复制代码
String decoderString = URLDecoder.decode(encoderString, "utf-8");
System.out.println("解码内容为:" + decoderString);

3.Base64编码

URL编码是对字符进行编码,表示成%xx的形式,而Base64编码是对二进制数据进行编码,表示成文本格式。

Base64编码可以把任意长度的二进制数据变为纯文本,并且纯文本内容中且只包含指定字符内容:A~Z、a~z、0~9、+、/、=。它的原理是把3字节的二进制数据按6bit一组,用4个整数表示,然后查表,把整数用索引对应到字符,得到编码后的字符串。

6位整数的范围总是0~63,所以,能用64个字符表示:字符A~Z对应索引0~25,字符a~z对应索引26~51,字符0~9对应索引52~61,最后两个索引62、63分别用字符+和/表示。

举个例子:3个byte数据分别是e4、b8、ad,按6bit分组得到十六进制39、0b、22和2d,分别对应十进制57、11、34、45,通过索引计算结果为5Lit4

java 复制代码
┌───────────────┬───────────────┬───────────────┐
│      e4       │      b8       │      ad       │
└───────────────┴───────────────┴───────────────┘
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│1│1│1│0│0│1│0│0│1│0│1│1│1│0│0│0│1│0│1│0│1│1│0│1│二进制
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
┌───────────┬───────────┬───────────┬───────────┐
│    39     │    0b     │    22     │    2d     │十六进制
└───────────┴───────────┴───────────┴───────────┘
┌───────────┬───────────┬───────────┬───────────┐
│    57     │    11     │    34     │    45     │十进制
└───────────┴───────────┴───────────┴───────────┘
┌───────────┬───────────┬───────────┬───────────┐
│    5      │    L      │    i      │    t      │十进制
└───────────┴───────────┴───────────┴───────────┘

在Java中,二进制数据就是byte[]数组。Java标准库提供了Base64来对byte[]数组进行编解码:

java 复制代码
public class Main {
    public static void main(String[] args) {
        byte[] input = new byte[] { (byte) 0xe4, (byte) 0xb8, (byte) 0xad };
        String b64encoded = Base64.getEncoder().encodeToString(input);
        System.out.println(b64encoded);
    }
}

编码后得到字符串结果:5Lit。要对这个字符使用Base64解码,仍然用Base64这个类:

java 复制代码
public class Main {
    public static void main(String[] args) {
        byte[] output = Base64.getDecoder().decode("5Lit");
        System.out.println(Arrays.toString(output)); // [-28, -72, -83]
    }
}

因为标准的Base64编码会出现+、/和=,所以不适合把Base64编码后的字符串放到URL中。一种针对URL的Base64编码可以在URL中使用的Base64编码,它仅仅是把+变成-,/变成_:

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 原始字节内容
		byte[] input = new byte[] { 0x01, 0x02, 0x7f, 0x00 };
		
		// 分别使用两种方式进行编码
		String b64Encode = Base64.getEncoder().encodeToString(input);
        String b64UrlEncoded = Base64.getUrlEncoder().encodeToString(input);
        
        // 替换"+、/和="
        System.out.println(b64Encode); 
        System.out.println(b64UrlEncoded);
        
        // 分别使用两种方式进行重新解码
        byte[] output1 = Base64.getDecoder().decode(b64Encode);
        byte[] output2 = Base64.getUrlDecoder().decode(b64UrlEncoded);

        // 结果完全一致
        System.out.println(Arrays.toString(output1));
        System.out.println(Arrays.toString(output2));
    }
}

Base64编码的目的是把二进制数据变成文本格式,这样在很多文本中就可以处理二进制数据。例如,电子邮件协议就是文本协议,如果要在电子邮件中添加一个二进制文件,就可以用Base64编码,然后以文本的形式传送。

Base64编码的缺点是传输效率会降低,因为它把原始数据的长度增加了1/3。和URL编码一样,Base64编码是一种编码算法,不是加密算法。

如果把Base64的64个字符编码表换成32个、48个或者58个,就可以使用Base32编码,Base48编码和Base58编码。字符越少,编码的效率就会越低。

Base64编码

String s = "中";
// 原始字符串转字节数组
byte[] bytes = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bytes));
// Base64编码(JDK1.8后提供)
String encode64 = Base64.getEncoder().encodeToString(bytes);
System.out.println("base64编码后:" + encode64);

Base64的补充字符

java 复制代码
String str = "ab";
byte[] bytes = str.getBytes();
System.out.println(Arrays.toString(bytes));
String enodingString = Base64.getEncoder().encodeToString(bytes);
System.out.println(enodingString);

Base64的占位符

java 复制代码
// Base64的"="占位符
// 原始字节内容
byte[] input = new byte[] { 0x01, 0x02, 0x7f, 0x00 };
// 分别使用两种方式进行编码
String b64Encode = Base64.getEncoder().encodeToString(input);
String b64UrlEncoded = Base64.getUrlEncoder().encodeToString(input);

// 替换"+、/和="
System.out.println(b64Encode);
System.out.println(b64UrlEncoded);

// 分别使用两种方式进行重新解码
byte[] output1 = Base64.getDecoder().decode(b64Encode);
byte[] output2 = Base64.getUrlDecoder().decode(b64UrlEncoded);

// 结果完全一致
System.out.println(Arrays.toString(output1));
System.out.println(Arrays.toString(output2));

Base64的应用

xx.jpg文件读取内容信息,编码,保存为base64的xx.txt文件

将xx.txt文件读取,解码,保存为xx.jpg文件

java 复制代码
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

//Base64应用:xx.jpg文件读取内容信息,编码,保存为base64的xx.txt文件
//将xx.txt文件读取,解码,保存为xx.jpg文件
public class Main {
	public static void main(String[] args) throws IOException {
		imgEcoding();
		imgDecoding();
	}

	private static void imgEcoding() throws IOException {
		byte[] b = Files.readAllBytes(Paths.get("E:\\apesourcefile\\maomao.jpg"));
		// 将二进制字节信息用base64编码
		String str = Base64.getEncoder().encodeToString(b);
		// 写出编码后的字符串
		Files.write(Paths.get("a.txt"), str.getBytes());
		System.out.println("图片base64信息保存成功");
	}

	private static void imgDecoding() throws IOException {
		// base64字符串对应的字节信息
		byte[] b = Files.readAllBytes(Paths.get("a.txt"));
		// 解码
		byte[] bytes = Base64.getDecoder().decode(b);
		Files.write(Paths.get("E:\\apesourcefile\\Base64.jpg"), bytes);
		System.out.println("base64解码图片成功");
	}
}

结论:

URL编码和Base64编码都是编码算法,它们不是加密算法;

URL编码的目的是把任意文本数据编码为%前缀表示的文本,便于浏览器和服务器处理;

Base64编码的目的是把任意二进制数据编码为文本,但编码后数据量会增加1/3。

相关推荐
飞翔的佩奇1 分钟前
Java项目: 基于SpringBoot+mybatis+maven医院管理系统(含源码+数据库+任务书+开题报告+毕业论文)
java·数据库·spring boot·毕业设计·maven·mybatis·医院管理系统
java—大象3 分钟前
基于JavaWeb开发的java+Springboot操作系统教学交流平台详细设计实现
java·开发语言·spring boot
nvd119 分钟前
Java ETL - Apache Beam 简介
java·apache·etl
柠檬少少开发23 分钟前
图像拼接算法及实现(一)
人工智能·算法·计算机视觉
晴子呀26 分钟前
Spring底层原理大致脉络
java·后端·spring
DreamByte27 分钟前
Python Tkinter小程序
开发语言·python·小程序
只吹45°风32 分钟前
Java-ArrayList和LinkedList区别
java·arraylist·linkedlist·区别
覆水难收呀36 分钟前
三、(JS)JS中常见的表单事件
开发语言·前端·javascript
阿华的代码王国40 分钟前
【JavaEE】多线程编程引入——认识Thread类
java·开发语言·数据结构·mysql·java-ee
黑蛋同志40 分钟前
array和linked list的区别
java