如何在 C 中使用 base64编码(解码) ?

我在一个无符号的 char 变量中有二进制数据。 我需要将它们转换成 c 中的 PEM base64。 我在 openssl 库中查找,但找不到任何函数。 有人知道吗?

374156 次浏览

但是您也可以在 openssl 中执行(openssl enc命令执行... .) ,看看 BIO_f_base64()函数

GNU coreutils 在 lib/base64中有它。有点臃肿,但是处理像 EBCDIC 这样的东西。你也可以自己玩,例如:

char base64_digit (n) unsigned n; {
if (n < 10) return n - '0';
else if (n < 10 + 26) return n - 'a';
else if (n < 10 + 26 + 26) return n - 'A';
else assert(0);
return 0;
}


unsigned char base64_decode_digit(char c) {
switch (c) {
case '=' : return 62;
case '.' : return 63;
default  :
if (isdigit(c)) return c - '0';
else if (islower(c)) return c - 'a' + 10;
else if (isupper(c)) return c - 'A' + 10 + 26;
else assert(0);
}
return 0xff;
}


unsigned base64_decode(char *s) {
char *p;
unsigned n = 0;


for (p = s; *p; p++)
n = 64 * n + base64_decode_digit(*p);


return n;
}

通过这些礼物,你们要知道,你们不应该把“自己玩”和“执行标准”混为一谈真恶心。

这是我用了很多年的解码器。

    static const char  table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int   BASE64_INPUT_SIZE = 57;


BOOL isbase64(char c)
{
return c && strchr(table, c) != NULL;
}


inline char value(char c)
{
const char *p = strchr(table, c);
if(p) {
return p-table;
} else {
return 0;
}
}


int UnBase64(unsigned char *dest, const unsigned char *src, int srclen)
{
*dest = 0;
if(*src == 0)
{
return 0;
}
unsigned char *p = dest;
do
{


char a = value(src[0]);
char b = value(src[1]);
char c = value(src[2]);
char d = value(src[3]);
*p++ = (a << 2) | (b >> 4);
*p++ = (b << 4) | (c >> 2);
*p++ = (c << 6) | d;
if(!isbase64(src[1]))
{
p -= 2;
break;
}
else if(!isbase64(src[2]))
{
p -= 2;
break;
}
else if(!isbase64(src[3]))
{
p--;
break;
}
src += 4;
while(*src && (*src == 13 || *src == 10)) src++;
}
while(srclen-= 4);
*p = 0;
return p-dest;
}

我写了一个用于 C + + 的程序,它非常快,可以处理流,免费和开源:

Https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/

如果符合你的用途,请随意使用。

编辑: 根据请求内联添加代码。

性能的提高是通过使用一个用于编码和解码的查找表来实现的。_UINT8是大多数操作系统的 unsigned char

/** Static Base64 character encoding lookup table */
const char CBase64::encodeCharacterTable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


/** Static Base64 character decoding lookup table */
const char CBase64::decodeCharacterTable[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21
,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1};


