←
Jeg skrev litt kode for Base64 enkoding og dekoding hvor jeg tok utgangspunkt i wikipedia artikkelen ↗ om Base64.
base64.h
Resultat hvor jeg enkoder og dekoder et bilde samt en streng med nordiske tegn, for å illustrere at det handler kun om bits og bytes.
ani.gif orginalbilde. 2,44 MB (2 563 212 byte)

Etter turen i kverna. 2,44 MB (2 563 212 byte).

Følgende kode ble brukt for å lage demoprogrammet ovenfor.
djFileBytes.h
djBase64++.cpp (demoprogrammet)
Publisert: 12.jul.2023 13:20 | Oppdatert: 12.jul.2023 13:43.
C/C++
djBase64++
Jeg skrev litt kode for Base64 enkoding og dekoding hvor jeg tok utgangspunkt i wikipedia artikkelen ↗ om Base64.
base64.h
// // Struktur for lagring av bytedata. // struct ByteData { unsigned long Size = 0; unsigned char* Bytes = 0; }; // // Klasse for å enkode og dekode bytes med Base64. // class djBase64 { public: static std::string EncodeBytes(ByteData* bd) { char base64_table[65]; table(base64_table); // Hent alle bits fra bytes. std::string bits; for (unsigned long a = 0; a < bd->Size; a++) { std::bitset<8> b(bd->Bytes[a]); bits.append(b.to_string()); } //std::ofstream _debug ("encode_orig_bit_string.txt"); //_debug.write(bits.c_str(), bits.length()); // Del de opp i 6-bit deler og lag en base64 streng. std::string base64str = ""; std::string sixbit = ""; int c; // Håndter komplette 6-bit sekvenser. for (unsigned int a = 0; a < bits.length(); a++) { if (sixbit.length() == 6) { c = std::stoi(sixbit, nullptr, 2); base64str.push_back(base64_table[c]); sixbit.clear(); } sixbit.append(bits.substr(a, 1)); } // Håndter gjenstående bits. if (sixbit.length() > 0) { while (sixbit.length() < 6) sixbit.push_back('0'); c = std::stoi(sixbit, nullptr, 2); base64str.push_back(base64_table[c]); } // // Legg på = padding hvis strengen ikke er delbar med 4. // Det trengs 4 base64 karakterer for å dekode 3 bytes. // Det trengs 3 base64 karakterer for å dekode 2 bytes. // Det trengs 2 base64 karakterer for å dekode 1 byte. // Dette er fordi 1x 8-bit tar 2x 6-bit lengder. // Ved dekoding vet man ved hjelp av padding hvor mange // bytes det er på slutten av en streng. // // Ikke så nyttig ved kun dekoding av enkeltstrenger, men // nyttig I tilfelle man dekoder strenger på rekke og rad. // while (base64str.length() % 4) base64str.push_back('='); return base64str; } static void DecodeBytes(std::string Base64String, ByteData* bd) { // Base64 alfabet. char base64_table[65]; table(base64_table); // Hent alle bits til en lang streng, unntatt =. std::string bits; for (size_t a = 0; a < Base64String.length(); a++) { for (int b = 0; b < 65; b++) { if (Base64String[a] == base64_table[b]) { std::bitset<6> sixbits(b); bits.append(sixbits.to_string()); } } // Ta høyde for padding. if (Base64String[a] == '=') { for (int c = 0; c < 6; c++) bits.pop_back(); // Fjern evt. 0 padding utført på slutten. while (bits.length() % 8) bits.pop_back(); } } //std::ofstream _debug ("decode_orig_bit_string.txt"); //_debug.write(bits.c_str(), bits.length()); // Bit-streng bør nå være klar for dekoding. std::string eightbit = ""; std::vector<unsigned char> c; for (size_t a = 0; a < bits.length() + 1; a++) { // bits.length()+1 gjør at alle bits blir sjekket. if (eightbit.length() == 8) { c.push_back(std::stoi(eightbit, nullptr, 2)); eightbit.clear(); } // Ikke gå over indeks. if (a < bits.length()) eightbit.push_back(bits[a]); } // Skriv bytes til pekt datastruktur. bd->Size = (unsigned long)c.size(); bd->Bytes = (unsigned char*)malloc(sizeof(unsigned char) * bd->Size); for (size_t a = 0; a < c.size(); a++) bd->Bytes[a] = c[a]; } private: static void table(char* out_table) { // Base64 alfabet. char base64_table[65] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=' }; // Kopier til tabell som spør. for (int a = 0; a < 65; a++) out_table[a] = base64_table[a]; } };
Resultat hvor jeg enkoder og dekoder et bilde samt en streng med nordiske tegn, for å illustrere at det handler kun om bits og bytes.
C:\Users\dj\source\repos\djBase64++\x64\Release>djBase64++.exe Enkoder ani.gif og skriver dekodet output til ani_out.gif ... Ferdig med filtest, sjekk resultat. Skal enkode: æøåÆØÅ er fine norske bokstaver. (64 bytes). Enkodet streng: 5gD4AOUAxgDYAMUAIABlAHIAIABmAGkAbgBlACAAbgBvAHIAcwBrAGUAIABiAG8AawBzAHQAYQB2AGUAcgAuAA== Dekoder 3x sammensatt streng på ett kall for å teste padding: æøåÆØÅ er fine norske bokstaver.æøåÆØÅ er fine norske bokstaver.æøåÆØÅ er fine norske bokstaver. (192 bytes).
ani.gif orginalbilde. 2,44 MB (2 563 212 byte)

