• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed C++, biblioteka standardowa. Szyfrowanie pliku.

Object Storage Arubacloud
0 głosów
265 wizyt
pytanie zadane 27 września 2019 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
zamknięte 2 października 2019 przez Jakub 0

Witam, już kilka razy robiłem programy xor'ujące pliki, nigdy jednak jeszcze nie próbowałem tego w czystym C++ ( bibliotece standardowej ). Oto mój kod:

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>

class StrLoopIterator {
private:
	std::string m_str;
	std::string::iterator m_it; 

public:
	StrLoopIterator(std::string str) : m_str(str), m_it(m_str.begin()) {}
	
	char next() {
		char t = *m_it;
		m_it++; 
		if (m_it >= m_str.end())
			m_it = m_str.begin();
		return t;
	}

	void reset() {
		m_it = m_str.begin();
	}
};

//-------------------------------------------------------------------------------

std::streampos getFileByteSize(std::string filename) {
	std::ifstream bfile(filename);
	if (!bfile.is_open()) 
		return 0; 
	bfile.seekg(0, std::ios_base::end);
	std::streampos size = bfile.tellg(); 
	bfile.clear(); 
	bfile.close();
	return size; 
}

//-------------------------------------------------------------------------------

void fileXOR(std::string filename, std::string key, std::streampos filesize) {
	std::fstream file(filename, std::ios_base::out | std::ios_base::in | std::ios_base::binary);
	if (!file.is_open())
		throw "File not found";

	StrLoopIterator keyit(key);
	for (std::streampos i = 0; i < filesize; i+=1) {
		char ch;
		file.seekg(i);
		file.read(&ch, sizeof ch);
		file.seekg(i);

		ch ^= keyit.next(); 
		file.write(&ch, sizeof ch);
	}

	file.clear();
	file.close();
}

//-------------------------------------------------------------------------------

int main() {

	std::cout << "File name: ";
	std::string filename; 
	std::cin >> filename; 
	std::cout << "Key: ";
	std::string key;
	std::cin >> key; 

	std::streampos filesize = getFileByteSize(filename);

	try {
		fileXOR(filename, key, filesize);
	}
	catch (char* str) {
		std::cout << str << "\n";
	}

	std::cin.get();
	std::cin.get();
}

I tu mam pytanie dotyczące efektywności tego co zrobiłem, tyczy się to oczywiście głównie samego odczytu i zapisu informacji do pliku:

StrLoopIterator keyit(key);
	for (std::streampos i = 0; i < filesize; i+=1) {
		char ch;
		file.seekg(i);
		file.read(&ch, sizeof ch);
		file.seekg(i);

		ch ^= keyit.next(); 
		file.write(&ch, sizeof ch);
	}

zapis i odczyt odbywa się w jednej pętli, bajt po bajcie. Dla małych plików jest ok, ale przetwarzanie zdjęć trwa nawet ponad minutę. Czy jest jakiś bardziej szybki i elegancki sposób w jaki mógłbym to zrealizować? Może pobierać większe bloki danych w zależności od pliku?

Pytanie krótkie, ale zawsze jak coś napiszę to zdaję sobie sprawę że to może być bardzo nieprofesjonalny kod i chcę znać ewentualne lepsze wyjścia z sytuacji ;)

Dziękuje za porady i pozdrawiam :)

komentarz zamknięcia: otrzymałem pomoc

3 odpowiedzi

0 głosów
odpowiedź 27 września 2019 przez mokrowski Mędrzec (155,460 p.)
wybrane 2 października 2019 przez Jakub 0
 
Najlepsza

Buforowane i z użyciem algorytmu. Jeśli dalej mała wydajność, pozostaną mechanizmy systemowe:

#include <fstream>
#include <string_view>
#include <iterator>
#include <iomanip>
#include <algorithm>

void cryptXOR(std::istream& in, std::ostream& out, std::string_view key) {
    using iItType = std::istream_iterator<char>;
    using oItType = std::ostream_iterator<char>;
    std::transform(iItType(in), iItType(), oItType(out),
        [&key, it = key.begin()](char c) mutable {
            char oVal = c ^ *it;
            if(++it == key.end()) {
                it = key.begin();
            }
            return oVal;
    });
}

constexpr static std::size_t BuffSize = 4096;

int main() {
    char iBuffer[BuffSize];
    char oBuffer[BuffSize];
    // TODO: Check open error...
    {
        std::ifstream iFile;
        std::ofstream oFile;
        iFile.rdbuf()->pubsetbuf(iBuffer, BuffSize);
        oFile.rdbuf()->pubsetbuf(oBuffer, BuffSize);
        iFile.open("input.txt");
        iFile >> std::noskipws;
        oFile.open("output.txt");
        std::string_view key = "tajne";
        cryptXOR(iFile, oFile, key);
    }
}

 

komentarz 27 września 2019 przez mokrowski Mędrzec (155,460 p.)

