C/C++编程:url编解码类url_coder实现

1059 篇文章 285 订阅

前置知识

编程技巧

编程技巧:指定可变参数的出现位置

#if	__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
#define	ACL_CPP_PRINTF(format_idx, arg_idx) \
	__attribute__((__format__ (__printf__, (format_idx), (arg_idx))))
#else
#define	ACL_CPP_PRINTF(format_idx, arg_idx)
#endif  // __GNUC__


url_coder& set(const char* name, bool override, const char* fmt, ...)
		ACL_CPP_PRINTF(4, 5);

编程技巧:* 如何变成&传参

std::string *buf_

void encode(std::string& buf){
}

// ----
 encode(*buf_);

编程技巧:操作int和string的技巧

通常会将int转换为字符串类型,然后调用操作字符串的函数

   url_coder& url_coder::set(const char* name, const char* value, bool override /* = true */){
      // ...
   }

   url_coder& url_coder::set(const char* name, int value, bool override /* = true */) {
        char buf[24];
        snprintf(buf, sizeof(buf), "%d", value);
        return set(name, buf, override);
    }

源码

//
// Created by oceanstar on 2021/8/11.
//

#ifndef OCEANSTAR_HTTP_URL_CODER_H
#define OCEANSTAR_HTTP_URL_CODER_H
#include <vector>
#include <string>
#include "global.h"

namespace oceanstar{
    /**
     * URL 编码函数
     * @param str {const char*} 源字符串
     * @return {char*} 编码后的字符串,返回值不可能为空,需要用 free 释放
     */
    char *acl_url_encode(const char *str);
    /**
     * URL 解码函数
     * @param str {const char*} 经URL编码后的字符串
     * @return {char*} 解码后的字符串,返回值不可能为空,需要用 free 释放
     */
    char *acl_url_decode(const char *str);
    struct URL_NV
    {
        char* name;
        char* value;
    };
    class  url_coder{
    public:
        /**
         * 构造函数
         * @param nocase {bool} 当为 true 时表示参数名不区别大小写
         */
        url_coder(bool nocase = true);
        /**
         * 构造函数,通过类实例对象构造
         * @param coder {const url_coder&}
         */
        url_coder(const url_coder& coder);
        ~url_coder();
        /**
         * 从 params_ 参数数组中删除某个变量
         * @param name {const char*} 变量名
         * @return {bool} 返回 true 表示删除成功,否则表示不存在
         */
        bool del(const char* name);
        /**
         * 重置解析器状态,清除内部缓存
         */
        void reset(void){
            params_.clear();
            buf_->clear();
        }
        /**
         * 采用 url 编码时,调用此函数添加变量
         * @param name {const char*} 变量名
         * @param value 变量值
         * @param override {bool} 如果存在同名变量是否直接覆盖
         * @return 返回 url_coder 对象的引用
         */
        url_coder& set(const char* name, const char* value, bool override = true);
        url_coder& set(const char* name, int value, bool override = true);
        /**
         * 获得 URL 解码后 params_ 数组中某个变量名的值
         * @param name {const char*} 变量名
         * @param found {bool*} 该指针非 NULL 时,将存储 name 是否存在,主要
         *  用在 name 的值为空的情形
         * @return {const char*} 返回 NULL 表示不存在
         */
        const char* get(const char* name, bool* found = NULL) const;
        /**
         * 获得 URL 解码后 params_ 数组中某个变量名的值
         * @param name {const char*} 变量名
         * @return {const char*} 返回 NULL 表示不存在或 name 的值为空
         *  注:如果 name 的值为空,则不能正确判断 name 是否存在
         */
        const char* operator[](const char* name) const;
        /**
         * 获得参数数组对象
         * @return {std::vector<URL_NV*>&}
         */
        const std::vector<URL_NV*>& get_params(void) const
        {
            return params_;
        }
        /**
         * URL 编码器对象的拷贝
         * @param coder {const url_coder&} URL 源编码器对象
         * @return {const url_coder&}
         */
        const url_coder& operator =(const url_coder& coder);
        /**
         * 获得将数组对象转换为编码后的字符串对象
         * @return {const string&}
         */
        const std::string& to_string(void) const;
        /**
         * 将存储于 params_ 数组中的数据进行 url 编码
         * @param clean {bool} 是否清空传入的 buf 缓冲区
         * @param buf {string&} 存储编码后的结果
         */
        void encode(std::string& buf, bool clean = true) const;
        /**
         * 解析以 URL 编码的字符串
         * @param str {const char*} url 编码形式的字符串
         */
        void decode(const char* str);
    private:
        void myfree(URL_NV* params){
            free(params->name);
            if(params->value && *params->value){
                free(params->value);
            }
            free(params);
        };
        std::vector<URL_NV*> params_;
        bool nocase_;
        std::string *buf_;
    };
}

