我正在移植我的“加密器/解密器”程序,该程序使用 key ( like this one ) 来触发文本。加密部分已完成,现在我正在研究解密器:
chars[index_of_long_string]
获取字符pass
通过一些谷歌搜索,我发现在 C++ 中使用 vector 会更好。我编写了以下代码以从我的数组中形成一个 vector :
vector<string> arrayAllReplaceDecryptVector(std::begin(arrayAllReplaceDecrypt), end(arrayAllReplaceDecrypt));
然后使用此代码;获取 key_string 的索引,将字符串与
chars[index]
连接起来,然后删除 key_string 的前 128 个字符:size_t index = std::distance(arrayAllReplaceDecryptVector.begin(), find(arrayAllReplaceDecryptVector.begin(), arrayAllReplaceDecryptVector.end(), pass.substr(0,128)));
output = output + chars[index];
pass = pass.substr(128, pass.length());
下图是第 127 行的当前故障,其中
cin >> pass
传递给函数时被切断。这似乎发生在输入中第一次出现空格字符时。这从上面的索引代码中产生了 95。显示的输入是加密后的“Hello”,索引号应该是14、9、22、22,然后是28。最终的输出应该是“Hello”。此代码将重现错误(但是请注意,您需要与编译代码位于同一目录中的这两个 .txt 文件才能运行程序 - [1] 、 [2] ):
#include <iostream>
#include <fstream>
#include <vector>
#include <stdlib.h>
#include <time.h>
#include <string>
using namespace std;
void fillChars(char(&chars)[95]);
string decrypt(char(chars)[95], string pass, string& lastJob);
char chars[95];
int runs = 0, lastJobID = 0;
bool keyCreated = false;
bool charsFilled = false;
int main()
{
string pass, lastJob;
//One time fills the character array
if (charsFilled == false)
{
fillChars(chars);
}
cout << "\nEnter a message to decrypt:\n";
cin >> pass;
lastJob = decrypt(chars, pass, lastJob);
lastJobID = 2;
}
void fillChars(char(&chars)[95])
{
//Opens chars.txt
ifstream characters("chars.txt");
for (int i = 0; i <= 94; i++)
{
if (i == 94)
{
chars[i] = ' ';
}
else
{
//Puts characters into array
characters >> chars[i];
}
}
characters.close();
}
string arrayAllReplaceDecrypt[95];
string decrypt(char(chars)[95], string pass, string& lastJob)
{
cout << "\n" << pass << "\n";
//Encrypted string
string output;
//Opens key
ifstream ifs("Key.txt");
//Reads entire key to string
string content((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
//Loops to add chars to
for (int i = 0; i < 94; i++)
{
//Adds 128 chars to replace array
arrayAllReplaceDecrypt[i] = content.substr(1, 128);
try
{
//Deletes old chars
content = content.substr(130, content.size());
}
catch (const exception& e)
{
//content.clear();
}
}
ifs.close();
vector<string> arrayAllReplaceDecryptVector(std::begin(arrayAllReplaceDecrypt), end(arrayAllReplaceDecrypt));
while (pass.length() > 0)
{
size_t index = std::distance(arrayAllReplaceDecryptVector.begin(), find(arrayAllReplaceDecryptVector.begin(), arrayAllReplaceDecryptVector.end(), pass.substr(0, 128)));
cout << "\n" << index << "\n";
output = output + chars[index];
pass = pass.substr(129, (pass.length() - 128));
}
return(output);
}
最佳答案
很有意思。你的程序随着时间的推移而发展,你发现了一些问题并逐步修补它们。有时通过两次犯同样的错误来消除错误。
尽管如此,仍有一些必须修复的严重错误。这些大多是越界错误。然后是样式错误,例如全局变量和错误数据类型的使用。
所以,软件设计的一个大问题,必须重构
最后但并非最不重要的一点是,您如何加密的想法是行不通的,因为您将第一个字母和 key 一起存储在 Key.txt”中。
sb = sb + (chars[(sb.length()) / 128]);
您始终添加后跟 128 字节 key 的明文字符。所以你的方法的安全性是 0。
那么,接下来。您必须了解数组的索引和边界。数组索引从 0 开始,最后一个有效索引是数组的大小减去 1。对于数组 { 1,2,3,4 } ,大小为 4,第一个索引为 0,最后一个索引为 3 !
您必须始终检查您使用的索引是否有效。例子。在你的声明中:
chars[rand() % 95 + 1]
你创造了许多越界问题。
rand
可以产生介于 0 和非常大的数字。如果这么大的数字的余数是 94,那么你在索引上加 1,得到无效的 95。因为,在变量定义中char chars[95];
int runs = 0;
运行在字符之后定义,您访问变量的低字节
runs
.你可以测试一下。在第一次创建 key 文件时,您会看到许多 0 字符。在第二次运行时,您将创建 '\001' 字符。最好是使用现代 C++ 容器来帮助您进行此类检查。
接下来,阅读您帖子的人无法理解您为什么要做某些事情。所以,我们想知道你为什么写
substr(1,128)
然后 content = content.substr(130, content.size());
.这样,您消除了第一个清除字符,然后读取了 128 个字节。然后您跳过附加到 key 文件的“\n”。所有这些都应该重构。另一个问题。用于阅读您的“chars.txt”。您发现您没有在循环中读取空格 ' ' 字符。但是您没有检查 IO 流的“skipws”属性,而是通过添加一个空格作为最后一个字符来修补它。您可以使用函数
get
或 getline
来克服这个问题。现在回答你的问题。在解密你使用
for (int i = 0; i < 94; i++)
所以,你阅读了除最后一行之外的所有内容。结束然后你做
pass = pass.substr(129, pass.length());
129是错误的。它必须是 128。你已经去掉了第一个清晰的字符和“\n”。然后它起作用了,我在调试器中对其进行了测试。
但是,所有这些都不起作用,因为您为小输入字符串创建了巨大的加密输出。因此,对于 3 个字符“abc”,您将创建 384 个字节的输出。谁应该输入,尤其是错误地创建 0 个字符。因此,您甚至无法测试代码。您应该将加密后的数据存储在一个文件中,并且为了解密,再次从文件中读取它。
顺便说一句,您正在调用您的函数以递归方式运行,这是错误的。
还有更多的发现。因此建议重构它。并请添加大量错误检查。特别是对于任何 IO 功能。您必须测试这是否有效。
如果您有更多问题,请提问。
为了向您展示如何进行错误检查,我为您创建了一个完整可行的解决方案。请参见:
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <random>
#include <algorithm>
#include <iterator>
#include <numeric>
// Definitions ---------------------------------------------------------------------------------------------------------------------
// Filenames that we want to use in our software
const std::string fileNamePrintableCharacters{ "chars.txt" };
const std::string fileNameKey{ "Key.txt" };
// That many printable characters we will use. We will use the ASCII and hence have characters in the range 32-126, and that are 95
constexpr size_t NumberOfUsedPrintableCharacters = 95U;
// One character will be encrypted with this number of bytes
constexpr size_t NumberOfEncryptionCharacters = 128U;
// This is datatype for our key. For each printable character we use NumberOfEncryptionCharacters.
using KeyData = std::vector<std::string>;
// ----------------------------------------------------------------------------------------------------------------------------------
// Read the printable characters from the definition file
std::string getPrintableCharacters() {
// Here we sill store the result. Initialize all values with 0
std::string printableCharacters{};
// Open the file with the printable characters and check, if the file could be opened
if (std::ifstream printableCharactersFileStream{ fileNamePrintableCharacters }; printableCharactersFileStream) {
// Read the printable characters and check, if that worked
if (!std::getline(printableCharactersFileStream, printableCharacters) || (printableCharacters.size() != NumberOfUsedPrintableCharacters)) {
std::cerr << "\n\n*** Error: Could not read the expected data from ('" << fileNamePrintableCharacters << "')\n";
printableCharacters = {};
}
}
else { // In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: File with printable characters ('" << fileNamePrintableCharacters << "') could not be opened\n";
}
// Return the array with printable characters to the caller using Return Value Optimization / Copy Elision
return printableCharacters;
}
// ----------------------------------------------------------------------------------------------------------------------------------
std::string generateNewPrintableCharactersFile() {
// Here we will stor the resulting printabl characters string
std::string printableCharacters(NumberOfUsedPrintableCharacters, '\0');
// Open output file and chcke, if it could be opened
if (std::ofstream printableCharactersFileStream{ fileNamePrintableCharacters }; printableCharactersFileStream) {
// Create all printable characters
std::iota(printableCharacters.begin(), printableCharacters.end(), ' ');
if (!(printableCharactersFileStream << printableCharacters)) {
std::cerr << "\n\n*** Error: Could not create printable character file\n";
printableCharacters.clear();
}
}
return printableCharacters;
}
// ----------------------------------------------------------------------------------------------------------------------------------
KeyData getKeyData() {
// Here we will store all key data
KeyData keyData{};
// indicator for error. In case of error, we delete all key data
bool thereWasAnError = false;
// Open the file with the key data and check, if the file could be opened
if (std::ifstream keyDataFileStream{ fileNameKey }; keyDataFileStream) {
// Read NumberOfUsedPrintableCharacters lines from the key data file
for (size_t i{}; (i < NumberOfUsedPrintableCharacters) && keyDataFileStream && !thereWasAnError; ++i) {
// Read a line, and check, if that worked
if (std::string line{}; std::getline(keyDataFileStream, line)) {
// Sanity check. Check, if there were the expected number of characters in the key file
if (line.size() != NumberOfEncryptionCharacters) {
std::cerr << "\n\n*** Error: Invalid data in keyfile line " << i + 1 << "\n";
thereWasAnError = true;
}
else { // Save new read line with encryption characters
keyData.push_back(line);
}
}
}
// Next sanity check. Check, if we have read enough lines
if (keyData.size() != NumberOfUsedPrintableCharacters) {
std::cerr << "\n\n*** Error: Not enough data in key file\n";
thereWasAnError = true;
}
}
else { // In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: File with key data ('" << fileNameKey << "') could not be opened\n";
thereWasAnError = true;
}
// In case of any error, reset the key data
if (thereWasAnError) keyData.clear();
// Return the array with printable characters to the caller using Return Value Optimization / Copy Elision
return keyData;
}
// ----------------------------------------------------------------------------------------------------------------------------------
KeyData generateNewKeyFile(const std::string& printableCharacters) {
// Here we will store all key data
KeyData keyData{};
// Sanity Check. Do we have the expected number of printable characters
if (NumberOfUsedPrintableCharacters == printableCharacters.size()) {
// Open already now the output file, and check, if it could be opened.
if (std::ofstream keyDataFileStream{ fileNameKey }; keyDataFileStream) {
// Initialize random number generation. Lambda for generating randim values
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<size_t> distribution(0U, NumberOfUsedPrintableCharacters - 1);
auto randomCharacter = [&]() { return printableCharacters[distribution(gen)]; };
// We want to create a key string for all printable characters
for (size_t i{}; i < NumberOfUsedPrintableCharacters && keyDataFileStream; ++i) {
// Temporary for string with random chars
std::string key(NumberOfEncryptionCharacters, '\0');
// Genrate string with random content
std::generate_n(key.begin(), NumberOfEncryptionCharacters, randomCharacter);
// Write data to file and check for possible error
if (!(keyDataFileStream << key << "\n")) {
std::cerr << "\n\n*** Error: Could not write line to key file\n";
keyData = {}; break;
}
else {
// Store the next line in the file in in the internal vector
keyData.push_back(std::move(key));
}
}
if (keyData.size() != NumberOfUsedPrintableCharacters) std::cerr << "\n*** Geeneral error during creation of key file\n";
}
else {
// In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: File with key data ('" << fileNameKey << "') could not be opened for writing\n";
}
}
return keyData;
}
// ----------------------------------------------------------------------------------------------------------------------------------
std::string encrypt(const std::string& toBeEncrypted, const KeyData& keyData, const std::string& printableCharacters, const std::string& outputFileName) {
// Here we will store the encrypted result
std::string encryptedString{};
// First open output file, if this does not work, no need to do something else
if (std::ofstream outFileStream{ outputFileName }; outFileStream) {
// Then make some sanity checks
if (((keyData.size() != NumberOfUsedPrintableCharacters)) || (printableCharacters.size() != NumberOfUsedPrintableCharacters)) {
std::cerr << "\n\n*** Error: Preconditions for encryption not met\n";
}
else {
// Go through all data in source string that shall be encrypter
for (const char c : toBeEncrypted) {
// Search for the character in our printable character string and check, if it is available and if it is in the key data
if (size_t index{ printableCharacters.find(c) }; (index != std::string::npos) && (index < NumberOfUsedPrintableCharacters)) {
// Prepare output
encryptedString += keyData[index] + "\n";
outFileStream << keyData[index] << "\n";
}
else {
std::cerr << "\n\n*** Error: Invalid character '" << c << "' in input string\n";
}
}
}
}
else {
// In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: output file ('" << outputFileName << "') for encrypted data could not be opened\n";
}
return encryptedString;
}
// ----------------------------------------------------------------------------------------------------------------------------------
std::string decrypt(const KeyData& keyData, const std::string& printableCharacters, const std::string& inputFileName) {
// Here we will store the decrypted result
std::string decryptedString{};
// First open input file, if this does not work, no need to do something else
if (std::ifstream inFileStream{ inputFileName }; inFileStream) {
// Then make some sanity checks
if (((keyData.size() != NumberOfUsedPrintableCharacters)) || (printableCharacters.size() != NumberOfUsedPrintableCharacters)) {
std::cerr << "\n\n*** Error: Preconditions for encryption not met\n";
}
else {
// Read all lines of the file
for (std::string line{}; std::getline(inFileStream, line); ) {
// Search this line in the keydata
if (KeyData::const_iterator searchResult = std::find(keyData.begin(), keyData.end(), line); searchResult != keyData.cend()) {
// Calculate the distance
if (const int index{ std::distance(keyData.begin() , searchResult) }; index < printableCharacters.size()) {
decryptedString += printableCharacters[index];
}
else { // With all the checks, this error should not occur
std::cerr << "\n\n*** Unexpected error while decrypting data\n";
}
}
else {
std::cerr << "\n\n*** Error: Could not decrypt data\n";
}
}
}
}
else {
// In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: input file ('" << inputFileName << "') with encrypted data could not be opened\n";
}
// Last sanity check
if (decryptedString.empty()) std::cerr << "\nGeneral Error during decryption\n";
return decryptedString;
}
int main() {
// First read the printable character string. Check, if that was successfull
if (std::string printableCharacters{ getPrintableCharacters() }; printableCharacters.length() == NumberOfUsedPrintableCharacters ||
(printableCharacters = generateNewPrintableCharactersFile()).size() == NumberOfUsedPrintableCharacters) {
// Next, try to read a KeyData file
if (KeyData keyData{ getKeyData() }; keyData.size() == NumberOfUsedPrintableCharacters ||
(keyData = generateNewKeyFile(printableCharacters)).size() == NumberOfUsedPrintableCharacters) {
// main programm loop
for (bool doRunMainLoop{ true }; doRunMainLoop; ) {
// Show menu to the user
std::cout << "\n\n\nPlease select, what to do. Press\n\n\t1\tfor encryption\n\t2\tfor decryption\n\t3\tto generate new key file\n\t4\tto exit\n\nSelection: ";
// Get input from user and check, if that worked
if (unsigned int selection{}; std::cin >> selection) {
switch (selection) {
case 1U:
// Encryption
std::cout << "\nEncryption\n\nEnter first a filename and then the word that you want to encrypt:\n";
if (std::string filename{}, word{}; std::cin >> filename >> word) {
std::cout << "\nEncrypted Data:\n\n" << encrypt(word, keyData, printableCharacters, filename) << "\n";
}
else std::cout << "\n\nError while reading your data. Please try again\n";
break;
case 2U:
// Decryption
std::cout << "\nDecryption\n\nEnter first a filename with the ecrypted information:\n";
if (std::string filename{}; std::cin >> filename) {
std::cout << "\nDecrypted Data:\n\n" << decrypt(keyData, printableCharacters, filename) << "\n";
}
else std::cout << "\n\nError while reading your data. Please try again\n";
break;
case 3U:
// Generate new keyfile
if ((keyData = generateNewKeyFile(printableCharacters)).size() != NumberOfUsedPrintableCharacters) {
std::cerr << "\n\n*** Error: New keyfile could not be generated. Exit\n";
doRunMainLoop = false;
}
break;
case 4U:
doRunMainLoop = false;
break;
default:
std::cout << "\nInvalid selection. Pleas try again\n";
} // End select
}
else std::cerr << "\n\n\nError: Unexpected error while reading from console\n";
} // End for
}
else std::cerr << "\n\n*** Error: Problem with key data generation\n";
}
else std::cerr << "\n\n*** Error: No printable character data present\n";
return 0;
}
关于c++ - 在 vector 中查找字符串的索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61167570/