简体   繁体   English

如何仅使用 boost 将字符串编码为 base64?

[英]How do I encode a string to base64 using only boost?

I'm trying to quickly encode a simple ASCII string to base64 (Basic HTTP Authentication using boost::asio) and not paste in any new code code or use any libraries beyond boost.我正在尝试将一个简单的 ASCII 字符串快速编码为 base64(使用 boost::asio 进行基本 HTTP 身份验证),并且不粘贴任何新代码代码或使用 boost 之外的任何库。

Simple signature would look like: string Base64Encode(const string& text);简单的签名看起来像: string Base64Encode(const string& text);

Again I realize the algorithm is easy and there are many libraries/examples doing this but I'm looking for a clean boost example.我再次意识到该算法很简单,并且有很多库/示例这样做,但我正在寻找一个干净的提升示例。 I found boost serialization but no clear examples there or from Google.我发现了 boost 序列化,但那里或谷歌没有明确的例子。 http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/dataflow.html http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/dataflow.html

Is this possible without adding the actual base64 algorithm explicitly to my code?如果不将实际的 base64 算法明确添加到我的代码中,这是否可行?

Here is my solution.这是我的解决方案。 It uses the same basic technique as the other solutions on this page, but solves the problem of the padding in what I feel is a more elegant way.它使用与此页面上的其他解决方案相同的基本技术,但以我认为更优雅的方式解决了填充问题。 This solution also makes use of C++11.该解决方案还利用了 C++11。

I think that most of the code is self explanatory.我认为大部分代码都是不言自明的。 The bit of math in the encode function calculates the number of '=' characters we need to add.编码 function 中的数学位计算我们需要添加的“=”字符的数量。 The modulo 3 of val.size() the remainder, but what we really want is the difference between val.size() and the next number divisible by three. val.size() 的模 3 是余数,但我们真正想要的是 val.size() 和下一个可被三整除的数字之间的差异。 Since we have the remainder we can just subtract the remainder from 3, but that leaves 3 in the case that we want 0, so we have to modulo 3 one more time.由于我们有余数,我们可以从 3 中减去余数,但是在我们想要 0 的情况下剩下 3,所以我们必须再对 3 取模一次。

#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/algorithm/string.hpp>

std::string decode64(const std::string &val) {
    using namespace boost::archive::iterators;
    using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
    return boost::algorithm::trim_right_copy_if(std::string(It(std::begin(val)), It(std::end(val))), [](char c) {
        return c == '\0';
    });
}

std::string encode64(const std::string &val) {
    using namespace boost::archive::iterators;
    using It = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
    auto tmp = std::string(It(std::begin(val)), It(std::end(val)));
    return tmp.append((3 - val.size() % 3) % 3, '=');
}

I improved the example in the link you provided a little:我改进了您提供的链接中的示例:

#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>
#include <sstream>
#include <string>
#include <iostream>


int main()
{
    using namespace boost::archive::iterators;

    std::string test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce ornare ullamcorper ipsum ac gravida.";

    std::stringstream os;
    typedef 
        insert_linebreaks<         // insert line breaks every 72 characters
            base64_from_binary<    // convert binary values to base64 characters
                transform_width<   // retrieve 6 bit integers from a sequence of 8 bit bytes
                    const char *,
                    6,
                    8
                >
            > 
            ,72
        > 
        base64_text; // compose all the above operations in to a new iterator

    std::copy(
        base64_text(test.c_str()),
        base64_text(test.c_str() + test.size()),
        ostream_iterator<char>(os)
    );

    std::cout << os.str();
}

This prints the string encoded base64 nicely formated with a line break every 72 characters onto the console, ready to be put into an email.这会将编码为 base64 的字符串打印到控制台,每 72 个字符有一个换行符,格式很好,可以放入 email。 If you don't like the linebreaks, just stay with this:如果您不喜欢换行符,请继续使用:

    typedef 
        base64_from_binary<
           transform_width<
                const char *,
                6,
                8
            >
        > 
        base64_text;

You could use beast's implementation.你可以使用野兽的实现。

For boost version 1.71, the functions are:对于 boost 1.71 版本,功能是:

boost::beast::detail::base64::encode()
boost::beast::detail::base64::encoded_size()
boost::beast::detail::base64::decode()
boost::beast::detail::base64::decoded_size()

From #include < boost/beast/core/detail/base64.hpp >来自#include < boost/beast/core/detail/base64.hpp >

For older versions back to beast's inclusion in 1.66, the functions are:对于回到 1.66 中包含野兽的旧版本,功能是:

boost::beast::detail::base64_encode()
boost::beast::detail::base64_decode()

