C++ - 使用基于范围的 for 循环将字符值分配给向量中的字符串不会分配值

C++ - Assigning character values to a string in a vector using range based for loop doesn't assign the value

我正在做一个自我练习练习。参数是我允许用户输入存储在向量中的名称。打印向量中的名称列表可为您提供每个名称的位置。您可以选择通过提供名称的位置来加密列表中的名称。加密会将名称中的每个字母与另一个字符串进行比较,该字符串是允许的名称字母表。当它在字母表中找到该字母时,它会从另一串随机字符中提取一个对应的字符,并将新字符分配到相同的位置。

使用基于范围的 for 循环我几乎让它工作了。通过添加输出语句,我可以看到代码正确地将名称字符与允许的字母进行比较,并在加密密钥中找到相应的值。然而,当循环完成并且我再次打印名称列表时,要加密的名称中的字符没有改变。

为了解决这个问题,我注释掉了基于范围的 for 循环,并尝试用传统的 for 循环做同样的事情。使用此代码,我在加密过程中遇到错误:

Position 1 A is the same as @ terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 26) >= this->size() (which is 2)

"Position 1 A is the same as @" 行是我添加的调试输出,用于显示代码能够找到正确的字符串、字符串中的字母以及他们键中的相应字母。

如果能帮助我理解为什么会出现这些错误,我们将不胜感激。

这是我的代码:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    // Declare strings for Encryption and Decryption
    string alphabet {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "};
    string key {"mnbvfghytcqwi1234567890`~!@#$%^&*()-=_+[]\{}|;':,./<>?"};

    //Declare collection of names for the list
    vector <string> names {};

    //Declare character to hold the user menu selection
    char selection {};
    string user_input{};
    string banner (50, '=');
    //Print menu
    do
    {
        cout << "\n" << banner << endl;
        cout << "A - Add name to list" << endl;
        cout << "P - Print all names in list" << endl;
        cout << "E - Encrypt a name in the list" << endl;
        cout << "D - Decrypt a name in the list" << endl;
        cout << "S - Show details of a name in the list" << endl;
        cout << "C - Clear all names in the list" << endl;
        cout << "Q - Quit" << endl;
        cout << banner << endl;
        cout << "Selection: ";
        getline(cin, user_input);
        if (user_input.size() != 1)
        {
            cout << "Error 4: Menu selection must be a single character" << endl;
            selection = '1';
        }
        else
        {
            for (auto c: user_input)
            {
                if (!isalpha(c))
                {
                    cout << "Error 5: Menu selection must be an alphabetic character" << endl;
                    selection = '1';
                }
                else
                    selection = c;

            }
        }
        // cin >> selection;
        // cin.clear();
        // cin.sync();

        switch (selection)
        {
            case 'a':
            case 'A':
            {
               string temp_name{};
               bool invalid_name {false};
               cout << "Enter full name: ";
               getline(cin, temp_name);
               if (!isalpha(temp_name[0]))
                   cout << "Error 2: Names must begin with an alphabetic character" << endl << endl;
                else 
                {
                    for (auto c: temp_name)
                    {
                        if (!isalpha(c) && !isspace(c) && c != '-')
                        {
                            invalid_name = true;
                            break;
                        }   
                        else
                            invalid_name = false;
                    }
                    if (invalid_name)
                        cout << "Error 3: Name contains invalid characters" << endl << endl;
                    else
                    {
                        temp_name.at(0) = toupper (temp_name.at(0));
                        for (size_t i {1}; i < temp_name.size(); i++)
                        {
                            size_t position{i-1};
                            if (isspace(temp_name.at(position)) || temp_name.at(position) == '-')
                            {
                                temp_name.at(i) = toupper(temp_name.at(i));
                            }
                        }
                        names.push_back(temp_name);
                        cout << "Added name #" << names.size() << endl;
                    }
                }
               break;
            }
            case 'p':
            case 'P':
            {
                for (size_t i {0}; i < names.size(); i++)
                    cout << i+1 << ". " << names.at(i) << endl;
                break;
            }
            case 'e':
            case 'E':
            {
                size_t encrypt_input{}, key_position{}, name_position {}, name_size {};
                cout << "Enter the position of the name to encrypt: ";
                cin >> encrypt_input;
                cin.clear();
                cin.sync();
                if (encrypt_input < 1 || encrypt_input > names.size())
                    cout << "Error 6: Invalid selection for name to encrypt" << endl << endl;
                else
                {
                    name_position = encrypt_input - 1;
                    name_size = names.at(name_position).size();
                    cout << "Encrypting name: " << names.at(name_position) << " of size " << name_size << endl << endl;
                    cout << "Position 1 " << names.at(name_position).at(0) << " is the same as ";
                    key_position = alphabet.find(names.at(name_position).at(0));
                    cout << key.at(key_position) << endl;
                    for (size_t i {0}; i < name_size; i++)
                    {
                        key_position = alphabet.find(names.at(name_position).at(i));
                        cout << "Finding " << names.at(key_position).at(i) << " in key at position " << key_position << endl;
                        cout << "Found encryption value of " << key.at(key_position) << " at position " << key_position << endl;
                        cout << "Changing " << names.at(key_position).at(i) << " to " << key.at(key_position) << endl;
                        names.at(name_position).at(i) = key.at(key_position);
                    }
                    /*
                    for (auto c: names.at(encrypt_input-1))
                    {
                        cout << "Converting " << c << " to ";
                        key_position = alphabet.find(c);
                        cout << key.at(key_position) << endl;
                        c = key.at(key_position);
                        cout << "C is now " << c << endl << endl;
                    } 
                     */
                }
                cout << names.at(encrypt_input-1) << endl;
                break;
            }
            case 'q':
            case 'Q':
                cout << "Goodbye" << endl << endl;
                break;
            default:
                cout << "Error 1: Invalid menu selection" << endl << endl;
                break;
        }
    } while (selection != 'Q' && selection != 'q');

    return 0;
}