#endif //OCEANSTAR_HTTP_URL_CODER_H


//
// Created by oceanstar on 2021/8/11.
//

#include <string.h>
#include <memory/acl_argv.h>
#include "url_coder.h"

namespace oceanstar{
    /**
     * URL 编码函数
     * @param str {const char*} 源字符串
     * @return {char*} 编码后的字符串,返回值不可能为空,需要用 free 释放
     */
    static unsigned char enc_tab[] = "0123456789ABCDEF";
    char *acl_url_encode(const char *str){
        int len = (int) strlen(str);
        int tmp_len = len;

        unsigned char *tmp = (unsigned char*) malloc(len + 1);

        int i, j;
        for (i = 0, j = 0; i < len; i++, j++) {
            tmp[j] = (unsigned char) str[i];
            if (!isalnum(tmp[j]) && strchr("_-.", tmp[j]) == NULL) { // 所传的字符不是字母和数字时,也不是_-.
                tmp_len += 3;
                tmp = (unsigned char*)realloc(tmp, tmp_len);
                tmp[j++] = '%';
                tmp[j++] = enc_tab[(unsigned char)str[i] >> 4];  //
                tmp[j] = enc_tab[(unsigned char)str[i] & 0x0F];
            }
        }

        tmp[j] = '\0';
        return (char*) tmp;
    }

    static unsigned char dec_tab[256] = {
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0,  0,  0,  0,  0,
            0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    };

    /**
     * URL 解码函数
     * @param str {const char*} 经URL编码后的字符串
     * @return {char*} 解码后的字符串,返回值不可能为空,需要用 free 释放
     */
    char *acl_url_decode(const char *str){
        int len = (int) strlen(str);
        char *tmp = (char *)malloc(len + 1);

        int i = 0, pos = 0;
        for (i = 0; i < len; i++) {
            if (str[i] != '%')
                tmp[pos] = str[i];
            else if (i + 2 >= len) {  /* check boundary */
                tmp[pos++] = '%';  /* keep it */
                if (++i >= len)
                    break;
                tmp[pos] = str[i];
                break;
            } else if (isalnum(str[i + 1]) && isalnum(str[i + 2])) {
                tmp[pos] = (dec_tab[(unsigned char) str[i + 1]] << 4)
                           + dec_tab[(unsigned char) str[i + 2]];
                i += 2;
            } else
                tmp[pos] = str[i];

            pos++;
        }

        tmp[pos] = '\0';
        return tmp;
    }
}
#include "acl_mystring.h"
namespace oceanstar{
    url_coder::url_coder(bool nocase /* = true */) :  nocase_(nocase){
        buf_ = new std::string;
    }

    url_coder::url_coder(const url_coder& coder){
        buf_ = new std::string(*coder.buf_);
        nocase_ = coder.nocase_;

        std::vector<URL_NV*>::const_iterator cit = coder.params_.begin();
        for (; cit != coder.params_.end(); ++cit) {
            set((*cit)->name, (*cit)->value);
        }
    }

    url_coder::~url_coder(){
        for(auto param : params_){
            myfree(param);
        }
        delete buf_;
    }

    void url_coder::decode(const char* str){
        ACL_ARGV* tokens = acl_argv_split(str, "&");
        for(auto & iter : tokens->argv){
            char* name  = (char*) iter.c_str();  // name1=value1
            char* value = strchr(name, '=');  //=value1
            if (value == NULL || *(value + 1) == 0) {  //value : { NULL  , = }
                value = NULL;
            } else {
                *value++ = 0;  // value : {= value1} -->  value: { value1}
                value = acl_url_decode(value);
            }
            name = acl_url_decode(name);
            URL_NV* param = (URL_NV*) malloc(sizeof(URL_NV));
            param->name = name;
            param->value = value;
            params_.push_back(param);
        }
        acl_argv_free(tokens);
    }

    void url_coder::encode(std::string& buf, bool clean /* = true */) const{
        if (clean) {   // 内部的buf_一般要清除
            buf.clear();
        }

        std::vector<URL_NV*>::const_iterator cit = params_.begin();
        char* name, *value;
        for (; cit != params_.end(); ++cit) {
            if (cit != params_.begin()) {   // 首次的话肯定没有& , 下面的会设置一对[name=value],这样第二次/第三次/...在计算[name=value]之前必须先添加一个&
                buf += '&';
            }
            name = acl_url_encode((*cit)->name);
            if ((*cit)->value && *(*cit)->value) {  // value不为空时需要先编码
                value = acl_url_encode((*cit)->value);
                buf += format("%s=%s", name, value);
                free(value);
            } else {
                buf += name;
            }
            free(name);
        }
    }

    const std::string& url_coder::to_string(void) const{
        encode(*buf_);
        return *buf_;
    }

