加密字符串但接收无限循环

Encrypting a string but receiving an infinite loop

本文关键字:无限循环 字符串 加密      更新时间:2023-10-16

问题:

我试图使用单个规则加密std::string密码:

  • 元音前后添加"0">

这样bAnanASplit就变得b0A0n0a0n0A0Spl0i0t. 然而,我陷入了一个无限循环。

这是代码:

const std::string VOWELS = "AEIOUaeiou";
std::string pass = "bAnanASplit";
//Add zeroes before and after vowels
for (int i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
std::cout << pass << "n";
if(i != std::string::npos)
{
std::cout << pass[i] << ": " << i << "n";
pass.insert(pass.begin() + i++, '0');
pass.insert(pass.begin() + ++i, '0');
}
}

。结果是:

bAnanASplit
A: 1
b0A0nanASplit
a: 5
b0A0n0a0nASplit
A: 9
b0A0n0a0n0A0Split
i: 15
b0A0n0a0n0A0Spl0i0t
b0A0n0a0n0A0Spl0i0t
A: 2
b00A00n0a0n0A0Spl0i0t
a: 8
b00A00n00a00n0A0Spl0i0t
A: 14
b00A00n00a00n00A00Spl0i0t
i: 22
b00A00n00a00n00A00Spl00i00t
b00A00n00a00n00A00Spl00i00t
...

有什么帮助吗?这确实看起来很奇怪。

编辑:所有的答案都是有用的,因此我接受了我认为最能回答这个问题的答案。但是,此答案显示了解决问题的最佳方法。

永远不要修改您正在迭代的集合/容器!

这样为您省去了很多麻烦。

让我们从您的代码开始,并生成一个新字符串,其元音被0包围。

const std::string VOWELS = "AEIOUaeiou";
std::string pass = "bAnanASplit", replacement;
//Add zeroes before and after vowels
for (auto ch : pass)
{
if(VOWELS.find(ch) != std::string::npos)
replacement += '0' + ch + '0';
else
replacement += ch;
}

你有它!

由于 OP 似乎在寻找不当行为的确切原因,我想添加另一个答案,因为现有答案没有显示确切的问题。

意外行为的原因在以下行中可见。

for (int i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
...

问题1:

循环计数器i是一个int(即signed int(。但是 std::string::find_first_of 如果没有匹配项,则返回 std::string::npos。这通常是unsigned long可表示的最大数量。将一个巨大的unsigned值分配给较短的signed变量将存储一个完全出乎意料的值(假设您不知道这一点(。在这种情况下,i意志在大多数平台中变得-1(如果您需要确定,请尝试int k = std::string::npos;并打印k(。i = -1是循环条件i < pass.length()的有效状态,因此将允许下一次迭代。

问题2:

与上述问题密切相关的是,使用相同的变量i来定义find操作的起始位置。但是,如前所述,i不会像您期望的那样代表字符的索引。

溶液:

存储格式错误的值可以通过使用正确的数据类型来解决。在当前情况下,最好的选择是使用std::string::size_type因为这始终保证有效(很可能这等于size_t任何地方(。为了使程序使用给定的逻辑,您还必须使用不同的变量来存储find结果。

但是,更好的解决方案是使用std::stringstream来构建字符串。这将比通过在中间插入字符来修改字符串的性能更好。

例如

#include <iostream>
#include <sstream>
int main() {
using namespace std;
const string VOWELS = "AEIOUaeiou";
const string pass = "bAnanASplit";
stringstream ss;
for (const char pas : pass) {
if (VOWELS.find(pas) == std::string::npos) {
ss << pas;
} else {
ss << '0' << pas << '0';
}
}
cout << pass << "n";
cout << ss.str() << endl;
}

如果循环变得std::string::nposi您不会退出循环。因此,当i值到达 i 之后的最后一个 i 或 0 的位置时,它会被更改为一些意外的值(可能类似于 -1((这里我指的是 spl i t 的i(。这是因为i是一个有符号整数,但在这种情况下,find_first_of()返回std::string::npos这是size_t可以持有的最大值。在这种情况下,终止条件i < pass.length()可能成立,循环继续。因此,我建议对您的代码进行以下更改 -

for (size_t i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
if(i == std::string::npos)
break;
pass.insert(pass.begin() + i++, '0');
pass.insert(pass.begin() + ++i, '0');
}

同样,if (i != std::String::npos)不会做你期望它做的事情。

但话又说回来,最好不要在迭代容器时修改容器,@Tanveer在他的回答中提到过