生成图片的base64编码(纯C语言实现)

news2024/11/15 8:24:17

一、前言

Base64编码是一种广泛使用的编码方案,将任意二进制数据转换为可打印的ASCII字符字符串。这种编码方式之所以重要,是因为许多通信协议和存储介质对数据的可传输性和可存储性有特定的要求,它们可能无法直接处理或有效传输二进制数据。Base64编码通过使用64个字符的标准字符集——包括大写字母A-Z、小写字母a-z、数字0-9以及符号“+”和“/”,来表示二进制数据中的每一个6位组。为了标识编码的结束,Base64还使用了=作为填充字符。

image-20240716143247390

在实际应用中,Base64编码常见于电子邮件附件、在URLs中嵌入二进制数据、在网页中内联图像和字体文件、以及在配置文件和数据库中存储非文本数据等多种场景。例如,在HTML或CSS文件中,可以使用Base64编码的图像数据直接作为背景图像,而无需额外的HTTP请求,这在某些情况下可以提高页面加载速度,尽管这样做可能会增加文件大小,因为Base64编码通常会使原始数据膨胀约33%左右。

在C语言中,Base64编码的实现主要涉及几个关键步骤:首先,输入的二进制数据被分成6位的区块;然后,每个6位区块被映射到Base64字符集中相应的字符;接下来,如果最后一个区块不足6位,使用0进行填充,并添加等于号作为填充字符以保持输出的长度一致。

编码过程可以分解为以下步骤:

  1. 将输入的二进制数据读入内存缓冲区。
  2. 遍历缓冲区,每次取出24位数据(即3个字节),这足以生成4个Base64字符。
  3. 将这24位分为4个6位组。
  4. 使用6位组索引Base64字符集,找到对应的字符并输出。
  5. 如果到达缓冲区末尾时剩余不足24位,使用0填充剩余位数,并输出相应的Base64字符,同时在输出字符串末尾添加等于号作为填充。

在C语言中实现Base64编码时,可以定义一个包含64个字符的数组,存储Base64字符集,通过循环和位操作来处理数据。由于C语言提供了对内存和位操作的直接访问,因此在性能敏感的应用中,使用C语言实现的Base64编码可以非常高效。

二、代码实操

2.1 将二进制数据转为Base64编码

下面是C语言程序示例,将给定的一串二进制数据转换成Base64编码并打印出来。程序使用了标准库函数,并且没有依赖任何外部库。程序中包含了创建Base64编码所需的所有步骤,如初始化字符集、读取输入数据、编码数据并打印结果。

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

#define BASE64_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
#define CHUNK_SIZE 3
#define BASE64_CHUNK_SIZE 4

void base64_encode(unsigned char const *bytes_to_encode, unsigned int in_len, char *out_text)
{
    unsigned char const *in = bytes_to_encode;
    unsigned char *out = (unsigned char*)out_text;
    unsigned int i;
    unsigned int j;
    unsigned int val;

    for (i = 0, j = 0; i < in_len - 2; i += CHUNK_SIZE, j += BASE64_CHUNK_SIZE) {
        val = ((in[i] & 0xFC) >> 2);
        out[j] = BASE64_CHARS[val];

        val = ((in[i] & 0x03) << 4) | ((in[i + 1] & 0xF0) >> 4);
        out[j + 1] = BASE64_CHARS[val];

        val = ((in[i + 1] & 0x0F) << 2) | ((in[i + 2] & 0xC0) >> 6);
        out[j + 2] = BASE64_CHARS[val];

        val = (in[i + 2] & 0x3F);
        out[j + 3] = BASE64_CHARS[val];
    }

    // Handle the last chunk gracefully.
    switch (in_len % CHUNK_SIZE) {
    case 1:
        out[j] = BASE64_CHARS[((in[i] & 0xFC) >> 2)];
        out[j + 1] = BASE64_CHARS[((in[i] & 0x03) << 4)];
        out[j + 2] = '=';
        out[j + 3] = '=';
        break;

    case 2:
        val = ((in[i] & 0xFC) >> 2);
        out[j] = BASE64_CHARS[val];

        val = ((in[i] & 0x03) << 4) | ((in[i + 1] & 0xF0) >> 4);
        out[j + 1] = BASE64_CHARS[val];

        out[j + 2] = BASE64_CHARS[((in[i + 1] & 0x0F) << 2)];
        out[j + 3] = '=';
        break;
    }
}

