c++ - ¿Cómo codifico una cadena para base64 usando solo boost?
string (8)
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 utilizar bibliotecas más allá de boost.
La firma simple se vería así: string Base64Encode (const string & text);
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
¿Es esto posible sin agregar el algoritmo base64 explícitamente a mi código?
Ahora que el impulso ha llegado a 1.66 y se incluye bestia, puede usar la implementación de bestia.
Las funciones principales que querrás son: boost::beast::detail::base64_encode() boost::beast::detail::base64_decode()
Desde #include < boost/beast/core/detail/base64.hpp >
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 parte 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, ''='');
}
Base64 codifica 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();
}
Mejoré el ejemplo en el enlace que proporcionó 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();
}
Esto imprime la base codificada de la base64 muy formateada con un salto de línea cada 72 caracteres en la consola, lista para ser colocada en un correo electrónico. Si no te gustan los saltos de línea, simplemente quédate con esto:
typedef
base64_from_binary<
transform_width<
const char *,
6,
8
>
>
base64_text;
Mientras la codificación funciona, el decodificador está roto. También hay un error abierto: https://svn.boost.org/trac/boost/ticket/5629 . No he encontrado una solución para eso.
Modifiqué la Respuesta 8 porque no funciona 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;
}
}
Otra solución que usa boost base64 codifica 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
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
}