/*!
\brief Encodes binary data to base 64 character data
\param in The data to encode
\param out The encoded data as characters
*/
void CBase64::Encode(std::istream &in, std::ostringstream &out)
{
char buff1[3];
char buff2[4];
_UINT8 i=0, j;
while(in.readsome(&buff1[i++], 1))
if (i==3)
{
out << encodeCharacterTable[(buff1[0] & 0xfc) >> 2];
out << encodeCharacterTable[((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4)];
out << encodeCharacterTable[((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6)];
out << encodeCharacterTable[buff1[2] & 0x3f];
i=0;
}


if (--i)
{
for(j=i;j<3;j++) buff1[j] = '\0';


buff2[0] = (buff1[0] & 0xfc) >> 2;
buff2[1] = ((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4);
buff2[2] = ((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6);
buff2[3] = buff1[2] & 0x3f;


for (j=0;j<(i+1);j++) out << encodeCharacterTable[buff2[j]];


while(i++<3) out << '=';
}


}


/*!
\brief Decodes base 64 character data to binary data
\param in The character data to decode
\param out The decoded data
*/
void CBase64::Decode(std::istringstream &in, std::ostream &out)
{
char buff1[4];
char buff2[4];
_UINT8 i=0, j;


while(in.readsome(&buff2[i], 1) && buff2[i] != '=')
{
if (++i==4)
{
for (i=0;i!=4;i++)
buff2[i] = decodeCharacterTable[buff2[i]];


out << (char)((buff2[0] << 2) + ((buff2[1] & 0x30) >> 4));
out << (char)(((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2));
out << (char)(((buff2[2] & 0x3) << 6) + buff2[3]);


i=0;
}
}


if (i)
{
for (j=i;j<4;j++) buff2[j] = '\0';
for (j=0;j<4;j++) buff2[j] = decodeCharacterTable[buff2[j]];


buff1[0] = (buff2[0] << 2) + ((buff2[1] & 0x30) >> 4);
buff1[1] = ((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2);
buff1[2] = ((buff2[2] & 0x3) << 6) + buff2[3];


for (j=0;j<(i-1); j++) out << (char)buff1[j];
}
}

我用的是这个:

#include <stdint.h>
#include <stdlib.h>




static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'};
static char *decoding_table = NULL;
static int mod_table[] = {0, 2, 1};




char *base64_encode(const unsigned char *data,
size_t input_length,
size_t *output_length) {


*output_length = 4 * ((input_length + 2) / 3);


char *encoded_data = malloc(*output_length);
if (encoded_data == NULL) return NULL;


for (int i = 0, j = 0; i < input_length;) {


uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;


uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;


encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
}


for (int i = 0; i < mod_table[input_length % 3]; i++)
encoded_data[*output_length - 1 - i] = '=';


return encoded_data;
}




unsigned char *base64_decode(const char *data,
size_t input_length,
size_t *output_length) {


if (decoding_table == NULL) build_decoding_table();


if (input_length % 4 != 0) return NULL;


*output_length = input_length / 4 * 3;
if (data[input_length - 1] == '=') (*output_length)--;
if (data[input_length - 2] == '=') (*output_length)--;


unsigned char *decoded_data = malloc(*output_length);
if (decoded_data == NULL) return NULL;


for (int i = 0, j = 0; i < input_length;) {


uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];


uint32_t triple = (sextet_a << 3 * 6)
+ (sextet_b << 2 * 6)
+ (sextet_c << 1 * 6)
+ (sextet_d << 0 * 6);


if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
}


return decoded_data;
}




void build_decoding_table() {


decoding_table = malloc(256);


for (int i = 0; i < 64; i++)
decoding_table[(unsigned char) encoding_table[i]] = i;
}




void base64_cleanup() {
free(decoding_table);
}

请记住,这不会做任何错误检查,而解码-非基64编码的数据将得到处理。

Libb64 同时具有 C 和 C + + API。它是轻量级的,可能是最快的公开可用实现。它还是一个专用的独立 base64编码库,如果您不需要使用 OpenSSL 或 glib 这样的大型库所提供的所有其他内容,那么这个库会很棒。

我需要在 字符串上实现 C + + 。没有一个答案能满足我的需求,我需要一个简单的两功能的解决方案来进行编码和解码,但是我太懒了,没有写自己的代码,所以我发现了这个:

Http://www.adp-gmbh.ch/cpp/common/base64.html

代码的学分归于勒内 · 尼芬格。

下面的代码是为了防止网站崩溃:

Base64.cpp

/*
base64.cpp and base64.h


Copyright (C) 2004-2008 René Nyffenegger


This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.


Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:


1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.


2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.


3. This notice may not be removed or altered from any source distribution.


René Nyffenegger rene.nyffenegger@adp-gmbh.ch


*/


#include "base64.h"
#include <iostream>


static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";




static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}


std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];


while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;


for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}


if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';


char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;


for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];


while((i++ < 3))
ret += '=';


}


return ret;


}


std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;


while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);


char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];


for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}


if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;


for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);


char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];


for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}


return ret;
}

Base64.h

#include <string>


std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

用法

const std::string s = "test";
std::string encoded = base64_encode(reinterpret_cast<const unsigned char*>(s.c_str()), s.length());
std::string decoded = base64_decode(encoded);

以下是我使用 OpenSSL 的解决方案。

/* A BASE-64 ENCODER AND DECODER USING OPENSSL */
#include <openssl/pem.h>
#include <string.h> //Only needed for strlen().


