解码JSON

JSON 核心概念

JSON(JavaScript Object Notation)是 “JavaScript 对象表示法” 的简称,是一种轻量级数据交换文本格式,不依赖任何编程语言。它具有简洁清晰的层次结构,易于人类阅读编写,同时便于机器解析和生成,现已成为 Web 开发、跨平台应用间数据传输的主流格式。

JSON 数据格式

JSON 的核心数据结构只有两种,可相互嵌套组成复杂数据:

对象(无序键值对集合)

  • 用花括号{}包围,键值对以:分隔,多个键值对用,分隔。
  • 键必须是字符串(用双引号""包裹),值可是任意合法 JSON 类型。
  • 示例(修正原文档键名重复错误):
{
  "name": "lmx",       // 值类型:字符串
  "age": 29,           // 值类型:整型
  "score": [           // 值类型:数组(元素为对象)
    {
      "math": 85.5,    // 值类型:浮点型
      "english": 92    // 值类型:整型
    }
  ]
}

数组(有序值集合)

  • 用方括号[]包围,多个值用,分隔。
  • 数组中的值可是任意合法 JSON 类型,包括对象和数组。
  • 示例:
["apple", "banana", 100, true, null, {"city": "Beijing"}]

合法值类型

  • 字符串(双引号包裹,不可用单引号)
  • 数值(整型、浮点型,无引号)
  • 布尔值(truefalse,小写)
  • 空值(null,小写)
  • 对象({}包裹的键值对集合)
  • 数组([]包裹的值集合)

cJSON 库介绍

cJSON 是基于 C 语言的轻量级 JSON 解析 / 构造库,开源且跨平台(兼容 C89 标准),适用于嵌入式开发等场景。

获取与使用

  • 下载地址:https://github.com/DaveGamble/cJSON
  • 核心文件:仅需cJSON.c(实现文件)和cJSON.h(头文件),直接复制到项目中即可使用。
  • 编译方式:将cJSON.c与项目源码一起编译(如gcc main.c cJSON.c -o main)。

核心结构体(cJSON)

用于存储 JSON 数据的树形结构节点,每个节点对应一个 JSON 值:

/**
 * cJSON核心结构体,存储单个JSON节点数据
 * @brief 构成JSON树形结构的基本单元,每个节点对应一个JSON值(字符串、数字、对象等)
 * @param next 链表下一个节点指针(用于遍历数组/对象的子节点)
 * @param prev 链表上一个节点指针(用于双向遍历)
 * @param child 子节点指针(对象/数组类型节点的子元素链表头)
 * @param type 节点类型(如cJSON_String、cJSON_Number、cJSON_Object等)
 * @param valuestring 字符串类型值(仅type为cJSON_String或cJSON_Raw时有效)
 * @param valueint 整型值(type为cJSON_Number时有效,建议用cJSON_SetNumberValue设置)
 * @param valuedouble 浮点型值(type为cJSON_Number时有效)
 * @param string 节点名称(仅当节点是对象的子节点时有效,即键名)
 */
typedef struct cJSON {
    struct cJSON *next;
    struct cJSON *prev;
    struct cJSON *child;
    int type;
    char *valuestring;
    int valueint;
    double valuedouble;
    char *string;
} cJSON;

JSON 解析流程(cJSON 实现)

解析流程:获取 JSON 字符串 → 解析为 cJSON 树形结构 → 提取数据 → 释放资源

关键函数

解析 JSON 字符串

/**
 * 解析JSON格式字符串为cJSON树形结构
 * @brief 将零终止的JSON字符串转换为cJSON结构体指针,后续可通过该指针遍历提取数据
 * @param string 输入的JSON格式字符串(必须零终止,格式合法)
 * @return cJSON* 成功返回指向cJSON树形结构根节点的指针,失败返回NULL
 * @note 解析成功后需调用cJSON_Delete释放内存,避免内存泄漏
 *       若JSON字符串格式错误(如括号不匹配、键未用双引号),返回NULL
 */
cJSON *cJSON_Parse(const char *string);

调试输出 JSON

/**
 * 将cJSON树形结构转换为格式化的JSON字符串
 * @brief 用于调试验证解析结果,生成易读的JSON字符串
 * @param json 指向cJSON树形结构根节点的指针
 * @return char* 成功返回动态分配的JSON字符串,失败返回NULL
 * @note 返回的字符串需手动用free释放,否则会造成内存泄漏
 *       生成的字符串包含换行和缩进,便于人类阅读
 */
