一、URL编码
该编码一般用于Content-Type:application/x-www-form-urlencoded
的数据,如果前端传到后端的是编码后的一段数据,我们就需要进行解码,URL编码和解码可以使用Java api中的URLEncoder和URLDecoder(很多Java Api都使用Charset替代String已传递编码参数了,但是URLEncoder和URLDecoder并无变化,虽然影响不大)。
/*
URL编码的使用
*/
String encodedText = "%e4%bd%a0%e7%9c%9f%e5%b8%85";
String decodedText = "你真帅";
System.out.println(URLDecoder.decode(encodedText, StandardCharsets.UTF_8.name()));
System.out.println(URLEncoder.encode(decodedText, StandardCharsets.UTF_8.name()));
学会了如何使用,接下来我们就应该理解一下编码规则:
1、获取相应编码表示下的字节流
2、把每一字节转化成十六进制表示并在前面加一个’%’;
下面是参照URLEncoder转换部分得到的演示代码:
/*
测试URL编码的规则
*/
byte[] bytes = decodedText.getBytes(StandardCharsets.UTF_8);
for (byte aByte : bytes) {
System.out.print("%");
System.out.print(Character.forDigit(aByte >> 4 & 0xF, 16));
System.out.print(Character.forDigit(aByte & 0x0F, 16));
}
这里需要注意aByte >> 4 & 0xF
,如果单单使用aByte >> 4
就会出现符号问题,当aByte为负数时,最高位会使用符号位填充,Charater.forDigit(int digit, int radix)
是获取0~radix范围内的radix进制表示,负数当然就表示不了了,返回\u0000,也就是字符’\0’。
URLEncoder#encode方法还需要考虑下面两种情况:
1、空格。把空格替换成字符’+’
2、unicode编码的代理码点:高代理(\uD800-\uDBFF)和低代理(\uDC00-\uDFFF)。因为有些代理对是还未被标准定义的, 目前Java对这些非法代理的处理方法是将高代理和低代理各自视为一个字符。
其实第二个情况不考虑也是可以的,因为对于非法代理对,我们上面代码是可以正常工作的,但是产生的编码和URLEncoder#encode的不一样,但是解码结果还是一致的,我们了解到这种情况也算是开拓了我们思路了。
二、Base64
三、Unicode
我在分析活动CF活动请求时,经常会碰到形如"\u975e\u5e38\u62b1\u6b49\uff0c\u60a8\u8fd8\u4e0d\u6ee1\u8db3\u53c2\u52a0\u8be5\u6d3b\u52a8\u7684\u6761\u4ef6\uff01"的响应形式,这是字符串的Unicode表示,我们可以通过简单的转换算法把它转换成对应的字符串。
在此之前我们先了解一下Unicode是怎么表示字符的。
unicode的码点(码点就是对应的int数值,类似ASCII码)范围从0x000000 - 0x10FFFF,char的范围只能是在0x0000到0xFFFF,也就是标准的 2 字节形式通常称作UCS-2,所以在Java中char类型就是双字节的,但unicode码大于0x10000的部分如何用char表示呢?比如😀,想要表示😀这个表情,我们需要引出Java Unicode编码中的高低代理表示方法,Unicode为了表示更多的字符,保留了0xD800 - 0xDFFF范围内的码点,0xD800 - 0xDBFF为高代理码点,0xDC00 - 0xDFFF为低代理码点。高低代理的的码点随机组合用于表示unicode码点大于0x10000范围的字符,所以😀的表示为\uD83D\uDE00,用char数组表示就是char[] arr = {0xD83D,0xDE00}
。
我们知道了Unicode的表示规则:就是把\u后面的十六进制翻译成int类型,然后一一放进char数组中。
public static String decodeUnicodeString(String text){
StringBuilder builder = new StringBuilder(text.length());
for (int i = 0; i < text.length();){
char ch = text.charAt(i);
if (ch == '\\'){
if (i + 1 < text.length() && text.charAt(i + 1) == 'u'){
/*
获取后面4个十六进制字符
*/
char[] unicode = new char[4];
int j = 0, index = i + 2;
for (; j < 4 && index < text.length(); ++j, ++index){
unicode[j] = text.charAt(index);
}
/*
转换成int类型
*/
if (j == 4){
int codepoint = Integer.parseInt(new String(unicode), 16);
builder.append((char)codepoint);
i = index;
continue;
}
}
}
builder.append(ch);
++i;
}
return builder.toString();
}
这代码还是很有工具价值的,因为遇到的需要进行转换的情况还是挺多的。
四、Md5
五、随机字符串
这个可能有些情况下会有用,这也是一种思路。
private static String randomMask = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static String getRandomString(int length){
if (length <= 0){
throw new IllegalArgumentException("Length can not be zero or negative");
}
StringBuilder builder = new StringBuilder(length);
for (int i = 0; i < length; ++i){
int rIndex = (int) (62 * Math.random());
builder.append(randomMask.charAt(rIndex));
}
return builder.toString();
}
六、时间戳和格式化时间互转【需要学习Java新的时间API】
七、Hex–字节数组转换成十六进制字符串
字节转换成十六进制可以使用Apache common codec的Hex工具类:
String str = "你是谁";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
String hexString = Hex.encodeHexString(bytes);
byte[] decodedBytes0 = Hex.decodeHex(hexString);
也可以自己实现:
编码部分:
public static String toHexString(byte[] bytes){
StringBuilder builder = new StringBuilder(bytes.length * 2);
for (byte b : bytes){
builder.append(Character.forDigit(b >> 4 & 0x0F, 16));
builder.append(Character.forDigit(b & 0x0F, 16));
}
return builder.toString();
}
解码部分:
public static byte[] fromHexString(String hexString){
if ((hexString.length() & 0x01) != 0){
throw new IllegalArgumentException("The hex string is not valid");
}
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0, j = 0; j < bytes.length; ++j){
byte b = 0;
int hb = Character.digit(hexString.charAt(i++), 16);
int lb = Character.digit(hexString.charAt(i++), 16);
b |= hb << 4;
b |= lb;
bytes[j] = b;
}
return bytes;
}
注意,获取值对应的十六进制表示时不要直接对byte用Integer#toHexString
,因为Integer#toHexString
会省略高位为0的情况’0’,比如0x01会输出0x1,这不是我们想要的结果。