base64-Dekodierungs-Snippet in c ++

Antworten:

91

Google ist dein Freund

Hier ist die Implementierung von dieser Seite:

/* 
   base64.cpp and base64.h

   Copyright (C) 2004-2008 René Nyffenegger

   This source code is provided 'as-is', without any express or implied
   warranty. In no event will the author be held liable for any damages
   arising from the use of this software.

   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:

   1. The origin of this source code must not be misrepresented; you must not
      claim that you wrote the original source code. If you use this source code
      in a product, an acknowledgment in the product documentation would be
      appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original source code.

   3. This notice may not be removed or altered from any source distribution.

   René Nyffenegger [email protected]

*/

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";


static inline bool is_base64(unsigned char c) {
  return (isalnum(c) || (c == '+') || (c == '/'));
}

std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
  std::string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--) {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';

  }

  return ret;

}
std::string base64_decode(std::string const& encoded_string) {
  int in_len = encoded_string.size();
  int i = 0;
  int j = 0;
  int in_ = 0;
  unsigned char char_array_4[4], char_array_3[3];
  std::string ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
        ret += char_array_3[i];
      i = 0;
    }
  }

  if (i) {
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
  }

  return ret;
}
John Millikin
quelle
18
Für die Neugierigen war dies der allererste Hit für "base64 decode c ++"
John Millikin
1
Ja, irgendwie habe ich das verpasst. Ich habe versucht, Google-Code-Suche anstelle von alten Google. Vielen Dank.
Joeld
12
und jetzt hat diese Frage 2500 Aufrufe, also bin ich wohl nicht der einzige. :)
Joeld
2
Wir sollten all diese unnötigen Verkettungen von Zeichenfolgen vermeiden. Da wir wissen in_len, dass wir die Länge von kennen ret, geben Sie bei der Initialisierung eine feste Länge an.
Ray
3
Google ist nicht immer dein Freund. Diese Implementierung ist fast die schlechteste, die Sie auswählen könnten. Siehe dies: stackoverflow.com/questions/342409/…
DaedalusAlpha
107

Hier ist meine Modifikation der Implementierung , die ursprünglich von René Nyffenegger geschrieben wurde . Und warum habe ich es geändert? Nun, weil es mir nicht angemessen erschien, mit im std::stringObjekt gespeicherten Binärdaten zu arbeiten ;)

base64.h :

#ifndef _BASE64_H_
#define _BASE64_H_

#include <vector>
#include <string>
typedef unsigned char BYTE;

std::string base64_encode(BYTE const* buf, unsigned int bufLen);
std::vector<BYTE> base64_decode(std::string const&);

#endif

base64.cpp :

#include "base64.h"
#include <iostream>

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";


static inline bool is_base64(BYTE c) {
  return (isalnum(c) || (c == '+') || (c == '/'));
}

std::string base64_encode(BYTE const* buf, unsigned int bufLen) {
  std::string ret;
  int i = 0;
  int j = 0;
  BYTE char_array_3[3];
  BYTE char_array_4[4];

  while (bufLen--) {
    char_array_3[i++] = *(buf++);
    if (i == 3) {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';
  }

  return ret;
}

std::vector<BYTE> base64_decode(std::string const& encoded_string) {
  int in_len = encoded_string.size();
  int i = 0;
  int j = 0;
  int in_ = 0;
  BYTE char_array_4[4], char_array_3[3];
  std::vector<BYTE> ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
          ret.push_back(char_array_3[i]);
      i = 0;
    }
  }

  if (i) {
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
  }

  return ret;
}

Hier ist die Verwendung:

std::vector<BYTE> myData;
...
std::string encodedData = base64_encode(&myData[0], myData.size());
std::vector<BYTE> decodedData = base64_decode(encodedData);

Ich hoffe, dass jemand diese Antwort hilfreich findet ^^

