2011-08-13 117 views
38

我想快速編碼一個簡單的ASCII字符串base64(使用boost :: asio的基本HTTP身份驗證),而不是粘貼在任何新的代碼代碼或使用任何超出推廣的庫。如何使用boost來將字符串編碼爲base64?

簡單的簽名看起來像: string Base64Encode(const string & text);

我再次意識到算法很簡單,並且有很多庫/示例正在做這個,但我正在尋找一個乾淨的示例。我發現序列化提升,但沒有明確的例子,或從谷歌。 http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/dataflow.html

這可能沒有明確添加實際的base64算法到我的代碼?

+2

請有類似的問題,這將字符串轉換以及從升壓使用的base64看看我的示例程序並說明正確的填充(與接受的答案相反):http://stackoverflow.com/a/10973348/1132850 – PiQuer

+0

晚會到了!但是這個[Boost Beast文件](https://github.com/boostorg/beast/blob/6f08814a0c291eb9f03aaa1daefffdc45c1b9087/include/boost/beast/core/detail/base64.hpp)正是我所需要的。 –

回答

38

我改進了鏈接,您提供了一個小例子:

#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> 


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(); 
} 

這將打印很好地格式化用一條線串base64編碼每72個字符突破到控制檯,隨時可以投入到電子郵件中。如果你不喜歡線條,那就留在這裏:

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

這太棒了!它應該分成76個字符。 – DanDan

+2

如果輸入數據緩衝區不是3的倍數,則還需要填充0(變換前)和'='(變換後)。 – DanDan

+4

如何使用上述解決方案進行填充? – Tobi

6

對於任何來自谷歌的人來說,這裏是我的base64編碼/解碼功能,基於關閉提升。它按照上面的DanDan評論正確地處理填充。解碼函數在遇到非法字符時會停止,並返回一個指向該字符的指針,如果您正在使用json或xml解析base64,這非常棒。

使用升壓的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 
} 
13

另一種解決方案:

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(); 
} 

這裏是測試情況:

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=" 
}; 

希望這有助於

+0

使用上面的base64_decode函數,我得到了以下錯誤:「拋出boost :: archive的實例後調用terminate: :iterators :: dataflow_exception 什麼():嘗試解碼不在base64字符集中的值「。但是,我能夠解決它。看到我的問題線程:[嘗試解碼值不在base64字符集](http://stackoverflow.com/questions/34680998/attempt-to-decode-a-value-not -in-base64-char-set/34733928#34733928) – ap6491

0

我修改應答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; 
    } 
} 
+3

你可能會鏈接到你認爲的答案8?答案號碼根據新答案定期更改並投票 –

+1

,並解釋什麼是「不實用」的,以及「我的平臺」是什麼。 –

30

這是我的解決方案。它使用與本頁面上其他解決方案相同的基本技術,但是解決了我認爲是更優雅方式的填充問題。該解決方案還使用C++ 11。

我認爲大部分代碼是自我解釋的。編碼函數中的數學位計算我們需要添加的'='字符的數量。 val.size()的模3是餘數,但我們真正想要的是val.size()和下一個可以被三整除的數之間的差異。既然我們有餘數,我們可以從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, '='); 
} 
+3

我喜歡在這裏使用'using'! –

+1

如果'使用It'導致編譯器錯誤,您可以將其更改爲: 'typedef transform_width ,8,6> It;' –

+5

警告:如果您的val字符串包含多個'\ 0'(通常情況下,我知道),那麼這段代碼可能會在最後刪除太多的字符。 – zpon

0

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(); 
} 
2

現在升壓已經達到1.66和野獸在內,你可以用野獸的實現。

你要的主要功能是: boost::beast::detail::base64_encode() boost::beast::detail::base64_decode()

從的#include < boost/beast/core/detail/base64.hpp>

+0

「詳細」名稱空間中的聲明被認爲是私有的,不應該依賴! –

相關問題