Нахождение индекса строки в векторе - PullRequest
4 голосов
/ 12 апреля 2020

Я в процессе портирования моей программы "encryptor / decryptor", которая использует ключ ( как этот ) для триггера текста. Часть шифрования завершена, теперь я работаю над расшифровщиком, который:

  1. Ищет строку из 128 символов в массиве
  2. Получает индекс этой строки
  3. Получает символ из chars[index_of_long_string]
  4. Объединяет этот символ в строку pass


При некотором поиске я смог найти, что мне будет лучше отключить использование векторов в C ++. Я написал следующий код для формирования вектора из моего массива:

vector<string> arrayAllReplaceDecryptVector(std::begin(arrayAllReplaceDecrypt), end(arrayAllReplaceDecrypt));

Затем используйте этот код для; получить индекс key_string, объединить строку с chars[index], затем удалить первые 128 символов key_string:

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».

enter image description here

Этот код будет воспроизводить ошибку (однако учтите, что для запуска программы вам понадобятся эти два файла .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);
}

1 Ответ

0 голосов
/ 13 апреля 2020

Очень интересно. Со временем ваша программа развивалась, вы обнаружили некоторые проблемы и пошагово исправили их. Иногда устраняя ошибки, делая одну и ту же ошибку дважды.

Тем не менее, есть некоторые критические ошибки, которые необходимо исправить. Это в основном несвязанные ошибки. Затем возникают ошибки стиля, такие как глобальные переменные и использование неправильных типов данных. Итак, большая проблема с дизайном программного обеспечения, которая должна быть подвергнута рефакторингу

И, наконец, что не менее важно, ваша идея о шифровании не будет работать, потому что вы храните в Key.txt «всегда первую букву вместе с ключом. В

sb = sb + (chars[(sb.length()) / 128]);

Вы всегда добавляете символ очистки, за которым следуют 128 байтов ключа. Таким образом, безопасность вашего подхода равна 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. Вы можете проверить это. В первом создании файла ключа вы увидите много символов 0. Во втором случае, когда запуски будут равны единице, вы создадите символы \ 001.

Лучше всего использовать современные контейнеры C ++, которые помогут вам в таких проверках.

Далее, люди, которые читают вас сообщение не может понять, почему вы делаете определенные вещи. Итак, нам было интересно, почему вы пишете substr(1,128), а затем content = content.substr(130, content.size());. После этого вы удалили первый чистый символ, а затем прочитали 128 байтов. И затем вы пропускаете "\ n", который вы добавили в ваш ключевой файл. Все это должно быть переработано.

Еще одна проблема. Для чтения вашего "chars.txt". Вы узнали, что не читали пробел '' в вашем l oop. Но вы не проверяли атрибут «skipws» IO-потока, а исправили его, добавив пробел в качестве последнего символа. Вы могли бы использовать функцию get или getline, чтобы решить эту проблему.

Теперь к вашему вопросу. В расшифровке вы использовали

for (int i = 0; i < 94; i++)

Итак, вы читаете все, кроме последней строки. Конец, то вы делаете

pass = pass.substr(129, pass.length());

129 неправильно. Это должно быть 128. Вы уже удалили первый чистый символ и "\ n". Затем он работает, я тестировал его в отладчике.

Однако все это не будет работать, потому что вы создаете огромный зашифрованный вывод для небольших входных строк. Таким образом, для 3 символов «ab c» вы создадите 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...