欢迎使用 Whosebug!我完全同意 PaulMcKenzie 的观点,这么大的函数并不是最好的,原因有很多——直接的原因是它很难阅读,也很难发现问题——但还有更多的原因。

话虽如此,我在 E 案例中发现了一个错误。

                for (size_t i {0}; i < name_size; i++)
                {
                    key_position = alphabet.find(names.at(name_position).at(i));
                    cout << "Finding " << names.at(key_position).at(i) << " in key at position " << key_position << endl;
                    cout << "Found encryption value of " << key.at(key_position) << " at position " << key_position << endl;
                    cout << "Changing " << names.at(key_position).at(i) << " to " << key.at(key_position) << endl;
                    names.at(name_position).at(i) = key.at(key_position);
                }

应该是

        for (unsigned int i{ 0 }; i < name_size; i++)
        {
            key_position = alphabet.find(names.at(name_position).at(i));
            cout << "Finding " << names.at(name_position).at(i) << " in key at position " << key_position << endl;
            cout << "Found encryption value of " << key.at(key_position) << " at position " << key_position << endl;
            cout << "Changing " << names.at(name_position).at(i) << " to " << key.at(key_position) << endl;
            names.at(name_position).at(i) = key.at(key_position);
        }

key_position 在 2 个地方应该是 name_position

可能还有其他错误,但这应该可以阻止崩溃并正确编码。

编辑:应 OP 的要求添加了新的代码片段。

int i = 0;  // position counter
for (auto c: names.at(encrypt_input-1))
{
    cout << "Converting " << c << " to ";
    key_position = alphabet.find(c);
    cout << key.at(key_position) << endl;
    c = key.at(key_position);
    cout << "C is now " << c << endl << endl;
    names.at(name_position).at(i++) = c;  // update the names variable.
}

这应该可以解决您提到的自动循环问题。

您正在访问 names 向量的无效位置,错误/异常显示了这一点。

当你这样做时:

names.at( key_position ).at( i )
//        ^^^
//        It should be name_position

在这个声明中,

cout << "Finding " << names.at( key_position ).at( i ) << " in key at position " << key_position << endl;

您正在访问 names 的无效索引,而它应该是:

names.at( name_position ).at( i )

而且,这会起作用,因为它访问了一个有效的索引。

你在这句话中也犯了同样的错误:

cout << "Changing " << names.at( key_position ).at( i ) << " to " << key.at( key_position ) << endl;

更正这些,它应该可以工作!


提示:

是时候阅读 How to debug small programs
它将帮助您以更系统的方式找出您的程序出了什么问题。


关于一般代码组织的几点:

  • 您应该将程序划分为多个函数,而不是使 main 函数混乱。
  • 您可以在 switch 语句中编写对应于每个 case 的函数,例如addName()encryptName()decryptName()
  • 这种模块化一定会帮助您和其他人轻松高效地阅读、调试、维护和扩展您的代码。在您的情况下,它还可以帮助您立即编写 Minimal, Complete, and Verifiable example

希望对您有所帮助!
祝你好运!
编码愉快!