Etter turen i kverna. 2,44 MB (2 563 212 byte).

Følgende kode ble brukt for å lage demoprogrammet ovenfor.
djFileBytes.h
// // Klasse for å lese og skrive filer binært. // class djFileBytes { public: static void ReadFileBytes(const char* filename, ByteData* bd) { FILE* fptr; size_t fres; // Åpne fil i binær lesemodus. fopen_s(&fptr, filename, "rb"); if (fptr == 0) { std::cout << L"ReadFileBytes feilet å åpne filen." << std::endl; exit(1); } // Filstørrelse. fseek(fptr, 0, SEEK_END); bd->Size = ftell(fptr); rewind(fptr); // Reserver minne til å holde fildata. bd->Bytes = (unsigned char*)malloc(sizeof(unsigned char) * bd->Size); if (bd->Bytes == 0) { std::cout << L"ReadFileBytes feilet å reservere minne." << std::endl; exit(1); } // Kopier fila inn til minnet. fres = fread(bd->Bytes, 1, bd->Size, fptr); if (fres != bd->Size) { std::cout << L"ReadFileBytes feilet å lese filen til minnet." << std::endl; exit(1); } fclose(fptr); } static void WriteFileBytes(ByteData* fd, const char* filename) { FILE* fptr; size_t fres; // Åpne fil i binær skrivemodus. fopen_s(&fptr, filename, "wb"); if (fptr == 0) { std::cout << L"WriteFileBytes feilet å åpne filen." << std::endl; exit(1); } fres = fwrite(fd->Bytes, 1, fd->Size, fptr); fclose(fptr); } };
djBase64++.cpp (demoprogrammet)
#include <iostream> #include <bitset> #include <string> #include <vector> #include <Windows.h> #include "base64.h" #include "djFileBytes.h" int main() { setlocale(LC_ALL, ""); // // Test med filer. // ByteData BD; // Les bytes fra fil. djFileBytes::ReadFileBytes("ani.gif", &BD); // Kod om til base64 og tilbake igjen. std::string Base64String = djBase64::EncodeBytes(&BD); free(BD.Bytes); std::cout << "\r\nEnkoder ani.gif og skriver dekodet output til ani_out.gif ...\r\n"; djBase64::DecodeBytes(Base64String, &BD); // Skriv bytes til fil. djFileBytes::WriteFileBytes(&BD, "ani_out.gif"); // Trenger ikke ta vare på bytes i minnet lenger. free(BD.Bytes); std::cout << "Ferdig med filtest, sjekk resultat.\r\n\r\n"; // // Tester i denne omgang med wchar_t tekstdata. // std::wstring orig_tekst = L"æøåÆØÅ er fine norske bokstaver."; std::wcout << L"Skal enkode: " << orig_tekst; // Hent byte-vis alle wchar_t's inn i orig_bytes. std::vector<unsigned char> orig_bytes; unsigned char wchar_bytes[2] = {0}; for (int a = 0; a < orig_tekst.size(); a++) { *(wchar_t*)&wchar_bytes[0] = *(wchar_t*)&orig_tekst[a]; orig_bytes.push_back(wchar_bytes[0]); orig_bytes.push_back(wchar_bytes[1]); } std::cout << " (" << orig_bytes.size() << " bytes).\r\n"; // Reserver minne til å holde tekstdata. BD.Size = (unsigned long)orig_bytes.size(); BD.Bytes = (unsigned char*)malloc(sizeof(unsigned char) * BD.Size); for (size_t a = 0; a < BD.Size; a++) BD.Bytes[a] = orig_bytes.data()[a]; orig_bytes.clear(); // Vis enkoding av streng. Base64String = djBase64::EncodeBytes(&BD); std::cout << "Enkodet streng: " << Base64String << "\r\n\r\n"; free(BD.Bytes); // Vis dekoding av sammensatt streng for å bekrefte at padding tas høyde for. Base64String = Base64String + Base64String + Base64String; djBase64::DecodeBytes(Base64String, &BD); wchar_t dekodet_karakter[2] = L" "; std::wstring dekodet_tekst = L""; for (size_t a = 0; a < BD.Size; a += 2) { *(wchar_t*)&dekodet_karakter[0] = *(wchar_t*)&BD.Bytes[a]; dekodet_tekst.append(dekodet_karakter); } std::wcout << L"Dekoder 3x sammensatt streng på ett kall for å teste padding: \r\n" << dekodet_tekst << " (" << BD.Size << " bytes).\r\n\r\n"; free(BD.Bytes); }
Publisert: 12.jul.2023 13:20 | Oppdatert: 12.jul.2023 13:43.
C/C++