LihO
quelle
1
Vielen Dank. Irgendwelche Einschränkungen bei der Verwendung Ihres Codes? (Mein persönlicher Fall wird ein akademischer CFD-Code sein).
Azrael3000
1
Ich habe versucht, eine JPG-Datei mit Ihrer Methode zu dekodieren, indem ich den gesamten Vektor in eine CFile mit ihrer Größe geschrieben habe, aber es war keine große Überraschung, dass der Bildheader beschädigt war. Die Größe ist jedoch gleich. Irgendwelche besseren Ideen, wie man Bilddateien wiederherstellt?
Masche
3
@masche: Hier geht es um das Codieren und Decodieren von Daten jeglicher Art auf Byte-Ebene. Bild -> Rohdaten (Bytes) -> In Base64-Zeichenfolge codieren, dann ist Base64-Zeichenfolge zurück -> In Rohdaten (Bytes) dekodieren -> einen Eingabestream oder ein Objekt oder was auch immer darauf erstellen, um damit als Bild zu arbeiten wieder ...
LihO
1
Entschuldigung - Ihr Code funktioniert einwandfrei und sogar ein Bild kann wiederhergestellt werden. Es war dumm von mir, einfach den gesamten Vektor in einer CFile zusammenzudrücken, das wird nicht funktionieren! Wenn ich den Vecteor iteriere und jedes einzelne Byte in die Datei schreibe, funktioniert es. Vielleicht ist Filestream hier eine bessere Lösung.
Masche
4
Was ist falsch daran, Binärdaten in std :: string zu speichern?
GaspardP
45

Hier gibt es mehrere Ausschnitte. Dieser ist jedoch kompakt, effizient und c ++ 11-freundlich:

static std::string base64_encode(const std::string &in) {

    std::string out;

    int val=0, valb=-6;
    for (uchar c : in) {
        val = (val<<8) + c;
        valb += 8;
        while (valb>=0) {
            out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val>>valb)&0x3F]);
            valb-=6;
        }
    }
    if (valb>-6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val<<8)>>(valb+8))&0x3F]);
    while (out.size()%4) out.push_back('=');
    return out;
}

static std::string base64_decode(const std::string &in) {

    std::string out;

    std::vector<int> T(256,-1);
    for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; 

    int val=0, valb=-8;
    for (uchar c : in) {
        if (T[c] == -1) break;
        val = (val<<6) + T[c];
        valb += 6;
        if (valb>=0) {
            out.push_back(char((val>>valb)&0xFF));
            valb-=8;
        }
    }
    return out;
}
Manuel Martinez
quelle
1
Ich sollte das zurücknehmen ... Ich sollte einfach keine Leistungstests im Debug-Modus durchführen, sorry. Es ist zumindest schneller als die akzeptierte Lösung.
Marco Freudenberger
5
Bitshifting "int val" aus seinem Bereich ist UB. "unsigned val = 0; int valb = ..." ist korrekt.
Kevin Yin
21

Ich denke, das funktioniert besser:

#include <string>

static const char* B64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const int B64index[256] =
{
    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, 63, 62, 62, 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,  63,
    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
};

const std::string b64encode(const void* data, const size_t &len)
{
    std::string result((len + 2) / 3 * 4, '=');
    char *p = (char*) data, *str = &result[0];
    size_t j = 0, pad = len % 3;
    const size_t last = len - pad;

    for (size_t i = 0; i < last; i += 3)
    {
        int n = int(p[i]) << 16 | int(p[i + 1]) << 8 | p[i + 2];
        str[j++] = B64chars[n >> 18];
        str[j++] = B64chars[n >> 12 & 0x3F];
        str[j++] = B64chars[n >> 6 & 0x3F];
        str[j++] = B64chars[n & 0x3F];
    }
    if (pad)  /// set padding
    {
        int n = --pad ? int(p[last]) << 8 | p[last + 1] : p[last];
        str[j++] = B64chars[pad ? n >> 10 & 0x3F : n >> 2];
        str[j++] = B64chars[pad ? n >> 4 & 0x03F : n << 4 & 0x3F];
        str[j++] = pad ? B64chars[n << 2 & 0x3F] : '=';
    }
    return result;
}

