uri解码简析与C语言实现

  • Post author:
  • Post category:其他




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)



版权声明:本文为qq_31324949原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。