char *cJSON_Print(const cJSON *json);

获取对象中的键值对

/**
 * 从JSON对象中获取指定键名的子节点(不区分大小写)
 * @brief 用于提取对象类型节点中某个键对应的值节点
 * @param object 指向JSON对象节点的指针(type必须为cJSON_Object)
 * @param string 要查找的键名(字符串)
 * @return cJSON* 成功返回对应值节点的指针,失败返回NULL
 * @note 若对象中无该键,或传入的节点不是对象类型,返回NULL
 *       区分大小写版本可用cJSON_GetObjectItemCaseSensitive
 */
cJSON *cJSON_GetObjectItem(const cJSON *const object, const char *const string);

获取数组信息

/**
 * 获取JSON数组的元素个数
 * @brief 用于确定数组节点中包含的子元素数量
 * @param array 指向JSON数组节点的指针(type必须为cJSON_Array)
 * @return int 成功返回数组元素个数,失败返回0
 * @note 若传入的节点不是数组类型,返回0
 */
int cJSON_GetArraySize(const cJSON *array);

/**
 * 获取数组中指定索引的元素节点
 * @brief 按索引遍历数组,提取对应位置的子节点
 * @param array 指向JSON数组节点的指针(type必须为cJSON_Array)
 * @param index 元素索引(从0开始)
 * @return cJSON* 成功返回对应索引的元素节点指针,失败返回NULL
 * @note 索引超出数组范围(>=元素个数)时返回NULL
 */
cJSON *cJSON_GetArrayItem(const cJSON *array, int index);

释放资源

/**
 * 释放cJSON树形结构占用的内存
 * @brief 递归释放整个cJSON链表的内存,包括所有子节点
 * @param item 指向cJSON树形结构根节点的指针
 * @return void 无返回值
 * @note 必须在解析完成后调用,否则会造成严重内存泄漏
 *       仅需传入根节点指针,会自动递归释放所有子节点
 */
void cJSON_Delete(cJSON *item);

完整解析示例(解析天气 API 响应)

#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"

int main() {
    // 假设这是从API获取的JSON字符串
    const char *json_str = "{"
        "\"results\": ["
            "{"
                "\"path\": \"Beijing, Beijing, China\","
                "\"timezone\": \"Asia/Shanghai\","
                "\"timezone_offset\": \"+08:00\","
                "\"txt\": \"sunny\","
                "\"code\": \"0\","
                "\"temperature\": \"32\","
                "\"last_update\": \"2024-06-11T11:27:51+08:00\""
            "},"
            "{"
                "\"path\": \"Shanghai, Shanghai, China\","
                "\"timezone\": \"Asia/Shanghai\","
                "\"timezone_offset\": \"+08:00\","
                "\"txt\": \"cloudy\","
                "\"code\": \"1\","
                "\"temperature\": \"28\","
                "\"last_update\": \"2024-06-11T11:27:51+08:00\""
            "}"
        "]"
    "}";

    // 解析JSON
    cJSON *root = cJSON_Parse(json_str);
    if (root == NULL) {
        printf("JSON解析失败:%s\n", cJSON_GetErrorPtr());
        return -1;
    }

    // 获取results数组
    cJSON *results_array = cJSON_GetObjectItem(root, "results");
    if (results_array == NULL || !cJSON_IsArray(results_array)) {
        printf("未找到results数组\n");
        cJSON_Delete(root);
        return -1;
    }

    // 使用 cJSON_ArrayForEach 宏遍历数组
    // 宏的参数:当前元素节点指针, 数组节点指针
    cJSON *weather_obj;
    cJSON_ArrayForEach(weather_obj, results_array) {
        // 提取基础类型值
        cJSON *city = cJSON_GetObjectItem(weather_obj, "path");
        cJSON *weather = cJSON_GetObjectItem(weather_obj, "txt");
        cJSON *temp = cJSON_GetObjectItem(weather_obj, "temperature");
        cJSON *update_time = cJSON_GetObjectItem(weather_obj, "last_update");

        // 输出提取的信息
        if (city && cJSON_IsString(city) && weather && cJSON_IsString(weather) &&
            temp && cJSON_IsString(temp) && update_time && cJSON_IsString(update_time)) {
            printf("\n城市:%s\n", city->valuestring);
            printf("天气:%s\n", weather->valuestring);
            printf("温度:%s℃\n", temp->valuestring);
            printf("最后更新时间:%s\n", update_time->valuestring);
        }
    }

    // 释放资源
    cJSON_Delete(root);
    return 0;
}