const std::string b64decode(const void* data, const size_t &len)
{
    if (len == 0) return "";

    unsigned char *p = (unsigned char*) data;
    size_t j = 0,
        pad1 = len % 4 || p[len - 1] == '=',
        pad2 = pad1 && (len % 4 > 2 || p[len - 2] != '=');
    const size_t last = (len - pad1) / 4 << 2;
    std::string result(last / 4 * 3 + pad1 + pad2, '\0');
    unsigned char *str = (unsigned char*) &result[0];

    for (size_t i = 0; i < last; i += 4)
    {
        int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
        str[j++] = n >> 16;
        str[j++] = n >> 8 & 0xFF;
        str[j++] = n & 0xFF;
    }
    if (pad1)
    {
        int n = B64index[p[last]] << 18 | B64index[p[last + 1]] << 12;
        str[j++] = n >> 16;
        if (pad2)
        {
            n |= B64index[p[last + 2]] << 6;
            str[j++] = n >> 8 & 0xFF;
        }
    }
    return result;
}

std::string b64encode(const std::string& str)
{
    return b64encode(str.c_str(), str.size());
}

std::string b64decode(const std::string& str64)
{
    return b64decode(str64.c_str(), str64.size());
}

Dank an @Jens Alfke für den Hinweis auf ein Leistungsproblem habe ich einige Änderungen an diesem alten Beitrag vorgenommen. Dieser arbeitet viel schneller als zuvor. Der andere Vorteil ist der reibungslose Umgang mit beschädigten Daten.

Letzte Ausgabe : Obwohl bei solchen Problemen die Geschwindigkeit ein Overkill zu sein scheint, habe ich nur zum Spaß einige andere Modifikationen vorgenommen, um diesen Algorithmus zum schnellsten AFAIK-Algorithmus auf dem Markt zu machen. Besonderer Dank geht an @GaspardP für seine wertvollen Vorschläge und den schönen Benchmark.

Polfosol ఠ_ఠ
quelle
1
Alle diese Aufrufe strchrwerden den Decoder verlangsamen - Sie durchlaufen durchschnittlich 32 Mal eine Schleife für jedes decodierte Byte. Die meisten Lösungen verwenden eine Nachschlagetabelle mit 256 Elementen, um dies zu vermeiden, was viel schneller ist.
Jens Alfke
3
Ich habe stackoverflow.com/questions/342409/… kommentiert - obwohl dieser Code nicht am schnellsten zu codieren ist, ist er am schnellsten zu decodieren (im Vergleich zu 16 anderen Implementierungen).
GaspardP
3
Im Decoder können Sie die Leistung etwas weiter steigern (10-15% in meinen Tests), indem Sie an einem char*statt an einem arbeiten std::string(einfach tun char* out = &str[0]und dann verwenden out[j++]statt str[j++]). Auf diese Weise überspringen Sie die unnötigen Überprüfungen von std::string::operator[]. Vermeiden Sie auch das letzte, push_backwas sich als sehr teuer herausstellen kann, indem Sie ein weiteres Byte zuweisen ( std::string str; str.resize(3*((len+3)/4));und dann out[j++]überall und str.resize(j);am Ende verwenden.
GaspardP
1
Sie haben in der letzten Bearbeitung einen Speicherverlust hinzugefügt, ganz zu schweigen von einer Pufferkopie. Nicht newohne verwenden delete. Eigentlich gar nicht benutzen new. @Gaspard, mir sind keine "unnötigen Überprüfungen durch std::string::operator[]" bekannt (tatsächlich bin ich mir ziemlich sicher, dass es zumindest in der Version keine gibt), aber Sie könnten einen verwenden, vector<char>wenn Sie wirklich verzweifelt sind - Ihr Ersatzcode ist es sowieso nicht was Gaspard vorschlug, das immer noch eine Zeichenfolge für alles außer dem Elementzugriff verwendete und sicher / schnell war, ungeachtet der Tatsache, dass ich es für unnötig halte;)
Leichtigkeitsrennen im Orbit
4
@polfosol ఠ_ఠ, Danke dafür. Bitte verwenden Sie unsigned char *panstelle des char *pEncoders. Ich habe eine beschädigte Base64-Zeichenfolge, wenn meine Eingabe Bytes enthält >= 0x80. Nach dem Hinzufügen unsignedscheint alles in Ordnung zu sein.
Denis Golovkin
15

Mit base-n mini lib können Sie Folgendes tun:

some_data_t in[] { ... };
constexpr int len = sizeof(in)/sizeof(in[0]);