From #include < boost/beast/core/detail/base64.hpp >来自#include < boost/beast/core/detail/base64.hpp >

Another solution using boost base64 encode decode:使用 boost base64 编码解码的另一种解决方案:

const std::string base64_padding[] = {"", "==","="};
std::string base64_encode(const std::string& s) {
  namespace bai = boost::archive::iterators;

  std::stringstream os;

  // convert binary values to base64 characters
  typedef bai::base64_from_binary
  // retrieve 6 bit integers from a sequence of 8 bit bytes
  <bai::transform_width<const char *, 6, 8> > base64_enc; // compose all the above operations in to a new iterator

  std::copy(base64_enc(s.c_str()), base64_enc(s.c_str() + s.size()),
            std::ostream_iterator<char>(os));

  os << base64_padding[s.size() % 3];
  return os.str();
}

std::string base64_decode(const std::string& s) {
  namespace bai = boost::archive::iterators;

  std::stringstream os;

  typedef bai::transform_width<bai::binary_from_base64<const char *>, 8, 6> base64_dec;

  unsigned int size = s.size();

  // Remove the padding characters, cf. https://svn.boost.org/trac/boost/ticket/5629
  if (size && s[size - 1] == '=') {
    --size;
    if (size && s[size - 1] == '=') --size;
  }
  if (size == 0) return std::string();

  std::copy(base64_dec(s.data()), base64_dec(s.data() + size),
            std::ostream_iterator<char>(os));

  return os.str();
}

And here are the test cases:以下是测试用例:

    std::string t_e[TESTSET_SIZE] = {
        ""
      , "M"
      , "Ma"
      , "Man"
      , "pleasure."
      , "leasure."
      , "easure."
      , "asure."
      , "sure."
};
std::string t_d[TESTSET_SIZE] = {
        ""
      , "TQ=="
      , "TWE="
      , "TWFu"
      , "cGxlYXN1cmUu"
      , "bGVhc3VyZS4="
      , "ZWFzdXJlLg=="
      , "YXN1cmUu"
      , "c3VyZS4="
};

Hope this helps希望这可以帮助

For anyone coming here from Google, here's my base64 encode/decode functions based off boost.对于从 Google 来到这里的任何人,这是我的 base64 基于 boost 的编码/解码功能。 It handles padding correctly as per DanDan's comment above.它按照上面 DanDan 的评论正确处理填充。 The decode functions stops when it encounters an illegal character, and returns a pointer to that character, which is great if you're parsing base64 in json or xml.解码函数在遇到非法字符时停止,并返回一个指向该字符的指针,如果您正在解析 json 或 Z0F635D0E0F3874FFF8B581C132E6C7A7 中的 base64,那就太好了。

///
/// Convert up to len bytes of binary data in src to base64 and store it in dest
///
/// \param dest Destination buffer to hold the base64 data.
/// \param src Source binary data.
/// \param len The number of bytes of src to convert.
///
/// \return The number of characters written to dest.
/// \remarks Does not store a terminating null in dest.
///
uint base64_encode(char* dest, const char* src, uint len)
{
    char tail[3] = {0,0,0};
    typedef base64_from_binary<transform_width<const char *, 6, 8> > base64_enc;

    uint one_third_len = len/3;
    uint len_rounded_down = one_third_len*3;
    uint j = len_rounded_down + one_third_len;

    std::copy(base64_enc(src), base64_enc(src + len_rounded_down), dest);

    if (len_rounded_down != len)
    {
        uint i=0;
        for(; i < len - len_rounded_down; ++i)
        {
            tail[i] = src[len_rounded_down+i];
        }

        std::copy(base64_enc(tail), base64_enc(tail + 3), dest + j);

        for(i=len + one_third_len + 1; i < j+4; ++i)
        {
            dest[i] = '=';
        }

        return i;
    }

    return j;
}

///
/// Convert null-terminated string src from base64 to binary and store it in dest.
///
/// \param dest Destination buffer
/// \param src Source base64 string
/// \param len Pointer to unsigned int representing size of dest buffer. After function returns this is set to the number of character written to dest.
///
/// \return Pointer to first character in source that could not be converted (the terminating null on success)
///
const char* base64_decode(char* dest, const char* src, uint* len)
{
    uint output_len = *len;

    typedef transform_width<binary_from_base64<const char*>, 8, 6> base64_dec;

    uint i=0;
    try
    {
        base64_dec src_it(src);
        for(; i < output_len; ++i)
        {
            *dest++ = *src_it;
            ++src_it;
        }
    }
    catch(dataflow_exception&)
    {
    }

    *len = i;
    return src + (i+2)/3*4; // bytes in = bytes out / 3 rounded up * 4
}