JSON 构造流程(cJSON 实现)

构造流程:创建根对象 → 添加子节点 / 键值对 → 生成 JSON 字符串 → 释放资源

关键构造函数

/**
 * 创建JSON对象节点(对应{})
 * @brief 生成一个空的JSON对象节点,作为树形结构的根或子对象
 * @return cJSON* 成功返回对象节点指针,失败返回NULL
 * @note 需配合cJSON_AddItemToObject等函数添加键值对
 */
cJSON *cJSON_CreateObject(void);

/**
 * 创建JSON数组节点(对应[])
 * @brief 生成一个空的JSON数组节点
 * @return cJSON* 成功返回数组节点指针,失败返回NULL
 * @note 需配合cJSON_AddItemToArray等函数添加元素
 */
cJSON *cJSON_CreateArray(void);

/**
 * 创建字符串类型节点
 * @brief 生成存储指定字符串的JSON节点
 * @param string 要存储的字符串(零终止)
 * @return cJSON* 成功返回字符串节点指针,失败返回NULL
 */
cJSON *cJSON_CreateString(const char *string);

/**
 * 创建数值类型节点
 * @brief 生成存储指定浮点型数值的JSON节点(支持整型和浮点型)
 * @param num 要存储的数值(整型可直接传入,自动转换为double)
 * @return cJSON* 成功返回数值节点指针,失败返回NULL
 */
cJSON *cJSON_CreateNumber(double num);

/**
 * 向JSON对象添加键值对
 * @brief 将子节点作为指定键的值,添加到对象节点中
 * @param object 目标对象节点指针
 * @param string 键名(字符串)
 * @param item 要添加的子节点指针(任意JSON类型节点)
 * @return void 无返回值
 * @note 子节点的内存由cJSON管理,无需手动释放
 */
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);

/**
 * 向JSON数组添加元素
 * @brief 将子节点添加到数组节点的末尾
 * @param array 目标数组节点指针
 * @param item 要添加的子节点指针(任意JSON类型节点)
 * @return void 无返回值
 */
void cJSON_AddItemToArray(cJSON *array, cJSON *item);

完整构造示例

#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
int main() {
    // 创建顶层对象(根节点)
    cJSON *root = cJSON_CreateObject();
    if (root == NULL) {
        printf("创建根对象失败\n");
        return -1;
    }

    // 向根对象添加基础类型键值对
    cJSON_AddItemToObject(root, "name", cJSON_CreateString("zhangsan"));
    cJSON_AddItemToObject(root, "age", cJSON_CreateNumber(25));
    cJSON_AddItemToObject(root, "is_student", cJSON_CreateBool(0)); // false

    // 创建数组并添加元素
    cJSON *hobbies = cJSON_CreateArray();
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("reading"));
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("coding"));
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("hiking"));
    // 将数组添加到根对象
    cJSON_AddItemToObject(root, "hobbies", hobbies);

    // 创建嵌套对象并添加
    cJSON *address = cJSON_CreateObject();
    cJSON_AddItemToObject(address, "province", cJSON_CreateString("Guangdong"));
    cJSON_AddItemToObject(address, "city", cJSON_CreateString("Shenzhen"));
    cJSON_AddItemToObject(address, "detail", cJSON_CreateString("Nanshan District"));
    // 将嵌套对象添加到根对象
    cJSON_AddItemToObject(root, "address", address);

    // 生成JSON字符串(格式化输出)
    char *json_str = cJSON_Print(root);
    if (json_str == NULL) {
        printf("生成JSON字符串失败\n");
        cJSON_Delete(root);
        return -1;
    }
    printf("构造的JSON:\n%s\n", json_str);

    // 释放资源
    free(json_str);
    cJSON_Delete(root);

    return 0;
}

输出:

构造的JSON:
{
        "name": "zhangsan",
        "age":  25,
        "is_student":   false,
        "hobbies":      ["reading", "coding", "hiking"],
        "address":      {
                "province":     "Guangdong",
                "city": "Shenzhen",
                "detail":       "Nanshan District"
        }
}