int main()
{
    unsigned char data[] = {0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x27, 0x0b, 0xcf, 0xa3, 0x57, 0x67};
    unsigned int data_len = sizeof(data);
    char encoded_data[100]; // Assuming enough space for the encoded string.

    base64_encode(data, data_len, encoded_data);
    encoded_data[data_len * 4 / 3] = '\0'; // Null terminate the string.

    printf("Original data: ");
    for (int i = 0; i < data_len; i++) {
        printf("%02x ", data[i]);
    }
    printf("\n");

    printf("Encoded data: %s\n", encoded_data);

    return 0;
}

在例子中,data 数组包含了要被编码的数据。base64_encode 函数接受这些数据,并将其转换为Base64编码。编码后的字符串被存储在 encoded_data 数组中。注意,encoded_data 数组的大小应该足够容纳编码后的字符串,因为Base64编码后的字符串长度通常是原始数据长度的4/3倍。

这个程序将打印出原始数据和编码后的Base64字符串。可以根据需要修改 data 数组的内容,以便测试不同的输入。

image-20240716142913231

2.2 实现图片的base64编码和解码

下面是一个完整的C语言程序,实现了将图片文件编码为Base64字符串,并且可以将Base64字符串解码为图片并保存到本地磁盘。这个示例程序使用标准C库,不依赖于任何第三方库。

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

// 函数:将二进制数据编码为Base64字符串
char* base64_encode(const unsigned char* src, size_t len) {
    static const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    char* out, * pos;
    const unsigned char* end, * in;

    size_t olen;
    int line_len;

    olen = len * 4 / 3 + 4; // 输出长度
    olen += olen / 72; // 换行符
    olen++; // 结尾的NULL字符
    out = (char*)malloc(olen);
    if (out == NULL) return NULL;

    end = src + len;
    in = src;
    pos = out;
    line_len = 0;
    while (end - in >= 3) {
        *pos++ = base64_table[in[0] >> 2];
        *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
        *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
        *pos++ = base64_table[in[2] & 0x3f];
        in += 3;
        if (line_len += 4, line_len == 72) {
            *pos++ = '\n';
            line_len = 0;
        }
    }

    if (end - in) {
        *pos++ = base64_table[in[0] >> 2];
        if (end - in == 1) {
            *pos++ = base64_table[(in[0] & 0x03) << 4];
            *pos++ = '=';
        }
        else {
            *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
            *pos++ = base64_table[(in[1] & 0x0f) << 2];
        }
        *pos++ = '=';
    }

    *pos = '\0';
    return out;
}

// 函数:将Base64字符串解码为二进制数据
unsigned char* base64_decode(const char* src, size_t* out_len) {
    static const unsigned char base64_table[] = {
        0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,  // +10
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff,  // +20
        0xff, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,  // +30
        0x3c, 0x3d, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x3f,  // +40
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03,  // +50
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,  // +60
        0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,  // +70
        0x18, 0x19, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,  // +80
        0xff, 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,  // +90
        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,  // +100
        0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33          // +110
    };

    unsigned char dtable[256], * out, * pos, block[4], tmp;
    size_t i, count, olen;
    int pad = 0;

    memset(dtable, 0x80, 256);
    for (i = 0; i < sizeof(base64_table); i++)
        dtable[base64_table[i]] = (unsigned char)i;
    dtable['='] = 0;

    count = 0;
    for (i = 0; i < strlen(src); i++) {
        if (dtable[src[i]] != 0x80)
            count++;
    }

    if (count == 0 || count % 4)
        return NULL;

    olen = count / 4 * 3;
    pos = out = (unsigned char*)malloc(olen);
    if (out == NULL) return NULL;

    for (i = 0; i < strlen(src); i++) {
        tmp = dtable[src[i]];
        if (tmp == 0x80) continue;

        if (src[i] == '=')
            pad++;
        block[count++] = tmp;
        if (count == 4) {
            *pos++ = (block[0] << 2) | (block[1] >> 4);
            *pos++ = (block[1] << 4) | (block[2] >> 2);
            *pos++ = (block[2] << 6) | block[3];
            count = 0;
            if (pad) {
                if (pad == 1) pos--;
                else if (pad == 2) pos -= 2;
                else {
                    free(out);
                    return NULL;
                }
                break;
            }
        }
    }

    *out_len = pos - out;
    return out;
}

int main() {
    FILE* fp;
    char* base64_data;
    unsigned char* decoded_data;
    size_t decoded_len, base64_len;
    char* filename = "test.png"; // 替换为你的图片文件名
    char* output_filename = "decoded_image.png"; // 解码后保存的文件名

    // 读取图片文件
    fp = fopen(filename, "rb");
    if (!fp) {
        fprintf(stderr, "无法打开文件 %s\n", filename);
        return 1;
    }
    fseek(fp, 0, SEEK_END);
    base64_len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    unsigned char* image_data = (unsigned char*)malloc(base64_len);
    fread(image_data, 1, base64_len, fp);
    fclose(fp);

    // 将图片数据编码为Base64字符串
    base64_data = base64_encode(image_data, base64_len);
    free(image_data);

    if (!base64_data) {
        fprintf(stderr, "Base64 编码失败\n");
        return 1;
    }

    // 输出Base64编码后的数据
    printf("Base64 编码结果:\n%s\n", base64_data);

    // 解码Base64字符串为图片数据
    decoded_data = base64_decode(base64_data, &decoded_len);
    free(base64_data);

    if (!decoded_data) {
        fprintf(stderr, "Base64 解码失败\n");
        return 1;
    }

    // 将解码后的图片数据保存为文件
    fp = fopen(output_filename, "wb");
    if (!fp) {
        fprintf(stderr, "无法打开文件 %s 进行写入\n", output_filename);
        free(decoded_data);
        return 1;
    }
    fwrite(decoded_data, 1, decoded_len, fp);
    fclose(fp);
    free(decoded_data);

    printf("图片已成功解码并保存到 %s\n", output_filename);

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2096992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[进阶]面向对象之static关键字

文章目录 一、什么是static关键字为什么要设置static关键字二、static的使用规则1、static修饰变量:2.static的修饰方法 练习&#xff08;定义数组工具类&#xff09;练习2&#xff08;定义学生工具类&#xff09;三、static的注意事项 一、什么是static关键字 static关键字是…

美国高防服务器运行《黑神话:悟空》配置需求及优化方法

《黑神话&#xff1a;悟空》作为一款备受关注的动作角色扮演游戏&#xff0c;其对计算资源和图形处理能力的需求相当高。在这种背景下&#xff0c;云服务器成为了一个能够提供强大支持的选项。下面将分析美国高防服务器在运行《黑神话&#xff1a;悟空》时的配置需求及优化方法…

微服务CI/CD实践(五)Jenkins Docker 自动化构建部署Node服务

微服务CI/CD实践系列&#xff1a; 微服务CI/CD实践&#xff08;一&#xff09;环境准备及虚拟机创建 微服务CI/CD实践&#xff08;二&#xff09;服务器先决准备 微服务CI/CD实践&#xff08;三&#xff09;gitlab部署及nexus3部署 微服务CI/CD实践&#xff08;四&#xff09…

为什么现在那么多人用骨传导耳机?怎么选骨传导耳机?答案来了!

随着健康生活观念的深入人心&#xff0c;运动健身已成为一种生活态度&#xff0c;而音乐则是这场健康之旅中不可或缺的伴侣。然而&#xff0c;传统耳机在运动时的不便促使市场呼唤更加人性化的产品。骨传导耳机的出现&#xff0c;正是对这一需求的精准把握。它采用非入耳技术&a…

VTK平面切割

文章目录 一、vtkClipPolyData二、CapClip三、SolidClip四、vtkClipClosedSurface 本文的主要内容&#xff1a;简单介绍VTK中通过平面切割模型的相关功能。 哪些人适合阅读本文&#xff1a;有一定VTK基础的人。 一、vtkClipPolyData VTK官网描述&#xff1a; vtkClipPolyData使…

AI绘画 | Stable diffusion 修复老照片

大家好呀&#xff01;今天我要和大家分享一个使用Stable diffusion修复老照片的功能。 你有没有遇到这样的问题&#xff1a;想要修复一张老照片&#xff0c;但是发现照片质量太差&#xff0c;噪点太多&#xff0c;根本无法修复&#xff1f; 别担心&#xff01;Stable diffusio…

DS18B20温度传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.工作时序 3.工作原理&#xff1a;复位脉冲与应答脉冲 4.工作原理&#xff1a;写时序 5.工作原理&#xff1a;读时序 6.工作原理&#xff1a;DS18B20读取的数据格式 7.工作原理&#xff1a;DS18B20配置步骤 三、程序设计 ma…

vue 批量自动引入并注册组件或路由

有时候有大量的组件.vue后缀的,或.js,或.ts文件, 需要一个个的手动引入很麻烦,那么你可以尝试这样创建一个index.js 本项目使用vue3.x, vue2.x也可以照样用; 这里在components里面创建了一个idnex.js 文件 require.context 可以读取文件, 第一个参数是指当前文件夹, 第二个参…

路径优化 minimum-snap(对A*的全局路径进行优化)

实现效果&#xff1a; 介绍&#xff1a; 使用Astar进行路径规划&#xff0c;使用minimum-snap进行路径优化处理&#xff0c;建议参考文章&#xff1a; 【附源码和详细的公式推导】Minimum Snap轨迹生成&#xff0c;闭式求解Minimum Snap问题&#xff0c;机器人轨迹优化&#…

泥水位监测站——水文百科

传统的水位监测往往依赖于人工定期巡检&#xff0c;这不仅费时费力&#xff0c;而且可能受到天气、交通等多种因素的限制。而水位监测站的全自动化监测则能够24小时不间断地工作&#xff0c;无需人员现场值守&#xff0c;从而显著减少了人工巡检的次数和成本。自动化监测系统能…

全面解析BotSharp:如何利用.Net和LLMs革新你的业务系统

作为一名关注.Net与AI相关技术的博主&#xff0c;今天我要推荐一个开源项目——BotSharp。BotSharp 是一个基于 .Net的智能代理应用程序构建框架&#xff0c;专注于连接大型语言模型&#xff08;LLMs&#xff09;到现有业务系统中&#xff0c;以促进会话即平台(Conversation as…

模型案例:| 垃圾桶识别模型

导读 2023年以ChatGPT为代表的大语言模型横空出世&#xff0c;它的出现标志着自然语言处理领域取得了重大突破。它在文本生成、对话系统和语言理解等方面展现出了强大的能力&#xff0c;为人工智能技术的发展开辟了新的可能性。同时&#xff0c;人工智能技术正在进入各种应用领…

【Java|Stream流】不可变集合

文章目录 1.什么是不可变集合2.创建不可变集合的方式2.1 List类型2.2 Set2.3 Map 1.什么是不可变集合 不可变集合:不可以被修改的集合 不可变集合优点: 安全性 由于不可变集合不能被修改&#xff0c;所以可以安全地在多个线程之间共享&#xff0c;而不用担心被意外修改&#xf…

H264编码

H264是一种针对视频的压缩编码方式。 一、压缩方法 H264主要基于以下几种方法&#xff0c;将数据进行压缩&#xff1a; 1.帧内预测压缩&#xff1a;解决空间域数据冗余的问题 2.帧间预测压缩&#xff1a;&#xff08;运动估计与补偿&#xff09;解决时间域数据冗余的问题 3…

AD5270 AD5271 STM32 SPI驱动设计

硬件设计 AD5270 AD5271 简介&#xff1a;1024位、1%电阻容差误差、SPI接口和50-TP存储器数字变阻器。特性&#xff1a;单通道、1024/256位分辨率&#xff1b;标称电阻&#xff1a;20 kΩ&#xff0c;50 kΩ和100 kΩ&#xff1b;标称电阻容差误差&#xff1a;1%&#xff08;最…

Android 11添加系统服务,并封装jar包供第三方应用使用

概述&#xff1a; 如果你是做技术支持&#xff0c;有没有遇到这种情况&#xff0c;客户既要实现具备系统权限的功能&#xff0c;但是呢&#xff0c;又不想把自己的应用做成系统应用。这时候你咋办。 我们可以添加一个具备系统权限的服务&#xff0c;不管前台的&#xff0c;还是…

原来这就是 布隆过滤器

1.布隆过滤器的引出 一个有趣的现象 不知道大家有没有发现这么一个现象&#xff0c;当我们在使用一些软件的时候&#xff0c;比如像 CSDN、这种具有推荐算法的应用&#xff0c;他并不会给我们推送我们已经浏览过的内容&#xff0c;这是怎么做到的呢&#xff1f; 说白了就是人…

【物理教学】高中物理速度时间练习

速度时间图像代码 这段代码是一个使用Python编写的脚本&#xff0c;它利用matplotlib库来绘制物理问题中的速度-时间图。代码的主要优点如下&#xff1a; 用户交互&#xff1a;代码通过input函数与用户进行交互&#xff0c;允许用户输入物理问题的参数&#xff0c;如初始速度…

钢结构厂房通风天窗使用场景探讨

钢结构厂房通风天窗作为现代建筑中高效通风的解决方案&#xff0c;广泛应用于多个领域&#xff0c;为各类建筑提供优质的室内环境。成都昱合昇带大家一起探讨通风天窗在不同使用场景下的表现。 1、工业厂房降温 工业厂房是通风天窗典型的应用场景之一。在高温季节或生产过程中产…

苏州科技大学商学院:加强生态保护,推动绿色发展

原标题&#xff1a;苏州科技大学商学院&#xff1a;加强生态保护&#xff0c;推动绿色发展&#xff0c;在美丽中国建设中贡献青春力量 建设美丽中国是全面建设社会主义现代化国家的重要目标&#xff0c;也是激励全国人民为实现中华民族伟大复兴中国梦而共同奋斗的伟大旗帜。中…