std::string encoded;
bn::encode_b64(in, in + len, std::back_inserter(encoded));

some_data_t out[len];
bn::decode_b64(encoded.begin(), encoded.end(), out);

Die API ist generisch und iteratorbasiert.

Offenlegung: Ich bin der Autor.

Azawadzki
quelle
3
std::sizewäre schöner als sizeofhax
Leichtigkeitsrennen im Orbit
1
In der Tat ist es ab C ++ 17 eindeutig das richtige Warten, um dies zu tun.
Azawadzki
1
Es war von Anfang an der richtige Weg. Es ist nur so, dass Sie es vor C ++ 17 selbst implementieren mussten (aber es ist praktisch ein Einzeiler). Die Größe des Hacks ist in C ++ niemals in Ordnung.
Leichtigkeitsrennen im Orbit
13

Nach diesem hervorragenden Vergleich von GaspardP würde ich diese Lösung nicht wählen. Es ist nicht das Schlimmste, aber es ist auch nicht das Beste. Das einzige, was es in Gang gebracht hat, ist, dass es möglicherweise leichter zu verstehen ist.

Ich fand die beiden anderen Antworten ziemlich schwer zu verstehen. Sie erzeugen auch einige Warnungen in meinem Compiler und die Verwendung einer Suchfunktion im Dekodierungsteil sollte zu einer ziemlich schlechten Effizienz führen. Also habe ich beschlossen, meine eigenen zu rollen.

Header:

#ifndef _BASE64_H_
#define _BASE64_H_

#include <vector>
#include <string>
typedef unsigned char BYTE;

class Base64
{
public:
    static std::string encode(const std::vector<BYTE>& buf);
    static std::string encode(const BYTE* buf, unsigned int bufLen);
    static std::vector<BYTE> decode(std::string encoded_string);
};

#endif

Körper:

static const BYTE from_base64[] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                                    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
                                    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255,  62, 255,  63, 
                                     52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255, 
                                    255,   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, 255, 255, 255, 255,  63,
                                    255,  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, 255, 255, 255, 255, 255};

static const char to_base64[] = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";


std::string Base64::encode(const std::vector<BYTE>& buf)
{
    if (buf.empty())
        return ""; // Avoid dereferencing buf if it's empty
    return encode(&buf[0], (unsigned int)buf.size());
}

std::string Base64::encode(const BYTE* buf, unsigned int bufLen)
{
    // Calculate how many bytes that needs to be added to get a multiple of 3
    size_t missing = 0;
    size_t ret_size = bufLen;
    while ((ret_size % 3) != 0)
    {
        ++ret_size;
        ++missing;
    }

    // Expand the return string size to a multiple of 4
    ret_size = 4*ret_size/3;

    std::string ret;
    ret.reserve(ret_size);

    for (unsigned int i=0; i<ret_size/4; ++i)
    {
        // Read a group of three bytes (avoid buffer overrun by replacing with 0)
        size_t index = i*3;
        BYTE b3[3];
        b3[0] = (index+0 < bufLen) ? buf[index+0] : 0;
        b3[1] = (index+1 < bufLen) ? buf[index+1] : 0;
        b3[2] = (index+2 < bufLen) ? buf[index+2] : 0;

        // Transform into four base 64 characters
        BYTE b4[4];
        b4[0] =                         ((b3[0] & 0xfc) >> 2);
        b4[1] = ((b3[0] & 0x03) << 4) + ((b3[1] & 0xf0) >> 4);
        b4[2] = ((b3[1] & 0x0f) << 2) + ((b3[2] & 0xc0) >> 6);
        b4[3] = ((b3[2] & 0x3f) << 0);

        // Add the base 64 characters to the return value
        ret.push_back(to_base64[b4[0]]);
        ret.push_back(to_base64[b4[1]]);
        ret.push_back(to_base64[b4[2]]);
        ret.push_back(to_base64[b4[3]]);
    }

    // Replace data that is invalid (always as many as there are missing bytes)
    for (size_t i=0; i<missing; ++i)
        ret[ret_size - i - 1] = '=';

    return ret;
}