    bool url_coder::del(const char* name){
        int (*cmp)(const char*, const char*) = nocase_ ? strcasecmp : strcmp;
        std::vector<URL_NV*>::iterator it = params_.begin();

        for (; it != params_.end(); ++it) {
            if (cmp((*it)->name, name) == 0) {
                myfree(*it);
                params_.erase(it);
                return true;
            }
        }

        return false;
    }

    url_coder& url_coder::set(const char* name, const char* value, bool override /* = true */){
        if (name == NULL || *name == 0) {
            return *this;
        }

        if (override) {
            int (*cmp)(const char*, const char*) = nocase_ ? strcasecmp : strcmp;

            std::vector<URL_NV*>::iterator it = params_.begin();
            for (; it != params_.end(); ++it) {
                if (cmp((*it)->name, name) == 0) {
                    myfree(*it);
                    params_.erase(it);
                    break;
                }
            }
        }

        URL_NV* param = (URL_NV *)malloc(sizeof(URL_NV));
        param->name = strdup(name);
        if (value && *value) {
            param->value = strdup(value);
        } else {
            param->value = NULL;
        }
        params_.push_back(param);
        return *this;
    }
    url_coder& url_coder::set(const char* name, int value, bool override /* = true */) {
        char buf[24];
        snprintf(buf, sizeof(buf), "%d", value);
        return set(name, buf, override);
    }


    const char* url_coder::get(const char* name, bool* found /* = NULL */) const
    {
        int (*cmp)(const char*, const char*) = nocase_ ? strcasecmp : strcmp;
        std::vector<URL_NV*>::const_iterator cit = params_.begin();

        for (; cit != params_.end(); ++cit) {
            if (cmp((*cit)->name, name) == 0) {
                if (found) {
                    *found = true;
                }
                return (*cit)->value;
            }
        }

        if (found) {
            *found = false;
        }
        return NULL;
    }

    const char* url_coder::operator [](const char* name) const
    {
        return get(name, NULL);
    }

    const url_coder& url_coder::operator =(const url_coder& coder)
    {
        nocase_ = coder.nocase_;
        *buf_ = *coder.buf_;

        std::vector<URL_NV*>::const_iterator cit = coder.params_.begin();
        for (; cit != coder.params_.end(); ++cit) {
            set((*cit)->name, (*cit)->value);
        }

        return *this;
    }
}

测试

#include <fcntl.h>
#include <connpool/connect_client.h>
#include <connpool/connect_manager.h>
#include <memory/acl_argv.h>
#include <http/url_coder.h>
#include "acl_file.h"

using namespace oceanstar;


int main(void){



    url_coder coder1;

    coder1.set("name1", "value1");
    coder1.set("name2", 2);
    coder1.set("name4", "中国人");
    printf("coder1 >> %s, name1: %s, name2: %s, name3: %s, name4: %s\r\n",
           coder1.to_string().c_str(),
           coder1["name1"], coder1["name2"], coder1["name3"], coder1["name4"]);

    coder1.del("name1");
    const char* ptr = coder1["name1"];
    printf("coder1 >> %s, name1: %s, name2: %s, name3: %s, name4: %s\r\n",
           coder1.to_string().c_str(), ptr ? ptr : "null",
           coder1["name2"], coder1["name3"], coder1["name4"]);

    //

    url_coder coder2;

    coder2 = coder1;
    coder2.set("name5", "&=value5=&");
    ptr = coder2["name1"];
    printf("--------------------------------------------------------\r\n");
    printf("coder2 >> %s, name1: %s, name2: %s, name3: %s, name4: %s, name5: %s\r\n",
           coder2.to_string().c_str(), ptr ? ptr : "null",
           coder2["name2"], coder2["name3"], coder2["name4"], coder2["name5"]);

    //
    url_coder coder3(coder2);

    coder3.set("name5", 5);
    coder3.set("name6", "=&外国人&=");
    ptr = coder3["name1"];
    printf("--------------------------------------------------------\r\n");
    printf("coder3 >> %s, name1: %s, name2: %s, name3: %s, name4: %s, name5: %s, name6: %s\r\n",
           coder3.to_string().c_str(), ptr ? ptr : "null",
           coder3["name2"], coder3["name3"], coder3["name4"],
           coder3["name5"], coder3["name6"]);
    //
    url_coder coder4;
    const char* s = "name1=value1&name2=2&name3=value3&name4=%D6%D0%B9%FA%C8%CB";
    coder4.decode(s);
    printf("--------------------------------------------------------\r\n");
    printf("coder4 >> %s, name1: %s, name2: %s, name3: %s, name4: %s\r\n",
           coder4.to_string().c_str(),
           coder4["name1"], coder4["name2"], coder4["name3"], coder4["name4"]);
    return 0;
}

测试是否内存泄露: valgrind --tool=memcheck --leak-check=yes -v ./oceanstar_http

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值