前置知识
编程技巧
编程技巧:指定可变参数的出现位置
#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