While the encoding works, the decoder is certainly broken.虽然编码有效,但解码器肯定坏了。 Also there is a bug opened: https://svn.boost.org/trac/boost/ticket/5629 .还打开了一个错误: https://svn.boost.org/trac/boost/ticket/5629 I have not found a fix for that.我还没有找到解决办法。

Base64 encode text and data Base64 编码文本和数据

const std::string base64_padding[] = {"", "==","="};

std::string base64EncodeText(std::string text) {
    using namespace boost::archive::iterators;
    typedef std::string::const_iterator iterator_type;
    typedef base64_from_binary<transform_width<iterator_type, 6, 8> > base64_enc;
    std::stringstream ss;
    std::copy(base64_enc(text.begin()), base64_enc(text.end()), ostream_iterator<char>(ss));
    ss << base64_padding[text.size() % 3];
    return ss.str();
}

std::string base64EncodeData(std::vector<uint8_t> data) {
    using namespace boost::archive::iterators;
    typedef std::vector<uint8_t>::const_iterator iterator_type;
    typedef base64_from_binary<transform_width<iterator_type, 6, 8> > base64_enc;
    std::stringstream ss;
    std::copy(base64_enc(data.begin()), base64_enc(data.end()), ostream_iterator<char>(ss));
    ss << base64_padding[data.size() % 3];
    return ss.str();
}

This is another answer:这是另一个答案:

#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>

std::string ToBase64(const std::vector<unsigned char>& binary)
{
    using namespace boost::archive::iterators;
    using It = base64_from_binary<transform_width<std::vector<unsigned char>::const_iterator, 6, 8>>;
    auto base64 = std::string(It(binary.begin()), It(binary.end()));
    // Add padding.
    return base64.append((3 - binary.size() % 3) % 3, '=');
}

std::vector<unsigned char> FromBase64(const std::string& base64)
{
    using namespace boost::archive::iterators;
    using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
    auto binary = std::vector<unsigned char>(It(base64.begin()), It(base64.end()));
    // Remove padding.
    auto length = base64.size();
    if(binary.size() > 2 && base64[length - 1] == '=' && base64[length - 2] == '=')
    {
        binary.erase(binary.end() - 2, binary.end());
    }
    else if(binary.size() > 1 && base64[length - 1] == '=')
    {
        binary.erase(binary.end() - 1, binary.end());
    }
    return binary;
}

I modified the Answer 8 because it's not functional on my platform.我修改了答案 8,因为它在我的平台上不起作用。

const std::string base64_padding[] = {"", "==","="};
std::string *m_ArchiveData;

/// \brief  To Base64 string
bool Base64Encode(string* output) 
{  
    try
    {
        UInt32 iPadding_Mask = 0;
        typedef boost::archive::iterators::base64_from_binary
            <boost::archive::iterators::transform_width<const char *, 6, 8> > Base64EncodeIterator;  
        UInt32 len = m_ArchiveData->size();
        std::stringstream os;

        std::copy(Base64EncodeIterator(m_ArchiveData->c_str()), 
            Base64EncodeIterator(m_ArchiveData->c_str()+len), 
            std::ostream_iterator<char>(os));

        iPadding_Mask = m_ArchiveData->size() % 3;
        os << base64_padding[iPadding_Pask];

        *output = os.str();
        return output->empty() == false;  
    }
    catch (...)
    {
        PLOG_ERROR_DEV("unknown error happens");
        return false;
    }
}  

/// \brief  From Base64 string
bool mcsf_data_header_byte_stream_archive::Base64Decode(const std::string *input) 
{  
    try
    {
        std::stringstream os;
        bool bPaded = false;
        typedef boost::archive::iterators::transform_width<boost::archive::iterators::
            binary_from_base64<const char *>, 8, 6> Base64DecodeIterator;  

        UInt32 iLength = input->length();
        // Remove the padding characters, cf. https://svn.boost.org/trac/boost/ticket/5629
        if (iLength && (*input)[iLength-1] == '=') {
            bPaded = true;
            --iLength;
            if (iLength && (*input)[iLength - 1] == '=') 
            {
                --iLength;
            }
        }
        if (iLength == 0)
        {
            return false;
        }

        if(bPaded)
        {
            iLength --;
        }

        copy(Base64DecodeIterator(input->c_str()) ,
            Base64DecodeIterator(input->c_str()+iLength), 
            ostream_iterator<char>(os)); 

        *m_ArchiveData = os.str();
        return m_ArchiveData->empty() == false;
    }
    catch (...)
    {
        PLOG_ERROR_DEV("unknown error happens");
        return false;
    }
}  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM