1、编码原理
1、uri只允许可打印字符,如0-9、a-z、A-Z,保留原字符
2、对于不可打印字符,需要将该字符串转为它对应的16进制
显示出来,用’%‘表示16进制,如0xE4表示为%E4
3、某些特殊字符如’+‘是表示为空格’ '还是保留原字符,取决于每个库自己的实现方式。
总结:对字符串进行uri编码时,会逐个字符进行编码,每个字符均表示为其16进制,具体显示为%XX
。特殊的:如’a’的16进制是0x61,可以表示为%61
,但因为它是可见字符,则保留为原样。
2、C代码实现
注:代码主要是针对于glib2库的g_filename_to_uri()精简,注释是自己的理解,有错请指出。
1、uri-encode.h
#ifndef URI_ENCODE_H
#define URI_ENCODE_H
char *encodeUri(const char*oriString);
#endif
2、uri-encode.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uri-encode.h"
const unsigned char acceptable[96] = {
/* A table of the ASCII chars from space (32) to DEL (127) */
/* ! " # $ % & ' ( ) * + , - . / */
0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
/* @ A B C D E F G H I J K L M N O */
0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
/* ` a b c d e f g h i j k l m n o */
0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
/* p q r s t u v w x y z { | } ~ DEL */
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
};
const char hex[16] = "0123456789ABCDEF";
#define MASK 0x8 /* Allows '/', '&', '=', ':', '@', '+', '$' and ',' */
#define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & MASK)) //判断字符(a)是保留还是转码
/* 1、ascii码在0-31之间的字符原样保留
* 2、ascii码在32-127之间的字符做处理
* 部分字符直接保留,eg: 0-9,a-z,A-Z,/,&,=,:,@,+,$,,
* 部分字符需要转码,eg: 空格-> %20
* @return 返回值为malloc的空间,非空时需要释放内存
**/
char *encodeUri(const char*oriString){
int unacceptable = 0;
int c;
const char *p;
char *result,*q;
if(!oriString)
return NULL;
//求需要转码的字符个数
for(p=oriString; *p!='\0'; ++p){
c = (unsigned char)*p;
if(!ACCEPTABLE(c))
++unacceptable;
}
//求转码后字符串的长度,并分配内存
result = malloc(p-oriString + unacceptable*2/*字母转码后需要占据3个字节空间*/ +1);
for(q=result,p=oriString; *p!='\0'; ++p){
c = (unsigned char)*p;
/*针对32-127之间的部分字符进行转码*/
if(!ACCEPTABLE(c)){
*q++ = '%'; //转码以'%'开始
*q++ = hex[c >> 4]; //(c >> 4) == (c/16)
*q++ = hex[c & 15]; //(c & 15) == (c%16)
}else
*q++ = *p;
}
*q = '\0';
return result;
}
3、main.c 用于对代码的检测验证
#include <stdio.h>
#include <stdlib.h>
#include "uri-encode.h"
int main (int argc, char * argv[])
{
if(argc < 2){
printf("usage: ./main \"string\"\n");
exit(-1);
}
char * encode = encodeUri(argv[1]);
printf("%s\n",encode);
if(encode) //释放
free(encode);
return 0;
}
3、编译运行
1、编译gcc main.c uri-encode.c uri-encode.h -o main
2、运行:(读者可参照输出认真理解第1步的编码原理)
输入 | 输出 |
---|---|
./main a盘+b卷 | a%E7%9B%98+b%E5%8D%B7 (可见代码中是直接保留了’+’) |
./main \(a,b\) | (a,b) |
4、总结
1、编码的过程实际上是10进制转16进制,将字符的16进制表示形式存入字符数组的过程
2、编码是逐字节进行的,可见字符一般直接保留;不可见字符一般用%XX
存储,其中XX
是该字符的16进制表示
if(!ACCEPTABLE(c)){
*q++ = '%'; //转码以'%'开始
*q++ = hex[c >> 4]; //(c >> 4) == (c/16)
*q++ = hex[c & 15]; //(c & 15) == (c%16)
}