实战练习:调用聚合天气 API 解析 JSON

核心步骤

  • 申请聚合天气 API 接口,获取 API 密钥。
  • 用 C 语言通过 HTTP 协议发送请求(如使用 libcurl 库),获取 JSON 格式的响应数据。
  • 用 cJSON 库解析响应数据,提取城市、天气、温度、风力等信息。
  • 将提取的信息输出到终端。

方式一:HTTP 请求实现(基于 libcurl)

需安装 libcurl 库(sudo apt-get install libcurl4-openssl-dev),编译时链接(gcc main.c cJSON.c -o main -lcurl):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include "cJSON.h" 

/**
 * @brief 用于存储动态增长的响应数据的结构体
 */
typedef struct {
    char *data;
    size_t size;
} ResponseBuffer;

/**
 * @brief libcurl 的回调函数,用于接收 HTTP 响应数据
 * 
 * @param ptr 指向接收到的数据的指针
 * @param size 每个数据块的大小(单位:字节)
 * @param nmemb 数据块的数量
 * @param userdata 用户自定义数据,这里是 ResponseBuffer 结构体指针
 * @return size_t 实际处理的数据大小(通常是 size * nmemb)
 */
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
    size_t realsize = size * nmemb;
    ResponseBuffer *buffer = (ResponseBuffer *)userdata;

    // 重新分配内存,为新数据腾出空间
    char *temp = (char *)realloc(buffer->data, buffer->size + realsize + 1);
    if (temp == NULL) {
        // 内存分配失败,这是一个严重错误
        fprintf(stderr, "Failed to allocate memory in write_callback\n");
        return 0; 
    }

    buffer->data = temp;
    // 将新数据复制到缓冲区末尾
    memcpy(&(buffer->data[buffer->size]), ptr, realsize);
    buffer->size += realsize;
    // 确保字符串以 null 字符结尾
    buffer->data[buffer->size] = '\0';

    return realsize;
}

/**
 * @brief 发送 HTTP GET 请求并返回响应体的 JSON 字符串
 * 
 * @param url 请求的 URL
 * @return char* 成功则返回动态分配的 JSON 字符串,失败则返回 NULL
 */
char *get_weather_json(const char *url) {
    CURL *curl;
    CURLcode res;
    ResponseBuffer buffer = {0}; // 初始化缓冲区

    // 全局初始化 libcurl (建议在程序开始时调用一次)
    curl_global_init(CURL_GLOBAL_DEFAULT);

    // 初始化一个 curl 会话
    curl = curl_easy_init();
    if (!curl) {
        fprintf(stderr, "curl_easy_init() failed\n");
        curl_global_cleanup();
        return NULL;
    }

    // 设置 curl 选项
    curl_easy_setopt(curl, CURLOPT_URL, url);
    // 设置回调函数和用户数据
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer);

    // 执行请求
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        free(buffer.data); // 释放已分配的内存
        buffer.data = NULL;
    }

    // 清理 curl 会话
    curl_easy_cleanup(curl);

    // 全局清理 libcurl (建议在程序结束时调用一次)
    curl_global_cleanup();

    // 返回响应数据(调用者需要负责 free)
    return buffer.data;
}