std::vector<BYTE> Base64::decode(std::string encoded_string)
{
    // Make sure string length is a multiple of 4
    while ((encoded_string.size() % 4) != 0)
        encoded_string.push_back('=');

    size_t encoded_size = encoded_string.size();
    std::vector<BYTE> ret;
    ret.reserve(3*encoded_size/4);

    for (size_t i=0; i<encoded_size; i += 4)
    {
        // Get values for each group of four base 64 characters
        BYTE b4[4];
        b4[0] = (encoded_string[i+0] <= 'z') ? from_base64[encoded_string[i+0]] : 0xff;
        b4[1] = (encoded_string[i+1] <= 'z') ? from_base64[encoded_string[i+1]] : 0xff;
        b4[2] = (encoded_string[i+2] <= 'z') ? from_base64[encoded_string[i+2]] : 0xff;
        b4[3] = (encoded_string[i+3] <= 'z') ? from_base64[encoded_string[i+3]] : 0xff;

        // Transform into a group of three bytes
        BYTE b3[3];
        b3[0] = ((b4[0] & 0x3f) << 2) + ((b4[1] & 0x30) >> 4);
        b3[1] = ((b4[1] & 0x0f) << 4) + ((b4[2] & 0x3c) >> 2);
        b3[2] = ((b4[2] & 0x03) << 6) + ((b4[3] & 0x3f) >> 0);

        // Add the byte to the return value if it isn't part of an '=' character (indicated by 0xff)
        if (b4[1] != 0xff) ret.push_back(b3[0]);
        if (b4[2] != 0xff) ret.push_back(b3[1]);
        if (b4[3] != 0xff) ret.push_back(b3[2]);
    }

    return ret;
}

Verwendung:

BYTE buf[] = "ABCD";
std::string encoded = Base64::encode(buf, 4);
// encoded = "QUJDRA=="
std::vector<BYTE> decoded = Base64::decode(encoded);

Ein Bonus hierbei ist, dass die Decodierungsfunktion auch die URL-Variante der Base 64-Codierung decodieren kann.

DaedalusAlpha
quelle
2
Bonuspunkte für no find () und eine Reserve () für die Ausgaben. 1 kleiner Punkt, weil Sie die Eingabe als Kopie nehmen (so können Sie bei Bedarf am Ende ein = hinzufügen). Wäre schön gewesen, wenn es keine Kopie wäre.
elegante Würfel
3
länger: Diese Antwort gefällt mir am besten ... auch für die Anzahl der Zeilen. Bonuspunkte für no find () und eine Reserve () für die Ausgaben. Dinge, die ich verbessern würde (was den Code verdrängen würde): Sie nehmen die Eingabe als Kopie (sodass Sie bei Bedarf am Ende ein = hinzufügen können). Wäre schön gewesen, wenn es keine Kopie wäre. Und hätte auch als einfache Funktionen geschrieben werden können - keine Notwendigkeit für die Klasse. Und sollte vor der Dereferenzierung von buf [0] auf leeren Buf-Vektor prüfen. Fügen Sie eine Schnittstelle hinzu, um die Daten in eine Referenz zu schreiben (damit der Anrufer den Speicher wiederverwenden kann).
elegante Würfel
1
Vielen Dank für das Feedback. Der Grund, warum ich die Zeichenfolge nach Wert genommen habe, ist, dass sie den Code vereinfacht, wenn ich immer garantieren kann, dass er eine gültige Länge hat. Ich denke, dass RVO in den meisten Fällen sowieso eine String-Kopie verhindern sollte, also sollte es kein Problem sein. Was die Dereferenzierung von Buf [0] betrifft - guter Fang, das werde ich beheben :)
DaedalusAlpha
1
Ich habe meine eigene modifizierte Version von Ihnen fertiggestellt. Ich werde sie hier zu Ihrem Vergnügen als Antwort veröffentlichen. Ich habe die Notwendigkeit für die Zeichenfolge als Kopie durch Hinzufügen eines Tests beseitigt (Sie führen bereits Tests durch, sodass sie nicht wirklich hinzugefügt werden Leistungsprobleme).
elegante Würfel
1
Ich liege wahrscheinlich falsch, aber sollten die Werte an den Indizes 60 und 61 in from_base64 [] nicht ausgetauscht werden? Ich vermute, die Idee ist, "<" (Index = 60) durch Rückgabe von 255 zu ignorieren und das Füllzeichen "=" (Index = 61) durch Rückgabe von 0 zu erkennen.
pbyhistorian
5

