1、解码原理
1、遇到
'%'
才开始解析,%后的两个字符最终解析为一个ascii字符.如%E4 -> 0xE4 -> ‘E’*16+4 -> 244,即 %E4 解析为ascii码为244的字符
2、
%XX
才进行解析,其余字符保留原样
总结:abc -> abc 、%61%62%63 -> 0x610x620x63 -> abc
2、C代码实现
注:本代码是对glib2库的g_filename_from_uri()的精简,注释是自己的理解,有错请指正。
1、uri-decode.h
#ifndef URL_DECODE_H
#define URL_DECODE_H
char *uriDecode(const char* uriString);
#endif
2、uri-decode.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
const unsigned int asciiTableData[256] = {
0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
0x004, 0x104, 0x104, 0x004, 0x104, 0x104, 0x004, 0x004,
0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
0x140, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459,
0x459, 0x459, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
0x0d0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253,
0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
0x253, 0x253, 0x253, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
0x0d0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073,
0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
0x073, 0x073, 0x073, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x004
/* the upper 128 are all zeroes */
};
char* unescapeUriString(const char* uriString,bool asciiEscape);
int unescapeCharacter(const char *scanner);
int asciiXdigitValue(char c);
int asciiDigitValue(char c);
bool asciiIsDigit(char c);
/*uri解码*/
char *uriDecode(const char* uriString){
const char* pathPart;
char* retName;
if(!uriString)
return NULL;
pathPart = uriString;
retName = unescapeUriString(pathPart,false);
return retName;
}
/* 解析uri字符串
@ asciiEscape 布尔值,ascii码是否必须解析
*/
char* unescapeUriString(const char* uriString,bool asciiEscape){
int strLen;
char *result,*out; //分配内存,存放解码后的字符串
const char *in,*end;
int c;
if(!uriString)
return NULL;
strLen = strlen(uriString);
result = malloc(strLen + 1);//可推测解码后的长度<=原长度
out = result;
for(in = uriString,end = in + strLen; in < end; ++in){
c = *in;
//遇到了'%'才去解析
if('%' == c){
if(in + 3 > end)
break;
//获取%后2个字符的解码值
c = unescapeCharacter(in+1);
if(c <= 0)
break;
if(asciiEscape && c <= 0x7F)
break;
if(strchr("/",c) != NULL)
break;
in += 2;//一般的格式为%后加两个ascii码字符
}
*out++ = c;//存储转义结果
}
*out = '\0';
if(in != end){
free(result);
return NULL;
}
return result;
}
/*假设 @scanner is "%E4",则返回"%E4"的解码值
*实际上是计算0xE4对应的十进制。
*/
int unescapeCharacter(const char *scanner){
int first,second;
first = asciiXdigitValue(scanner[0]);
if(first < 0)
return -1;
second = asciiXdigitValue(scanner[1]);
if(second < 0)
return -1;
return (first << 4) | second; //== (first*16 | second) == (first*16 + second)
}
/*求任一字符的数字值*/
int asciiXdigitValue(char c){
//printf("-->%c\n",c);
if(c >= 'A' && c <= 'F')
return c - 'A' + 10;//(A B C D E F)->(10 11 12 13 14 15)
if(c >= 'a' && c <= 'f')
return c - 'a' + 10;
return asciiDigitValue(c);//('0'...'9')->(0...9)
}
/*ascii码是数字则返回数字值,否则返回-1*/
int asciiDigitValue(char c){
if(asciiIsDigit(c))
return c - '0';
return -1;
}
/*判断ascii码是否是数字0-9*/
bool asciiIsDigit(char c){
/*字符的ascii码&8 结果为0-127,则是数字*/
return asciiTableData[(unsigned char)c & (1 << 3)];
}
3、main.c 对代码功能的检验验证
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "uri-decode.h"
int main(int argc,char* argv[]){
char uriString[256];
char *retString;
if(argc < 2){
printf("usage: ./main \"string\"\n");
return -1;
}
memset(uriString,0,sizeof(uriString));
strncpy(uriString,argv[1],sizeof(uriString));
retString = uriDecode(uriString);
printf("%s\n",retString);
if(retString)
free(retString);
return 0;
}
3、编译运行
1、编译:
gcc uri-decode.c uri-decode.h main.c -o main
2、运行:(读者可参照输出对第1步深入理解)
输入 | 输出 |
---|---|
./main %61%62%63 | abc |
./main abc | abc |
./main “http://www.oschina.net/search%3Fscope=bbs&q=C%E8%AF%AD%E8%A8%80” | http://www.oschina.net/search?scope=bbs&q=C语言 |
4、总结
1、解码的过程其实就是16进制转10进制,求字符的10进制ascii码值的过程
2、求出的10进制ascii码值存放在字符数组的1个字节的空间里(
仅1字节
)
3、单独对这一个字节来讲,它对应的字符仍然是不可见字符,但是多个不可见字符在连续的数组内存空间中可以拼接成一个
可见的字符串
。
4、如汉字就是这个逻辑,linux下
strlen("中")==3
,即汉字由3个
char字符
拼接而成。
return (first << 4) | second; //== (first*16 | second) == (first*16 + second)