C++中的Base64解码片段

97

在C++中有免费的Base64解码代码片段可用吗?


另一个线程展示了如何使用boost进行base64编码/解码:https://dev59.com/dGkv5IYBdhLWcg3wiRao - Oded Itzhaky
1
使用Boost C++库进行Base64解码:http://stackoverflow.com/questions/34680998/attempt-to-decode-a-value-not-in-base64-char-set - ap6491
2
在Windows上,您可以使用atlenc.h或wincrypt.h来完成此操作。 - Hemaolle
13个回答

1

我首先制作了自己的版本,然后发现了这个主题。

为什么我的版本看起来比这里呈现的其他版本简单?我做错了什么吗?我没有测试它的速度。

inline char const* b64units = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

inline char* b64encode(void const* a, int64_t b) {
    ASSERT(a != nullptr);
    if (b > 0) {
        uint8_t const* aa = static_cast<uint8_t const*>(a);
        uint8_t v = 0;
        int64_t bp = 0;
        int64_t sb = 0;
        int8_t off = 0;
        int64_t nt = ((b + 2) / 3) * 4;
        int64_t nd = (b * 8) / 6;
        int64_t tl = ((b * 8) % 6) ? 1 : 0;
        int64_t nf = nt - nd - tl;
        int64_t ri = 0;
        char* r = new char[nt + 1]();
        for (int64_t i = 0; i < nd; i++) {
            v = (aa[sb] << off) | (aa[sb + 1] >> (8 - off));
            v >>= 2;
            r[ri] = b64units[v];
            ri += 1;
            bp += 6;
            sb = (bp / 8);
            off = (bp % 8);
        }
        if (tl > 0) {
            v = (aa[sb] << off);
            v >>= 2;
            r[ri] = b64units[v];
            ri += 1;
        }
        for (int64_t i = 0; i < nf; i++) {
            r[ri] = '=';
            ri += 1;
        }
        return r;
    } else return nullptr;
}

P.S.:我的方法效果很好。我用Node.js进行了测试:

let data = 'stackabuse.com';
let buff = new Buffer(data);
let base64data = buff.toString('base64');

0

这是我写的一个程序,它使用联合和位域以实现最大效率和可读性。

const char PADDING_CHAR = '=';
const char* ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const uint8_t DECODED_ALPHBET[128]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,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,0,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,0,0,0,0,0};

/**
 * Given a string, this function will encode it in 64b (with padding)
 */
std::string encodeBase64(const std::string& binaryText)
{
    std::string encoded((binaryText.size()/3 + (binaryText.size()%3 > 0)) << 2, PADDING_CHAR);

    const char* bytes = binaryText.data();
    union
    {
        uint32_t temp = 0;
        struct
        {
            uint32_t first : 6, second : 6, third : 6, fourth : 6;
        } tempBytes;
    };
    std::string::iterator currEncoding = encoded.begin();

    for(uint32_t i = 0, lim = binaryText.size() / 3; i < lim; ++i, bytes+=3)
    {
        temp = bytes[0] << 16 | bytes[1] << 8 | bytes[2];
        (*currEncoding++) = ALPHABET[tempBytes.fourth];
        (*currEncoding++) = ALPHABET[tempBytes.third];
        (*currEncoding++) = ALPHABET[tempBytes.second];
        (*currEncoding++) = ALPHABET[tempBytes.first];
    }

    switch(binaryText.size() % 3)
    {
    case 1:
        temp = bytes[0] << 16;
        (*currEncoding++) = ALPHABET[tempBytes.fourth];
        (*currEncoding++) = ALPHABET[tempBytes.third];
        break;
    case 2:
        temp = bytes[0] << 16 | bytes[1] << 8;
        (*currEncoding++) = ALPHABET[tempBytes.fourth];
        (*currEncoding++) = ALPHABET[tempBytes.third];
        (*currEncoding++) = ALPHABET[tempBytes.second];
        break;
    }

    return encoded;
}

/**
 * Given a 64b padding-encoded string, this function will decode it.
 */