Meine Variation der @ DaedalusAlpha-Antwort. Das Kopieren der Parameter wird auf Kosten einiger Tests vermieden.

Verwendet uint8_t anstelle von BYTE.

Fügt einige praktische Funktionen für den Umgang mit Zeichenfolgen hinzu, obwohl die Eingabedaten normalerweise binär sind und null Bytes enthalten können. Daher sollten sie normalerweise nicht als Zeichenfolge bearbeitet werden (was häufig nullterminierte Daten impliziert).

Fügt auch einige Casts hinzu, um Compiler-Warnungen zu korrigieren (zumindest bei GCC habe ich es noch nicht über MSVC ausgeführt).

Teil von base64.hpp:

   void base64_encode(string & out, const vector<uint8_t>& buf);
   void base64_encode(string & out, const uint8_t* buf, size_t bufLen);
   void base64_encode(string & out, string const& buf);

   void base64_decode(vector<uint8_t> & out, string const& encoded_string);

   // use this if you know the output should be a valid string
   void base64_decode(string & out, string const& encoded_string);

base64.cpp:

static const uint8_t from_base64[128] = {
   // 8 rows of 16 = 128
   // note: only require 123 entries, as we only lookup for <= z , which z=122
               255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
               255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
               255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255,  62, 255,  63, 
                52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,   0, 255, 255, 255, 
               255,   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, 255, 255, 255, 255,  63,
               255,  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, 255, 255, 255, 255, 255
            };

static const char to_base64[65] = 
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"
            "0123456789+/";


void base64_encode(string & out, string const& buf)
{
   if (buf.empty())
      base64_encode(out, NULL, 0);
   else
      base64_encode(out, reinterpret_cast<uint8_t const*>(&buf[0]), buf.size());
}


void base64_encode(string & out, std::vector<uint8_t> const& buf)
{
   if (buf.empty())
      base64_encode(out, NULL, 0);
   else
      base64_encode(out, &buf[0], buf.size());
}

void base64_encode(string & ret, uint8_t const* buf, size_t bufLen)
{
   // Calculate how many bytes that needs to be added to get a multiple of 3
   size_t missing = 0;
   size_t ret_size = bufLen;
   while ((ret_size % 3) != 0)
   {
      ++ret_size;
      ++missing;
   }

   // Expand the return string size to a multiple of 4
   ret_size = 4*ret_size/3;

   ret.clear();
   ret.reserve(ret_size);

   for (size_t i = 0; i < ret_size/4; ++i)
   {
      // Read a group of three bytes (avoid buffer overrun by replacing with 0)
      const size_t index = i*3;
      const uint8_t b3_0 = (index+0 < bufLen) ? buf[index+0] : 0;
      const uint8_t b3_1 = (index+1 < bufLen) ? buf[index+1] : 0;
      const uint8_t b3_2 = (index+2 < bufLen) ? buf[index+2] : 0;

      // Transform into four base 64 characters
      const uint8_t b4_0 =                        ((b3_0 & 0xfc) >> 2);
      const uint8_t b4_1 = ((b3_0 & 0x03) << 4) + ((b3_1 & 0xf0) >> 4);
      const uint8_t b4_2 = ((b3_1 & 0x0f) << 2) + ((b3_2 & 0xc0) >> 6);
      const uint8_t b4_3 = ((b3_2 & 0x3f) << 0);

      // Add the base 64 characters to the return value
      ret.push_back(to_base64[b4_0]);
      ret.push_back(to_base64[b4_1]);
      ret.push_back(to_base64[b4_2]);
      ret.push_back(to_base64[b4_3]);
   }

   // Replace data that is invalid (always as many as there are missing bytes)
   for (size_t i = 0; i != missing; ++i)
      ret[ret_size - i - 1] = '=';
}


