gpt4 book ai didi

c++ - 在 vector 中查找字符串的索引

转载 作者:行者123 更新时间:2023-12-01 14:08:13 24 4
gpt4 key购买 nike

我正在移植我的“加密器/解密器”程序,该程序使用 key ( like this one ) 来触发文本。加密部分已完成,现在我正在研究解密器:

  • 在数组
  • 中查找 128 个字符的字符串
  • 获取该字符串的索引
  • 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”。

    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);
    }

    最佳答案

    很有意思。你的程序随着时间的推移而发展,你发现了一些问题并逐步修补它们。有时通过两次犯同样的错误来消除错误。

    尽管如此,仍有一些必须修复的严重错误。这些大多是越界错误。然后是样式错误,例如全局变量和错误数据类型的使用。
    所以,软件设计的一个大问题,必须重构

    最后但并非最不重要的一点是,您如何加密的想法是行不通的,因为您将第一个字母和 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”属性,而是通过添加一个空格作为最后一个字符来修补它。您可以使用函数 getgetline来克服这个问题。

    现在回答你的问题。在解密你使用
    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/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com