std::string decodeBase64(const std::string& base64Text)
{
    if( base64Text.empty() )
        return "";

    assert((base64Text.size()&3) == 0 && "The base64 text to be decoded must have a length devisible by 4!");

    uint32_t numPadding =  (*std::prev(base64Text.end(),1) == PADDING_CHAR) + (*std::prev(base64Text.end(),2) == PADDING_CHAR);

    std::string decoded((base64Text.size()*3>>2) - numPadding, '.');

    union
    {
        uint32_t temp;
        char tempBytes[4];
    };
    const uint8_t* bytes = reinterpret_cast<const uint8_t*>(base64Text.data());

    std::string::iterator currDecoding = decoded.begin();

    for(uint32_t i = 0, lim = (base64Text.size() >> 2) - (numPadding!=0); i < lim; ++i, bytes+=4)
    {
        temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12 | DECODED_ALPHBET[bytes[2]] << 6 | DECODED_ALPHBET[bytes[3]];
        (*currDecoding++) = tempBytes[2];
        (*currDecoding++) = tempBytes[1];
        (*currDecoding++) = tempBytes[0];
    }

    switch (numPadding)
    {
    case 2:
        temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12;
        (*currDecoding++) = tempBytes[2];
        break;
    
    case 1:
        temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12 | DECODED_ALPHBET[bytes[2]] << 6;
        (*currDecoding++) = tempBytes[2];
        (*currDecoding++) = tempBytes[1];
        break;
    }

    return decoded;
}

0

我喜欢GitHub上的这个解决方案

它是一个单一的hpp文件,与被接受的答案不同,它使用vector<byte>类型来处理原始数据。

#pragma once

#include <string>
#include <vector>
#include <stdexcept>
#include <cstdint>

namespace base64
{
    inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    inline static const char kPadCharacter = '=';

    using byte = std::uint8_t;

    inline std::string encode(const std::vector<byte>& input)
    {
        std::string encoded;
        encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);

        std::uint32_t temp{};
        auto it = input.begin();

        for(std::size_t i = 0; i < input.size() / 3; ++i)
        {
            temp  = (*it++) << 16;
            temp += (*it++) << 8;
            temp += (*it++);
            encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
            encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
            encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
            encoded.append(1, kEncodeLookup[(temp & 0x0000003F)      ]);
        }

        switch(input.size() % 3)
        {
        case 1:
            temp = (*it++) << 16;
            encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
            encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
            encoded.append(2, kPadCharacter);
            break;
        case 2:
            temp  = (*it++) << 16;
            temp += (*it++) << 8;
            encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
            encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
            encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
            encoded.append(1, kPadCharacter);
            break;
        }

        return encoded;
    }

    std::vector<byte> decode(const std::string& input)
    {
        if(input.length() % 4)
            throw std::runtime_error("Invalid base64 length!");

        std::size_t padding{};

        if(input.length())
        {
            if(input[input.length() - 1] == kPadCharacter) padding++;
            if(input[input.length() - 2] == kPadCharacter) padding++;
        }

        std::vector<byte> decoded;
        decoded.reserve(((input.length() / 4) * 3) - padding);

        std::uint32_t temp{};
        auto it = input.begin();

        while(it < input.end())
        {
            for(std::size_t i = 0; i < 4; ++i)
            {
                temp <<= 6;
                if     (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41;
                else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47;
                else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04;
                else if(*it == 0x2B)                temp |= 0x3E;
                else if(*it == 0x2F)                temp |= 0x3F;
                else if(*it == kPadCharacter)
                {
                    switch(input.end() - it)
                    {
                    case 1:
                        decoded.push_back((temp >> 16) & 0x000000FF);
                        decoded.push_back((temp >> 8 ) & 0x000000FF);
                        return decoded;
                    case 2:
                        decoded.push_back((temp >> 10) & 0x000000FF);
                        return decoded;
                    default:
                        throw std::runtime_error("Invalid padding in base64!");
                    }
                }
                else throw std::runtime_error("Invalid character in base64!");

                ++it;
            }

            decoded.push_back((temp >> 16) & 0x000000FF);
            decoded.push_back((temp >> 8 ) & 0x000000FF);
            decoded.push_back((temp      ) & 0x000000FF);
        }

        return decoded;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接