template <class Out>
void base64_decode_any( Out & ret, std::string const& in)
{
   typedef typename Out::value_type T;

   // Make sure the *intended* string length is a multiple of 4
   size_t encoded_size = in.size();

   while ((encoded_size % 4) != 0)
      ++encoded_size;

   const size_t N = in.size();
   ret.clear();
   ret.reserve(3*encoded_size/4);

   for (size_t i = 0; i < encoded_size; i += 4)
   {
      // Note: 'z' == 122

      // Get values for each group of four base 64 characters
      const uint8_t b4_0 = (            in[i+0] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+0])] : 0xff;
      const uint8_t b4_1 = (i+1 < N and in[i+1] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+1])] : 0xff;
      const uint8_t b4_2 = (i+2 < N and in[i+2] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+2])] : 0xff;
      const uint8_t b4_3 = (i+3 < N and in[i+3] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+3])] : 0xff;

      // Transform into a group of three bytes
      const uint8_t b3_0 = ((b4_0 & 0x3f) << 2) + ((b4_1 & 0x30) >> 4);
      const uint8_t b3_1 = ((b4_1 & 0x0f) << 4) + ((b4_2 & 0x3c) >> 2);
      const uint8_t b3_2 = ((b4_2 & 0x03) << 6) + ((b4_3 & 0x3f) >> 0);

      // Add the byte to the return value if it isn't part of an '=' character (indicated by 0xff)
      if (b4_1 != 0xff) ret.push_back( static_cast<T>(b3_0) );
      if (b4_2 != 0xff) ret.push_back( static_cast<T>(b3_1) );
      if (b4_3 != 0xff) ret.push_back( static_cast<T>(b3_2) );
   }
}

void base64_decode(vector<uint8_t> & out, string const& encoded_string)
{
   base64_decode_any(out, encoded_string);
}

void base64_decode(string & out, string const& encoded_string)
{
   base64_decode_any(out, encoded_string);
}
elegante Würfel
quelle
1
Ich wusste das nicht && und || hatte eine definierte Auswertungsreihenfolge in C ++, also habe ich heute etwas Neues gelernt. In solchen Fällen, in denen Sie den Zustand eines Index überprüfen und gleichzeitig sicherstellen möchten, dass der Index nicht außerhalb des Bereichs liegt, ist dies äußerst nützlich.
DaedalusAlpha
1
Ja, ich benutze diese Technik die ganze Zeit.
elegante Würfel
4

Eine kleine Variation mit einer kompakteren Nachschlagetabelle und Verwendung von c ++ 17-Funktionen:

std::string base64_decode(const std::string_view in) {
  // table from '+' to 'z'
  const uint8_t lookup[] = {
      62,  255, 62,  255, 63,  52,  53, 54, 55, 56, 57, 58, 59, 60, 61, 255,
      255, 0,   255, 255, 255, 255, 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,
      255, 255, 255, 255, 63,  255, 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};
  static_assert(sizeof(lookup) == 'z' - '+' + 1);

  std::string out;
  int val = 0, valb = -8;
  for (uint8_t c : in) {
    if (c < '+' || c > 'z')
      break;
    c -= '+';
    if (lookup[c] >= 64)
      break;
    val = (val << 6) + lookup[c];
    valb += 6;
    if (valb >= 0) {
      out.push_back(char((val >> valb) & 0xFF));
      valb -= 8;
    }
  }
  return out;
}

Wenn Sie nicht über std :: string_view verfügen, versuchen Sie stattdessen std :: experimental :: string_view.

nunojpg
quelle
2

Meine Version ist ein einfacher schneller Encoder (Decoder) von base64 für C ++ Builder.