int main() {
    // !!! 重要:请替换为你的聚合数据 API 密钥 !!!
    const char *api_key = "你的API密钥";
    const char *city = "Beijing";
    char api_url[256];
    
    // 构建完整的 API 请求 URL
    snprintf(api_url, sizeof(api_url), 
             "http://v.juhe.cn/tianqi/query?city=%s&key=%s", 
             city, api_key);

    printf("正在请求天气数据...\nURL: %s\n", api_url);

    // 调用函数获取 JSON 字符串
    char *json_str = get_weather_json(api_url);
    if (json_str == NULL) {
        fprintf(stderr, "获取天气数据失败\n");
        return 1;
    }

    printf("\n成功获取到 JSON 数据:\n%s\n", json_str);

    // --- 使用 cJSON 解析 JSON 数据 ---
    cJSON *root = cJSON_Parse(json_str);
    if (root == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            fprintf(stderr, "JSON 解析失败: %s\n", error_ptr);
        }
        free(json_str); // 释放 JSON 字符串
        return 1;
    }

    // 检查返回状态
    cJSON *error_code = cJSON_GetObjectItem(root, "error_code");
    if (error_code && error_code->valueint != 0) {
        cJSON *reason = cJSON_GetObjectItem(root, "reason");
        fprintf(stderr, "API 请求失败: %s (错误码: %d)\n", 
                reason ? reason->valuestring : "未知错误", 
                error_code->valueint);
        cJSON_Delete(root);
        free(json_str);
        return 1;
    }

    // 获取 results 数组
    cJSON *results_array = cJSON_GetObjectItem(root, "result");
    if (results_array == NULL || !cJSON_IsArray(results_array)) {
        printf("未找到 'result' 数组\n");
        cJSON_Delete(root);
        free(json_str);
        return 1;
    }

    // 使用 cJSON_ArrayForEach 宏遍历数组
    cJSON *weather_obj;
    cJSON_ArrayForEach(weather_obj, results_array) {
        cJSON *city_name = cJSON_GetObjectItem(weather_obj, "city");
        cJSON *weather = cJSON_GetObjectItem(weather_obj, "weather");
        cJSON *temperature = cJSON_GetObjectItem(weather_obj, "temperature");
        cJSON *humidity = cJSON_GetObjectItem(weather_obj, "humidity");
        cJSON *wind = cJSON_GetObjectItem(weather_obj, "wind");
        cJSON *update_time = cJSON_GetObjectItem(weather_obj, "update_time");

        // 打印提取的信息
        printf("\n--------------------\n");
        printf("城市: %s\n", city_name ? city_name->valuestring : "N/A");
        printf("天气: %s\n", weather ? weather->valuestring : "N/A");
        printf("温度: %s\n", temperature ? temperature->valuestring : "N/A");
        printf("湿度: %s\n", humidity ? humidity->valuestring : "N/A");
        printf("风向: %s\n", wind ? wind->valuestring : "N/A");
        printf("更新时间: %s\n", update_time ? update_time->valuestring : "N/A");
    }

    // --- 释放所有资源 ---
    cJSON_Delete(root); // 释放 cJSON 对象树
    free(json_str);     // 释放从 get_weather_json 返回的字符串

    return 0;
}

方式二:popen 调用 curl 命令,并通过 fread 读取其输出

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
 * @brief 使用 popen 和 curl 命令获取 HTTP 响应
 * 
 * @param url 请求的 URL 地址
 * @return char* 成功则返回动态分配的响应字符串,失败则返回 NULL
 */
char *get_weather_json_with_popen(const char *url) {
    if (url == NULL) {
        return NULL;
    }

    // 构建 curl 命令
    // -s: 静默模式,不显示进度条等额外信息
    // "%s": 要请求的 URL
    char command[1024];
    int ret = snprintf(command, sizeof(command), "curl -s \"%s\"", url);
    if (ret < 0 || ret >= sizeof(command)) {
        fprintf(stderr, "命令字符串太长,可能会被截断。\n");
        return NULL;
    }

    // 使用 popen 执行命令,并以只读方式打开管道
    FILE *fp = popen(command, "r");
    if (fp == NULL) {
        perror("popen 失败");
        return NULL;
    }

    // 读取管道内容
    // 使用动态缓冲区来存储读取的数据
    char *response = NULL;
    size_t buffer_size = 1024; // 初始缓冲区大小
    size_t total_bytes_read = 0;

    // 初始分配内存
    response = (char *)malloc(buffer_size);
    if (response == NULL) {
        perror("malloc 失败");
        pclose(fp);
        return NULL;
    }
    response[0] = '\0'; // 确保初始为空字符串

    char chunk[256];
    size_t bytes_read;

    printf("正在通过 curl 命令获取数据...\n");

    // 循环读取,直到文件结束
    while ((bytes_read = fread(chunk, 1, sizeof(chunk) - 1, fp)) > 0) {
        // 确保 chunk 是一个以 null 结尾的字符串,方便 strcat
        chunk[bytes_read] = '\0';

        // 检查缓冲区是否足够容纳新读取的数据
        if (total_bytes_read + bytes_read >= buffer_size - 1) { // 预留一个字节给 null 结束符
            buffer_size *= 2; // 翻倍扩容
            char *temp = (char *)realloc(response, buffer_size);
            if (temp == NULL) {
                perror("realloc 失败");
                free(response);
                pclose(fp);
                return NULL;
            }
            response = temp;
        }

        // 将新读取的数据追加到响应字符串末尾
        strcat(response, chunk);
        total_bytes_read += bytes_read;
    }

    // 检查 fread 是否因为错误而退出
    if (ferror(fp)) {
        perror("fread 失败");
        free(response);
        response = NULL;
    }

    // 关闭管道
    // pclose 会等待子进程 (curl) 结束并回收其资源
    if (pclose(fp) == -1) {
        perror("pclose 失败");
        // 即使 pclose 失败,我们也已经读取了数据,所以可以选择返回它
        // 这里为了简化,我们在发生任何错误时都返回 NULL
        free(response);
        response = NULL;
    }
    
    return response;
}

