UrlEncoder,UrlDecoder, 字符集对Encoder的影响

字符集对Encoder的影响,其实就是字符在不同字符集下编码不同,即使用的字节数不同,而每个字节都用2个16进制的字母在url中表示[color=red](它们之间的关系是,第一个16进制数代表一个字节的前4位,后一个16进制数代表一个字节的后4位!)[/color]
相应的我们可以看到,Tomcat中类org.apache.catalina.util.RequestUtil对url进行转码的方法,其中case '%'后面,data[ox++] = (byte)((convertHexDigit(data[ix++]) < < 4) + convertHexDigit(data[ix++]));就是根据这个规则来把%xx,转成字节,然后根据字节以及编码,转成正确的字符串。同时从convertHexDigit方法可以看出,%后面的字母是大小写不敏感的。

public static void parseParameters(Map map,
byte[] data,
String encoding) throws UnsupportedEncodingException

{
if (data != null && data.length > 0) {
int ix = 0;
int ox = 0;
String key = null;
String value = null;
while (ix < data.length) {
byte c = data[ix++];
switch ((char) c) {
case '&':
value = new String(data, 0, ox, encoding);
if (key != null) {
putMapEntry(map, key, value);
key = null;
}
ox = 0;
break;
case '=':
if (key == null) {
key = new String(data, 0, ox, encoding);
ox = 0;
} else {
data[ox++] = c;
}
break;
case '+':
data[ox++] = (byte)' ';
break;
case '%':
data[ox++] = (byte)((convertHexDigit(data[ix++]) < < 4)
+ convertHexDigit(data[ix++]));
break;
default:
data[ox++] = c;
}
}
//The last value does not end in '&'. So save it now.
if (key != null) {
value = new String(data, 0, ox, encoding);
putMapEntry(map, key, value);
}
}
}

private static byte convertHexDigit( byte b ) {
if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
throw new IllegalArgumentException(sm.getString("requestUtil.convertHexDigit.notHex",Character.valueOf((char)b)));
}

URLEncode,[url=http://tools.ietf.org/html/rfc3986]RFC 3986[/url]規範了哪些字符是作為保留字(如:!、@、/、?等),如果URL中使用到了這些保留字,就必須將它編碼為「%HEXHEX」的16进制形式[/color],舉例來說,「空白字元」的 ASCII code 是32,所以會被編碼為 %20,而其它 non-ASCII 字元(如:中文字)則以 UTF-8 字元編碼後的字节数组來編碼成 %HEXHEX 的形式(每个字节8位,取值在-128-127之间,用2个16进制的值来表示)。[color=red](这样就是在URL中用US-ASCII字符来表示非US-ASCII或者URL保留字的值)[/color]

在 RFC 3986 之前,[url=http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol]HTTP[/url] 也有為 GET/POST 在傳遞參數時的 url encode 方式作定義,基本上也是 encode 成 %HEXHEX 的形式,[color=red]保留字跟 RFC 3986 的有部份出入,不過就沒有限定一定是 UTF-8 的文字編碼了[/color],而且還有一個重點--「空白字元」會被編碼成 + 而不是 %20。

常用語言的函式庫
因為有這樣的差異,所以開發人員在使用函式庫的時候常常會搞混什麼時候該用/不該用什麼函式,以 PHP 來說就分為兩組函式:
urlencode / urldecode
簡單地說就是以 HTTP 所使用的 application/x-www-form-urlencoded 的編碼規則,也就是會將空白字元編碼成 + 而不是%20。
rawurlencode / rawurldecode
按照 RFC 3986 所定義的方式來作編碼。

Python 版本的話就是:
urllib.urlencode / urllib.urldecode
會把空白字元編碼成 +
目前要使用 Python 3 以後的 urllib.parse.urlencode 才會按照 RFC 3986 的方式來作編碼,若是 2.x 的版本就要另外處理或是找 3rd-party 資料庫來做。

JavaScript 的 encodeURI 或是 encodeURIComponent (兩者僅相差一些保留字是否要作編碼,如 #)目前則是都使用 RFC 3986 的方式來作編碼,所以要作 application/x-www-form-urlencoded 的編碼時(AJAX POST),就要自己把 %20 替換成 + (jQuery 目前的程式碼就是這樣做的)

Java 的 java.net.URLEncoder.encode 這個 method 也是編碼成 application/x-www-form-urlencoded 的方式,如果要遵照 RFC 3986 的定義,則可以自行再把 + 替換成 %20 即可。
参考:[url=http://blog.ericsk.org/archives/1423]混亂的 URLEncode[/url]

[color=red]使用不同的编码,来进行UrlEncoder[/color]
汉字“中”,在不同的编码中使用不同长度的字节来表示,而[color=red]每个字节是8位的,可以用16进制的2个字符来表示[/color],
在UTF-8中用3个字节表示的值:%E4%B8%AD,
而在GB2312编码中用的是2个字节的值:%D6%D0
它们字节的编码没有任何关系,但是可以通过Unicode编码中转后相互转换。

编码后,就把%后跟着2个16进制值的字符传递到服务器上去。

try {

System.out.println(((byte) 0x01));

// 汉字“中”用UTF-8进行URLEncode的时候,得到%e4%b8%ad(对应的ISO-8859-1的字符是中)
String item = new String(new byte[] { (byte) 0xe4, (byte) 0xb8, (byte) 0xad }, "UTF-8");
// 中
System.out.println(item);

item = new String(new byte[] { (byte) 0xe4, (byte) 0xb8, (byte) 0xad }, "ISO-8859-1");
// 中
System.out.println(item);

System.out.println(new BigInteger("253").toByteArray());
System.out.println(Integer.toBinaryString(253));

// 中
item = new String(item.getBytes("ISO_8859_1"), "UTF-8");
System.out.println(item);
// 中
item = new String(item.getBytes("UTF-8"), "ISO_8859_1");
System.out.println(item);

// %E4%B8%AD
System.out.println(URLEncoder.encode("中", "UTF-8"));
// %3F
System.out.println(URLEncoder.encode("中", "ISO-8859-1"));
// %D6%D0
System.out.println(URLEncoder.encode("中", "GB2312"));

// 中文
System.out
.println(URLDecoder.decode("%E4%B8%AD%E6%96%87", "UTF-8"));
// 乱码 ??
System.out.println(URLDecoder.decode("%3F%3F", "ISO-8859-1"));
// 中文
System.out.println(URLDecoder.decode("%D6%D0%CE%C4", "GB2312"));
// 乱码 涓��
System.out.println(URLDecoder
.decode("%E4%B8%AD%E6%96%87", "GB2312"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值