//---------------------------------------------------------------------------
UnicodeString __fastcall TExample::Base64Encode(void *data,int length)
{
 if (length<=0) return L"";
 static const char set[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 unsigned char *in=(unsigned char*)data;
 char *pos,*out=pos=new char[((length-1)/3+1)<<2];
 while ((length-=3)>=0)
 {
  pos[0]=set[in[0]>>2];
  pos[1]=set[((in[0]&0x03)<<4)|(in[1]>>4)];
  pos[2]=set[((in[1]&0x0F)<<2)|(in[2]>>6)];
  pos[3]=set[in[2]&0x3F];
  pos+=4;
  in+=3;
 };
 if ((length&2)!=0)
 {
  pos[0]=set[in[0]>>2];
  if ((length&1)!=0)
  {
   pos[1]=set[((in[0]&0x03)<<4)|(in[1]>>4)];
   pos[2]=set[(in[1]&0x0F)<<2];
  }
  else
  {
   pos[1]=set[(in[0]&0x03)<<4];
   pos[2]='=';
  };
  pos[3]='=';
  pos+=4;
 };
 UnicodeString code=UnicodeString(out,pos-out);
 delete[] out;
 return code;
};
//---------------------------------------------------------------------------
int __fastcall TExample::Base64Decode(const UnicodeString code,unsigned char **data)
{
 int length;
 if (((length=code.Length())==0)||((length&3)!=0)) return 0;
 wchar_t *str=code.c_str();
 unsigned char *pos,*out=pos=new unsigned char[(length>>2)*3];
 while (*str!=0)
 {
  length=-1;
  int shift=18,bits=0;
  do
  {
   wchar_t s=str[++length];
   if ((s>=L'A')&&(s<=L'Z')) bits|=(s-L'A')<<shift;
   else if ((s>=L'a')&&(s<=L'z')) bits|=(s-(L'a'-26))<<shift;
   else if (((s>=L'0')&&(s<=L'9'))) bits|=(s-(L'0'-52))<<shift;
   else if (s==L'+') bits|=62<<shift;
   else if (s==L'/') bits|=63<<shift;
   else if (s==L'=')
   {
    length--;
    break;
   }
   else
   {
    delete[] out;
    return 0;
   };
  }
  while ((shift-=6)>=0);
  pos[0]=bits>>16;
  pos[1]=bits>>8;
  pos[2]=bits;
  pos+=length;
  str+=4;
 };
 *data=out;
 return pos-out;
};
//---------------------------------------------------------------------------
Tutralex
quelle
1

Ich bin dran. Ich benutze das:

class BinaryVector {
public:
    std::vector<char> bytes;

    uint64_t bit_count = 0;

public:
    /* add a bit to the end */
    void push_back(bool bit);

    /* return false if character is unrecognized */
    bool pushBase64Char(char b64_c);
};

void BinaryVector::push_back(bool bit)
{
    if (!bit_count || bit_count % 8 == 0) {
        bytes.push_back(bit << 7);
    }
    else {
        uint8_t next_bit = 8 - (bit_count % 8) - 1;
        bytes[bit_count / 8] |= bit << next_bit;
    }
    bit_count++;
}

/* converts one Base64 character to 6 bits */
bool BinaryVector::pushBase64Char(char c)
{
    uint8_t d;

    // A to Z
    if (c > 0x40 && c < 0x5b) {
        d = c - 65;  // Base64 A is 0
    }
    // a to z
    else if (c > 0x60 && c < 0x7b) {
        d = c - 97 + 26;  // Base64 a is 26
    }
    // 0 to 9
    else if (c > 0x2F && c < 0x3a) {
        d = c - 48 + 52;  // Base64 0 is 52
    }
    else if (c == '+') {
        d = 0b111110;
    }
    else if (c == '/') {
        d = 0b111111;
    }
    else if (c == '=') {
        d = 0;
    }
    else {
        return false;
    }

    push_back(d & 0b100000);
    push_back(d & 0b010000);
    push_back(d & 0b001000);
    push_back(d & 0b000100);
    push_back(d & 0b000010);
    push_back(d & 0b000001);

    return true;
}

bool loadBase64(std::vector<char>& b64_bin, BinaryVector& vec)
{
    for (char& c : b64_bin) {
        if (!vec.pushBase64Char(c)) {
            return false;
        }
    }
    return true;
}

Verwenden Sie vec.bytesdiese Option, um auf konvertierte Daten zuzugreifen.

Macelaru Tiberiu
quelle
0

Ich habe zuerst meine eigene Version erstellt und dann dieses Thema gefunden.

Warum sieht meine Version einfacher aus als die anderen hier vorgestellten? Mache ich etwas falsch? Ich habe es nicht auf Geschwindigkeit getestet.

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

PS Meine Methode funktioniert gut, ich habe sie mit Node.js getestet:

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