2011-08-13 14 views
38

Estoy tratando de codificar rápidamente una cadena ASCII simple a base64 (Autenticación HTTP básica utilizando boost :: asio) y no pegar en ningún código de código nuevo ni usar bibliotecas más allá de boost.¿Cómo codifico una cadena para base64 usando solo boost?

La firma simple se vería así: cadena Base64Encode (const cadena & texto);

Nuevamente me doy cuenta de que el algoritmo es fácil y hay muchas bibliotecas/ejemplos haciendo esto, pero estoy buscando un ejemplo de impulso limpio. Encontré la serialización de impulso pero no ejemplos claros allí o de Google. http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/dataflow.html

¿Esto es posible sin agregar el algoritmo base64 explícitamente a mi código?

+2

favor, eche un vistazo a mi programa de ejemplo en una pregunta similar, que convierte las cadenas ay de base 64 utilizando impulso y cuentas para el relleno correcto (en contraste con la respuesta aceptada): http://stackoverflow.com/a/10973348/1132850 – PiQuer

+0

¡Tarde a la fiesta! Pero este [archivo Boost Beast] (https://github.com/boostorg/beast/blob/6f08814a0c291eb9f03aaa1daefffdc45c1b9087/include/boost/beast/core/detail/base64.hpp) tenía exactamente lo que necesitaba. –

Respuesta

38

que mejoraron el ejemplo en el enlace que ya ha proporcionado un poco:

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

Imprime la base64 cadena muy bien formateado con un salto de línea cada 72 caracteres en la consola, listo para ser puesto en un correo electrónico. Si no le gustan los saltos de línea, simplemente quédese con esto:

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

¡Esto es genial! Sin embargo, debería dividirse en 76 caracteres. – DanDan

+2

También necesita rellenar con 0 (antes de la transformación) y '=' (después de la transformación) si el búfer de datos de entrada no es un múltiplo de 3. – DanDan

+4

¿cómo se rellenaría con la solución anterior? – Tobi

6

Para cualquiera que venga aquí desde Google, aquí están mis funciones de codificación/decodificación base64 basadas en boost. Maneja el relleno correctamente según el comentario anterior de DanDan. Las funciones de decodificación se detienen cuando encuentra un carácter ilegal y devuelve un puntero a ese carácter, lo cual es excelente si está analizando base64 en json o xml.

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

Otra solución utilizando impulso base64 codificación decodificación:

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

Y aquí están los casos de prueba:

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

espero que esto ayude

+0

Utilizando la función base64_decode anterior, recibí el siguiente error: "finalizar llamado después de lanzar una instancia de boost :: archive: : iterators :: dataflow_exception what(): intenta decodificar un valor que no está en base64 char set ". Sin embargo, pude resolverlo. Ver el hilo de mi pregunta: [intento de decodificar-un-valor-no-en-base64-char-set] (http://stackoverflow.com/questions/34680998/attempt-to-decode-a-value-not -in-base64-char-set/34733928 # 34733928) – ap6491

0

he modificado la respuesta 8 porque es no funcional en mi plataforma.

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

¿Podría vincularse con lo que considera respuesta 8? Los números de respuesta cambian regularmente según las nuevas respuestas y los votos –

+1

y explican qué es "no funcional" al respecto, y cuál es "mi plataforma". –

30

Aquí está mi solución. Utiliza la misma técnica básica que las otras soluciones en esta página, pero resuelve el problema del relleno en lo que creo es una forma más elegante. Esta solución también hace uso de C++ 11.

Creo que la mayoría del código se explica por sí mismo. El bit de matemática en la función de codificación calcula el número de '=' caracteres que necesitamos agregar. El módulo 3 de val.size() el resto, pero lo que realmente queremos es la diferencia entre val.size() y el siguiente número divisible entre tres. Como tenemos el resto, podemos restar el resto de 3, pero eso deja 3 en el caso de que queramos 0, por lo que tenemos que modificar el módulo 3 una vez más.

#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

¡Me gusta el uso de 'using' aquí! –

+1

Si 'using It' provoca un error en el compilador, puede cambiarlo a: ' typedef transform_width , 8, 6> It; ' –

+5

Advertencia: si su serie val contiene varios '\ 0 'al final (normalmente no es el caso, lo sé), entonces este código puede eliminar demasiados caracteres al final. – zpon

0

Base64 codificar texto y datos

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

Ahora que ha llegado a 1,66 impulso y la bestia se incluye, se puede utilizar la aplicación de bestia.

Las principales funciones que querrá son: boost::beast::detail::base64_encode() boost::beast::detail::base64_decode()

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

+0

Las declaraciones en espacios de nombres "detallados" se consideran privadas, ¡y no se debe confiar en ellas! –

Cuestiones relacionadas