char *base64encode (const void *b64_encode_this, int encode_this_many_bytes){
BIO *b64_bio, *mem_bio;      //Declares two OpenSSL BIOs: a base64 filter and a memory BIO.
BUF_MEM *mem_bio_mem_ptr;    //Pointer to a "memory BIO" structure holding our base64 data.
b64_bio = BIO_new(BIO_f_base64());                      //Initialize our base64 filter BIO.
mem_bio = BIO_new(BIO_s_mem());                           //Initialize our memory sink BIO.
BIO_push(b64_bio, mem_bio);            //Link the BIOs by creating a filter-sink BIO chain.
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);  //No newlines every 64 characters or less.
BIO_write(b64_bio, b64_encode_this, encode_this_many_bytes); //Records base64 encoded data.
BIO_flush(b64_bio);   //Flush data.  Necessary for b64 encoding, because of pad characters.
BIO_get_mem_ptr(mem_bio, &mem_bio_mem_ptr);  //Store address of mem_bio's memory structure.
BIO_set_close(mem_bio, BIO_NOCLOSE);   //Permit access to mem_ptr after BIOs are destroyed.
BIO_free_all(b64_bio);  //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one).
BUF_MEM_grow(mem_bio_mem_ptr, (*mem_bio_mem_ptr).length + 1);   //Makes space for end null.
(*mem_bio_mem_ptr).data[(*mem_bio_mem_ptr).length] = '\0';  //Adds null-terminator to tail.
return (*mem_bio_mem_ptr).data; //Returns base-64 encoded data. (See: "buf_mem_st" struct).
}


char *base64decode (const void *b64_decode_this, int decode_this_many_bytes){
BIO *b64_bio, *mem_bio;      //Declares two OpenSSL BIOs: a base64 filter and a memory BIO.
char *base64_decoded = calloc( (decode_this_many_bytes*3)/4+1, sizeof(char) ); //+1 = null.
b64_bio = BIO_new(BIO_f_base64());                      //Initialize our base64 filter BIO.
mem_bio = BIO_new(BIO_s_mem());                         //Initialize our memory source BIO.
BIO_write(mem_bio, b64_decode_this, decode_this_many_bytes); //Base64 data saved in source.
BIO_push(b64_bio, mem_bio);          //Link the BIOs by creating a filter-source BIO chain.
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);          //Don't require trailing newlines.
int decoded_byte_index = 0;   //Index where the next base64_decoded byte should be written.
while ( 0 < BIO_read(b64_bio, base64_decoded+decoded_byte_index, 1) ){ //Read byte-by-byte.
decoded_byte_index++; //Increment the index until read of BIO decoded data is complete.
} //Once we're done reading decoded data, BIO_read returns -1 even though there's no error.
BIO_free_all(b64_bio);  //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one).
return base64_decoded;        //Returns base-64 decoded data with trailing null terminator.
}


/*Here's one way to base64 encode/decode using the base64encode() and base64decode functions.*/
int main(void){
char data_to_encode[] = "Base64 encode this string!";  //The string we will base-64 encode.


int bytes_to_encode = strlen(data_to_encode); //Number of bytes in string to base64 encode.
char *base64_encoded = base64encode(data_to_encode, bytes_to_encode);   //Base-64 encoding.


int bytes_to_decode = strlen(base64_encoded); //Number of bytes in string to base64 decode.
char *base64_decoded = base64decode(base64_encoded, bytes_to_decode);   //Base-64 decoding.


printf("Original character string is: %s\n", data_to_encode);  //Prints our initial string.
printf("Base-64 encoded string is: %s\n", base64_encoded);  //Prints base64 encoded string.
printf("Base-64 decoded string is: %s\n", base64_decoded);  //Prints base64 decoded string.


free(base64_encoded);                //Frees up the memory holding our base64 encoded data.
free(base64_decoded);                //Frees up the memory holding our base64 decoded data.
}

下面是一个编码器的优化版本,它也支持 MIME 和其他协议的分行(类似的优化可以应用到解码器) :

 char *base64_encode(const unsigned char *data,
size_t input_length,
size_t *output_length,
bool addLineBreaks)


*output_length = 4 * ((input_length + 2) / 3);
if (addLineBreaks) *output_length += *output_length / 38; //  CRLF after each 76 chars


char *encoded_data = malloc(*output_length);
if (encoded_data == NULL) return NULL;


UInt32 octet_a;
UInt32 octet_b;
UInt32 octet_c;
UInt32 triple;
int lineCount = 0;
int sizeMod = size - (size % 3); // check if there is a partial triplet
// adding all octet triplets, before partial last triplet
for (; offset < sizeMod; )
{
octet_a = data[offset++];
octet_b = data[offset++];
octet_c = data[offset++];


triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;


encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];
if (addLineBreaks)
{
if (++lineCount == 19)
{
encoded_data[mBufferPos++] = 13;
encoded_data[mBufferPos++] = 10;
lineCount = 0;
}
}
}


// last bytes
if (sizeMod < size)
{
octet_a = data[offset++]; // first octect always added
octet_b = offset < size ? data[offset++] : (UInt32)0; // conditional 2nd octet
octet_c = (UInt32)0; // last character is definitely padded


triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;


encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];


// add padding '='
sizeMod = size % 3;
// last character is definitely padded
encoded_data[mBufferPos - 1] = (byte)'=';
if (sizeMod == 1) encoded_data[mBufferPos - 2] = (byte)'=';
}
}

如果人们需要 c + + 解决方案,我将这个 OpenSSL 解决方案放在一起(同时用于编码和解码)。您需要链接到“ crypto”库(即 OpenSSL)。这已经通过 valGraduate 检查了泄漏(尽管您可以添加一些额外的错误检查代码来使其更好一些——我知道 write 函数至少应该检查返回值)。

#include <openssl/bio.h>
#include <openssl/evp.h>
#include <stdlib.h>


string base64_encode( const string &str ){


BIO *base64_filter = BIO_new( BIO_f_base64() );
BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL );


BIO *bio = BIO_new( BIO_s_mem() );
BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL );


bio = BIO_push( base64_filter, bio );


BIO_write( bio, str.c_str(), str.length() );


BIO_flush( bio );


char *new_data;


long bytes_written = BIO_get_mem_data( bio, &new_data );


string result( new_data, bytes_written );
BIO_free_all( bio );


return result;


}






string base64_decode( const string &str ){


BIO *bio, *base64_filter, *bio_out;
char inbuf[512];
int inlen;
base64_filter = BIO_new( BIO_f_base64() );
BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL );


bio = BIO_new_mem_buf( (void*)str.c_str(), str.length() );


bio = BIO_push( base64_filter, bio );


bio_out = BIO_new( BIO_s_mem() );


while( (inlen = BIO_read(bio, inbuf, 512)) > 0 ){
BIO_write( bio_out, inbuf, inlen );
}


BIO_flush( bio_out );


char *new_data;
long bytes_written = BIO_get_mem_data( bio_out, &new_data );


string result( new_data, bytes_written );


BIO_free_all( bio );
BIO_free_all( bio_out );


return result;


}

这个解决方案基于 schulwitz answer (使用 OpenSSL 进行编码/解码) ,但是是针对 C + + 的(好吧,最初的问题是关于 C 的,但是这里已经有了另一个 C + + 的答案) ,并且它使用错误检查(所以使用起来更安全) :

#include <openssl/bio.h>


std::string base64_encode(const std::string &input)
{
BIO *p_bio_b64 = nullptr;
BIO *p_bio_mem = nullptr;


try
{
// make chain: p_bio_b64 <--> p_bio_mem
p_bio_b64 = BIO_new(BIO_f_base64());
if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less


p_bio_mem = BIO_new(BIO_s_mem());
if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
BIO_push(p_bio_b64, p_bio_mem);


// write input to chain
// write sequence: input -->> p_bio_b64 -->> p_bio_mem
if (BIO_write(p_bio_b64, input.c_str(), input.size()) <= 0)
{ throw std::runtime_error("BIO_write failed"); }


if (BIO_flush(p_bio_b64) <= 0)
{ throw std::runtime_error("BIO_flush failed"); }


// get result
char *p_encoded_data = nullptr;
auto  encoded_len    = BIO_get_mem_data(p_bio_mem, &p_encoded_data);
if (!p_encoded_data) { throw std::runtime_error("BIO_get_mem_data failed"); }


std::string result(p_encoded_data, encoded_len);


// clean
BIO_free_all(p_bio_b64);


return result;
}
catch (...)
{
if (p_bio_b64) { BIO_free_all(p_bio_b64); }
throw;
}
}


std::string base64_decode(const std::string &input)
{
BIO *p_bio_mem = nullptr;
BIO *p_bio_b64 = nullptr;


try
{
// make chain: p_bio_b64 <--> p_bio_mem
p_bio_b64 = BIO_new(BIO_f_base64());
if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines


p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
BIO_push(p_bio_b64, p_bio_mem);


// read result from chain
// read sequence (reverse to write): buf <<-- p_bio_b64 <<-- p_bio_mem
std::vector<char> buf((input.size()*3/4)+1);
std::string result;
for (;;)
{
auto nread = BIO_read(p_bio_b64, buf.data(), buf.size());
if (nread  < 0) { throw std::runtime_error("BIO_read failed"); }
if (nread == 0) { break; } // eof


result.append(buf.data(), nread);
}


// clean
BIO_free_all(p_bio_b64);


return result;
}
catch (...)
{
if (p_bio_b64) { BIO_free_all(p_bio_b64); }
throw;
}
}

注意,如果输入的 base64序列不正确,base64 _ decode 将返回空字符串(openssl 就是这样工作的)。

我知道这个问题很老了,但是我被提供的解决方案的数量搞糊涂了——每个方案都声称自己更快更好。我在 github 上整合了一个项目来比较 base64编码器和解码器: https://github.com/gaspardpetit/base64/

此时,我并没有将自己局限于 C 算法-如果一个实现在 C + + 中表现良好,那么它可以很容易地被反向移植到 C。如果有人想用 clang/gcc 的结果更新这个答案,请自便。

最快编码器: 我找到的两个最快的编码器实现是 Jouni Malinen 在 http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c的和 Apache 在 https://opensource.apple.com/source/QuickTimeStreamingServer/QuickTimeStreamingServer-452/CommonUtilitiesLib/base64.c的。

以下是使用我迄今为止测试过的不同算法对32K 数据进行编码的时间(以微秒为单位) :

jounimalinen                25.1544
apache                      25.5309
NibbleAndAHalf              38.4165
internetsoftwareconsortium  48.2879
polfosol                    48.7955
wikibooks_org_c             51.9659
gnome                       74.8188
elegantdice                 118.899
libb64                      120.601
manuelmartinez              120.801
arduino                     126.262
daedalusalpha               126.473
CppCodec                    151.866
wikibooks_org_cpp           343.2
adp_gmbh                    381.523
LihO                        406.693
libcurl                     3246.39
user152949                  4828.21

(René Nyffenegger 的解决方案,归功于这个问题的另一个答案,在这里列为 adp _ gmbh)。

下面是来自 Jouni Malinen 的一个,我稍微修改了一下,返回一个 std: : string:

/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/


// 2016-12-12 - Gaspard Petit : Slightly modified to return a std::string
// instead of a buffer allocated with malloc.


#include <string>


static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


/**
* base64_encode - Base64 encode
* @src: Data to be encoded
* @len: Length of the data to be encoded
* @out_len: Pointer to output length variable, or %NULL if not used
* Returns: Allocated buffer of out_len bytes of encoded data,
* or empty string on failure
*/
std::string base64_encode(const unsigned char *src, size_t len)
{
unsigned char *out, *pos;
const unsigned char *end, *in;


size_t olen;


olen = 4*((len + 2) / 3); /* 3-byte blocks to 4-byte */


if (olen < len)
return std::string(); /* integer overflow */


std::string outStr;
outStr.resize(olen);
out = (unsigned char*)&outStr[0];


end = src + len;
in = src;
pos = out;
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 (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++ = '=';
}


return outStr;
}

最快解码器: 以下是解码结果,我必须承认我有点惊讶:

polfosol                    45.2335
wikibooks_org_c             74.7347
apache                      77.1438
libb64                      100.332
gnome                       114.511
manuelmartinez              126.579
elegantdice                 138.514
daedalusalpha               151.561
jounimalinen                206.163
arduino                     335.95
wikibooks_org_cpp           350.437
CppCodec                    526.187
internetsoftwareconsortium  862.833
libcurl                     1280.27
LihO                        1852.4
adp_gmbh                    1934.43
user152949                  5332.87

Polfool 从 C + + 中的 base64 decode 代码片段中截取的片段是最快的,几乎是原来的2倍。

为了完整起见,下面是代码:

static const int B64index[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, 62, 63, 62, 62, 63, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  3,  4,  5,  6,
7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,
0,  0,  0, 63,  0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };


std::string b64decode(const void* data, const size_t len)
{
unsigned char* p = (unsigned char*)data;
int pad = len > 0 && (len % 4 || p[len - 1] == '=');
const size_t L = ((len + 3) / 4 - pad) * 4;
std::string str(L / 4 * 3 + pad, '\0');


for (size_t i = 0, j = 0; i < L; i += 4)
{
int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
str[j++] = n >> 16;
str[j++] = n >> 8 & 0xFF;
str[j++] = n & 0xFF;
}
if (pad)
{
int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12;
str[str.size() - 1] = n >> 16;


if (len > L + 2 && p[L + 2] != '=')
{
n |= B64index[p[L + 2]] << 6;
str.push_back(n >> 8 & 0xFF);
}
}
return str;
}

Ryyst (得票最多的)对代码的一个小改进是不使用动态分配的解码表,而是使用静态常量预计算表。这消除了指针的使用和表的初始化,同时也避免了在使用 base64 _ clean up ()忘记清理解码表时的内存泄漏(顺便说一下,在 base64 _ clean up ()中,在调用 free (decding _ table)之后,应该使用 decding _ table = NULL,否则在 base64 _ clean up ()之后意外调用 base64 _ decode 将会崩溃或导致未确定的行为)。另一个解决方案可以是使用 std: : only _ ptr... 但是我满意的是堆栈中只有 const char [256] ,并且避免使用所有的指针——这样代码看起来更简洁、更短。

译码表计算如下:

