相信web开发者都或多或少见过类似如下的url:
http://loaclhost:8088/xxx/%e4%b8%ad%e5%9b%bd?size=4
初次见到url中包含红色字体的部分,心中有一个疑问,这到底是什么鬼?
后来知道了红色字体部分是中文“中国”通过url编码得到,那么疑问又来了,为什么不直接使用“中国”呢?又是通过什么手段将“中国”转换成那么奇怪的额字符串呢?
问题一,中文在计算机的世界中是没有办法被识别的,诸如韩文、日文也是如此,需要用一种统一的标准来标准各种语言文字,就像英文是世界的通用语言一样。于是Unicode出现了,Unicode将地球上所有出现过的文字都记录下来,并用数字与文字一一对应,每一个文字都有了独有的表达方式(比如“中”对应的数字为20013),如此,只认识“0”、“1”的计算机边可以通过数字间接识别文字。众所周知,字节是计算机中最小的计量单位,但是Unicode自身不利于编解码,所以后来在其基础之上诞生了诸如UTF-8、GBK、ISO-8859-1等编码,而其中的UTF-8成为了世界流行通用的编码。上文红色字体部分就是在UTF-8的编码基础之上得到的,具体如何实现见问题二。
问题二,对于中文的url编码可以基于UTF-8或者GBK,由于国际化,这里就讨论基于UTF-8的情况。UTF-8的编码规则可以参考百度百科。中文基本汉字在Unicode的范围为4E00-9FA5,编码成UTF-8为三个字节,对应的编码规则如下:
1110XXXX
10XX XXXX
10XX XXXX
举一个例子,将“中”编码成UTF-8。“中”对应的Unicode为“20013”,“20013”转换成二进制为“100 111000 101101”,通过上述编码规则编码成UTF-8为“11100100 10111000 10101101”,将红色部分提取出来正好是“20013”的二进制,将UTF-8编码转换成16进制为e4b8ad,每个字节前面添加%,变为%e4%b8%ad,是不是很熟悉的感觉。看看上文url的红色字体部分呢,对的,红色文字部分是“中国”的url编码。
url编码的原理已经介绍完毕了,下面用Java程序实现编码和解码过程。
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class UrlTest {
public static void main(String[] args) throws UnsupportedEncodingException {
System.out.println(urlEncode("中国")); //%e4%b8%ad%e5%9b%bd
System.out.println(urlDecode("%e4%b8%ad%e5%9b%bd")); //中国
}
static String urlEncode(String str) throws UnsupportedEncodingException {
StringBuilder sb = new StringBuilder();
//获得UTF-8编码的字节数组
byte[] utf8 = str.getBytes("UTF-8");
for (byte b : utf8) {
System.out.println(b);
//将字节转换成16进制,并截取最后两位
String hexStr = Integer.toHexString(b);
String temp = hexStr.substring(hexStr.length() - 2);
//添加%
sb.append("%");
sb.append(temp);
}
return sb.toString();
}
static String urlDecode(String str) throws UnsupportedEncodingException {
if (str == null) {
return null;
}
if (str.length() % 3 != 0) {
return null;
}
byte[] arr = new byte[str.length() / 3];
for (int i = 0; i <= str.length() - 3; i += 3) {
//截取%后两位
String temp = null;
if (i == str.length() - 3) {
temp = str.substring(i + 1);
} else {
temp = str.substring(i + 1, i + 3);
}
System.out.println(temp);
//转换成自字节
arr[i / 3] = (byte) Integer.parseInt(temp, 16);
}
System.out.println(Arrays.toString(arr)); //[-28, -72, -83, -27, -101, -67]
//解码
return new String(arr, 0, arr.length, "UTF-8");
}
}