Masz tu jeszcze wersję z iteratorem modulo i użyciem innych buforów. Jest znacząco szybsza:

#include <fstream>
#include <string_view>
#include <iterator>
#include <algorithm>

template<typename Container>
class ModuleIterator {
public:
    using value_type = typename Container::value_type;
    ModuleIterator(const Container& c);
    value_type operator*() const;
    ModuleIterator& operator++();
private:
    using it = typename Container::const_iterator;
    it begin;
    it end;
    it current;
};

template<typename T>
ModuleIterator<T>::ModuleIterator(const T& c)
    : begin{c.begin()}, end{c.end()}, current{c.begin()}
{}

template<typename T>
typename ModuleIterator<T>::value_type ModuleIterator<T>::operator*() const {
    return *current;
}

template<typename T>
ModuleIterator<T>& ModuleIterator<T>::operator++() {
    ++current;
    if (current == end) {
        current = begin;
    }
    return *this;
}

template<typename T>
ModuleIterator<T> make_module_iterator(T const& t) {
    return ModuleIterator<T>(t);
}

void cryptXOR(std::istream& in, std::ostream& out, std::string_view key) {
    using iItType = std::istreambuf_iterator<char>;
    using oItType = std::ostreambuf_iterator<char>;
    auto mIter = make_module_iterator(key);
    std::transform(iItType{in}, {}, oItType{out},
        [&](char c) {
            auto outVal = *mIter ^ c;
            ++mIter;
            return outVal;
    });
}

int main() {
    std::ifstream inFile{"input.txt", std::ios::binary};
    std::ofstream outFile{"output.txt", std::ios::binary};
    std::string_view key{"bardzo tajne"};
    cryptXOR(inFile, outFile, key);
}

 

komentarz 29 września 2019 przez Jakub 0 Pasjonat (23,120 p.)

@mokrowski

Dzięki za odpowiedź, przeanalizowałem twój kod ( pierwszy ), nie rozumiem tylko w pełni po co jest linijka:

iFile >> std::noskipws;

przecież skoro plik jest czytany w trybie binarnym to nie trzeba się martwić o białe znaki. Druga sprawa to dlaczego wielkość bufora to akurat 4096.

Z góry dziękuje za wytłumaczenie :)

komentarz 29 września 2019 przez mokrowski Mędrzec (155,460 p.)
W 1  kodzie nie otwierałem pliku binarnie, stąd skipws. Poprawione w 2 wersji.

4096 to częsta wielkość strony pamięci w typowym OS. Sprawdzają się bufory które są wilelokrornością tej wartości. Można oczywiście pobrać tę wielkość z systemu, ale w każdym robi się to nieco inaczej i stąd uproszczenie.
+1 głos
odpowiedź 27 września 2019 przez adrian17 Ekspert (344,860 p.)
Zamiast ciągle seekować i nadpisywać ten sam plik, spróbuj po prostu po kolei czytać bajty jednego pliku i pisać do drugiego. (jak chcesz nadpisać plik, po prostu na koniec podmień same pliki).

BTW, IMO ten StrLoopIterator jest przekombinowany, ja bym po prostu zrobił indeks % length :P Ale jak już chcesz używać iteratory, to pisz z normalnym interfejsem iteratorów, z operatorami ++, * etc.
+1 głos
odpowiedź 27 września 2019 przez j23 Mędrzec (194,920 p.)
edycja 27 września 2019 przez j23
#include <fstream>
#include <string_view>


int main()
{
	std::ifstream iFile("input.txt", std::ios::binary);
	std::ofstream oFile("output.txt", std::ios::binary);
	
	char buffer[4096];
	std::string key = "super tajne haslo";
	size_t kIndex = 0;
	size_t bytesRead;
	
	while ((bytesRead = iFile.read(buffer, 4096).gcount()) > 0) {
		for (size_t i = 0; i < bytesRead; ++i) {
			buffer[i] ^= key[kIndex++];
			if(kIndex == key.size()) kIndex = 0;
		}

		oFile.write(buffer, bytesRead);
	}

	return 0;
}

 

komentarz 27 września 2019 przez Jakub 0 Pasjonat (23,120 p.)
Kurcze, mam tyle odpowiedzi że nie wiem co najpierw ogarniać ;)
komentarz 27 września 2019 przez j23 Mędrzec (194,920 p.)
Porównaj czasy, bo chyba o wydajność Ci chodziło, i będziesz wiedział co najpierw ogarniać.

 

PS. kod nieco edytowałem.

Podobne pytania

0 głosów
2 odpowiedzi 408 wizyt
0 głosów
1 odpowiedź 137 wizyt
pytanie zadane 27 lipca 2022 w Sprzęt komputerowy przez Krloo Początkujący (480 p.)
0 głosów
0 odpowiedzi 785 wizyt
pytanie zadane 27 września 2018 w Offtop przez Greeenone Pasjonat (16,100 p.)

92,555 zapytań

141,403 odpowiedzi

319,557 komentarzy

61,940 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...