int main() {
    // !!! 重要:请替换为你的聚合数据 API 密钥 !!!
    const char *api_key = "你的API密钥";
    const char *city = "Beijing";
    char api_url[256];
    
    // 构建完整的 API 请求 URL
    snprintf(api_url, sizeof(api_url), 
             "http://v.juhe.cn/tianqi/query?city=%s&key=%s", 
             city, api_key);

    // 调用函数获取 JSON 字符串
    char *json_str = get_weather_json_with_popen(api_url);
    if (json_str == NULL) {
        fprintf(stderr, "获取天气数据失败\n");
        return 1;
    }

    printf("成功获取到 JSON 数据:\n%s\n", json_str);

    // 在这里可以继续使用 cJSON 解析 json_str ...
    // ...

    // 释放动态分配的内存
    free(json_str);

    return 0;
}

拓展:Base64 编码 + 百度 AI 物体识别 API 实战

Base64 编码核心规则

Base64 是一种基于 64 个可打印字符的编码方式,用于将二进制数据转换为文本格式(便于 HTTP 传输):

  • 编码表:A-Z(0-25)、a-z(26-51)、0-9(52-61)、+(62)、/(63)。
  • 编码原理:3 个字节(24 位)拆分为 4 个 6 位组,每个 6 位组对应编码表中的一个字符。
  • 不足 3 字节处理:缺 1 字节补 2 个=,缺 2 字节补 1 个=

图片转 Base64 编码(C 语言实现)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Base64编码表
const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// 将图片文件转换为Base64字符串
char *image_to_base64(const char *img_path) {
    // 读取图片二进制数据
    FILE *fp = fopen(img_path, "rb");
    if (!fp) return NULL;
    fseek(fp, 0, SEEK_END);
    long img_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    unsigned char *img_data = malloc(img_size);
    fread(img_data, 1, img_size, fp);
    fclose(fp);

    // 计算Base64字符串长度
    long base64_len = (img_size + 2) / 3 * 4;
    char *base64_str = malloc(base64_len + 1);
    memset(base64_str, 0, base64_len + 1);

    // 编码核心逻辑
    for (long i = 0, j = 0; i < img_size; i += 3, j += 4) {
        // 取3个字节
        unsigned char byte1 = i < img_size ? img_data[i] : 0;
        unsigned char byte2 = i+1 < img_size ? img_data[i+1] : 0;
        unsigned char byte3 = i+2 < img_size ? img_data[i+2] : 0;

        // 拆分为4个6位组
        unsigned int triple = (byte1 << 16) | (byte2 << 8) | byte3;
        base64_str[j] = base64_table[(triple >> 18) & 0x3F];
        base64_str[j+1] = base64_table[(triple >> 12) & 0x3F];
        base64_str[j+2] = i+1 < img_size ? base64_table[(triple >> 6) & 0x3F] : '=';
        base64_str[j+3] = i+2 < img_size ? base64_table[triple & 0x3F] : '=';
    }

    free(img_data);
    return base64_str;
}

调用百度 AI 物体识别 API

  • 百度 AI 开放平台申请 “物体识别” API,获取 API Key 和 Secret Key,生成访问令牌(Access Token)。
  • 将图片 Base64 编码字符串构造为 JSON 请求体:
{
  "image": "图片的Base64编码(去掉前缀data:image/jpg;base64,)",
  "image_type": "BASE64"
}
  • 用 HTTP POST 请求发送 JSON 数据,接收响应后用 cJSON 解析识别结果(如物体名称、置信度等)。
posted @ 2025-11-26 21:40  YouEmbedded  阅读(24)  评论(0)    收藏  举报