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

Erklæring om bruk av informasjonskapsler/cookies

Ditt personvern er ivaretatt; Informasjonskapsler brukes kun for funksjon.

Tredjeparter som har sine egne erklæringer:
Site Stats
OK / Skjul