const char encoding_table[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/' };


unsigned char decoding_table[256];


for (int i = 0; i < 256; i++)
decoding_table[i] = '\0';


for (int i = 0; i < 64; i++)
decoding_table[(unsigned char)encoding_table[i]] = i;


for (int i = 0; i < 256; i++)
cout << "0x" << (int(decoding_table[i]) < 16 ? "0" : "") << hex << int(decoding_table[i]) << (i != 255 ? "," : "") << ((i+1) % 16 == 0 ? '\n' : '\0');


cin.ignore();

我使用的修改代码是:

        static const char encoding_table[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/' };


static const unsigned char decoding_table[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };


char* base64_encode(const unsigned char *data, size_t input_length, size_t &output_length) {


const int mod_table[] = { 0, 2, 1 };


output_length = 4 * ((input_length + 2) / 3);


char *encoded_data = (char*)malloc(output_length);


if (encoded_data == nullptr)
return nullptr;


for (int i = 0, j = 0; i < input_length;) {


uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;


uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;


encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];


}


for (int i = 0; i < mod_table[input_length % 3]; i++)
encoded_data[output_length - 1 - i] = '=';


return encoded_data;


};


unsigned char* base64_decode(const char *data, size_t input_length, size_t &output_length) {


if (input_length % 4 != 0)
return nullptr;


output_length = input_length / 4 * 3;


if (data[input_length - 1] == '=') (output_length)--;
if (data[input_length - 2] == '=') (output_length)--;


unsigned char* decoded_data = (unsigned char*)malloc(output_length);


if (decoded_data == nullptr)
return nullptr;


for (int i = 0, j = 0; i < input_length;) {


uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];


uint32_t triple = (sextet_a << 3 * 6)
+ (sextet_b << 2 * 6)
+ (sextet_c << 1 * 6)
+ (sextet_d << 0 * 6);


if (j < output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
if (j < output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
if (j < output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;


}


return decoded_data;


};

这是一个解码器,通过直接写入 putchar 函数,专门用来避免对缓冲区的需求。这是基于 wikibook 的实现 https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C

这不像上面的其他选项那样容易使用。但是,它可以在嵌入式系统中使用,在嵌入式系统中,您希望转储一个大文件,而不需要分配另一个大缓冲区来存储生成的 base64数据库字符串。(很遗憾 datauri 不允许您指定文件名)。

void datauriBase64EncodeBufferless(int (*putchar_fcptr)(int), const char* type_strptr, const void* data_buf, const size_t dataLength)
{
const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const uint8_t *data = (const uint8_t *)data_buf;
size_t x = 0;
uint32_t n = 0;
int padCount = dataLength % 3;
uint8_t n0, n1, n2, n3;


size_t outcount = 0;
size_t line = 0;


putchar_fcptr((int)'d');
putchar_fcptr((int)'a');
putchar_fcptr((int)'t');
putchar_fcptr((int)'a');
putchar_fcptr((int)':');
outcount += 5;


while (*type_strptr != '\0')
{
putchar_fcptr((int)*type_strptr);
type_strptr++;
outcount++;
}


putchar_fcptr((int)';');
putchar_fcptr((int)'b');
putchar_fcptr((int)'a');
putchar_fcptr((int)'s');
putchar_fcptr((int)'e');
putchar_fcptr((int)'6');
putchar_fcptr((int)'4');
putchar_fcptr((int)',');
outcount += 8;


/* increment over the length of the string, three characters at a time */
for (x = 0; x < dataLength; x += 3)
{
/* these three 8-bit (ASCII) characters become one 24-bit number */
n = ((uint32_t)data[x]) << 16; //parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0


if((x+1) < dataLength)
n += ((uint32_t)data[x+1]) << 8;//parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0


if((x+2) < dataLength)
n += data[x+2];


/* this 24-bit number gets separated into four 6-bit numbers */
n0 = (uint8_t)(n >> 18) & 63;
n1 = (uint8_t)(n >> 12) & 63;
n2 = (uint8_t)(n >> 6) & 63;
n3 = (uint8_t)n & 63;


/*
* if we have one byte available, then its encoding is spread
* out over two characters
*/


putchar_fcptr((int)base64chars[n0]);
putchar_fcptr((int)base64chars[n1]);
outcount += 2;


/*
* if we have only two bytes available, then their encoding is
* spread out over three chars
*/
if((x+1) < dataLength)
{
putchar_fcptr((int)base64chars[n2]);
outcount += 1;
}


/*
* if we have all three bytes available, then their encoding is spread
* out over four characters
*/
if((x+2) < dataLength)
{
putchar_fcptr((int)base64chars[n3]);
outcount += 1;
}


/* Breaking up the line so it's easier to copy and paste */
int curr_line = (outcount/80);
if( curr_line != line )
{
line = curr_line;
putchar_fcptr((int)'\r');
putchar_fcptr((int)'\n');
}
}


/*
* create and add padding that is required if we did not have a multiple of 3
* number of characters available
*/
if (padCount > 0)
{
for (; padCount < 3; padCount++)
{
putchar_fcptr((int)'=');
}
}


putchar_fcptr((int)'\r');
putchar_fcptr((int)'\n');
}

这是测试

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


int main(void)
{
char str[] = "test";
datauriBase64EncodeBufferless(putchar, "text/plain;charset=utf-8", str, strlen(str));
return 0;
}

预期产出: data:text/plain;charset=utf-8;base64,dGVzdA==

ABC0和 EVP_DecodeBlock功能让这一切变得非常简单:

#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>


char *base64(const unsigned char *input, int length) {
const int pl = 4*((length+2)/3);
char *output = calloc(pl+1, 1); //+1 for the terminating null that EVP_EncodeBlock adds on
const int ol = EVP_EncodeBlock(output, input, length);
if (ol != pl) { fprintf(stderr, "Whoops, encode predicted %d but we got %d\n", pl, ol); }
return output;
}


unsigned char *decode64(const char *input, int length) {
const int pl = 3*length/4;
unsigned char *output = calloc(pl+1, 1);
const int ol = EVP_DecodeBlock(output, input, length);
if (pl != ol) { fprintf(stderr, "Whoops, decode predicted %d but we got %d\n", pl, ol); }
return output;
}

我修复了@ryyst 的 bug,这是一个网址安全版本:

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

static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '-', '_'};
static char *decoding_table = NULL;
static int mod_table[] = {0, 2, 1};
 

void build_decoding_table() {
 

decoding_table = malloc(256);
 

for (int i = 0; i < 64; i++)
decoding_table[(unsigned char) encoding_table[i]] = i;
}
 

 

void base64_cleanup() {
free(decoding_table);
}
 

char *base64_encode(const char *data,
size_t input_length,
size_t *output_length) {
 

*output_length = 4 * ((input_length + 2) / 3);
 

char *encoded_data = malloc(*output_length);
if (encoded_data == NULL) return NULL;
 

for (int i = 0, j = 0; i < input_length;) {
 

uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
 

uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
 

encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
}
 

//int i=0;
for (int i = 0; i < mod_table[input_length % 3]; i++)
encoded_data[*output_length - 1 - i] = '=';
    

*output_length  = *output_length -2 + mod_table[input_length % 3];
encoded_data[*output_length] =0;


return encoded_data;
}
 

 

unsigned char *base64_decode(const char *data,
size_t input_length,
size_t *output_length) {
 

if (decoding_table == NULL) build_decoding_table();
 

if (input_length % 4 != 0) return NULL;
 

*output_length = input_length / 4 * 3;
if (data[input_length - 1] == '=') (*output_length)--;
if (data[input_length - 2] == '=') (*output_length)--;
 

unsigned char *decoded_data = malloc(*output_length);
if (decoded_data == NULL) return NULL;
 

for (int i = 0, j = 0; i < input_length;) {
 

uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
 

uint32_t triple = (sextet_a << 3 * 6)
+ (sextet_b << 2 * 6)
+ (sextet_c << 1 * 6)
+ (sextet_d << 0 * 6);
 

if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
}
 

return decoded_data;
}
 

int main(){
    

const char * data = "Hello World! 您好!世界!";
size_t input_size = strlen(data);
printf("Input size: %ld \n",input_size);
char * encoded_data = base64_encode(data, input_size, &input_size);
printf("After size: %ld \n",input_size);
printf("Encoded Data is: %s \n",encoded_data);
    

size_t decode_size = strlen(encoded_data);
printf("Output size: %ld \n",decode_size);
unsigned char * decoded_data = base64_decode(encoded_data, decode_size, &decode_size);
printf("After size: %ld \n",decode_size);
printf("Decoded Data is: %s \n",decoded_data);
return 0;
}

如果你想找到一个可行的 C 解决方案, 我相信你需要 这个
Https://github.com/littlstar/b64.c

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


int
main (void) {
unsigned char *str = "brian the monkey and bradley the kinkajou are friends";
char *enc = b64_encode(str, strlen(str));


printf("%s\n", enc); // YnJpYW4gdGhlIG1vbmtleSBhbmQgYnJhZGxleSB0aGUga2lua2Fqb3UgYXJlIGZyaWVuZHM=


char *dec = b64_decode(enc, strlen(enc));


printf("%s\n", dec); // brian the monkey and bradley the kinkajou are friends
free(enc);
free(dec);
return 0;
}

基于 GaspardP 的回答,下面是 Jouni Malinen 的 C 语言编码器的简化版本,我为我所参与的项目制作了这个版本:

/* Character list for url-safe base64 encoding */
//char cl[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";


/* Character list for url-unsafe base64 encoding */
char cl[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


/**
* @brief Encodes s_in into base64 and writes it to s_out.
*    @param s_in  Pointer to input buffer.
*    @param s_out Pointer to output buffer.
* @return Pointer to end of output buffer
* @usage b64e("ABC",buf);
*/
char *b64e(char *s_in, char *s_out){
int i=0;
if (s_in[i]==0) return s_out;
if (s_in[i+1]==0 || s_in[i+2]==0) {
*s_out++= b64_cl[ s_in[i] >> 2 ];
if (s_in[i+1]==0) {
*s_out++ = b64_cl[ ( ( s_in[i]   & 0b000011 ) << 4 ) ];
} else
if (s_in[i+2]==0) {
*s_out++ = b64_cl[ ( ( s_in[i]   & 0b000011 ) << 4 ) + ( ( s_in[i+1] >> 4 ) & 0b001111 ) ];
*s_out++ = b64_cl[ ( ( s_in[i+1] & 0b001111 ) << 2 ) ];
}
return s_out;
}
*s_out++ = b64_cl[    s_in[i] >> 2 ];
*s_out++ = b64_cl[ ( (s_in[i]   & 0b000011 ) << 4 ) + ( (s_in[i+1] >> 4) & 0b001111 ) ];
*s_out++ = b64_cl[ ( (s_in[i+1] & 0b001111 ) << 2 ) + ( (s_in[i+2] >> 6) & 0b000011 ) ];
*s_out++ = b64_cl[ (  s_in[i+2] & 0b111111 ) ];
return b64e( s_in+3, s_out );
}