std::remove/_if() 实际上并没有删除任何东西,它只是将匹配的项目移动到容器的末尾,然后将一个迭代器返回到该项目范围的开头。然后调用者可以使用该迭代器从容器中实际删除项目。
您将该迭代器传递给std::string::erase() 的重载,该重载将1 个迭代器作为输入。因此,它最多只会擦除 1 个字符(如果 std::remove_if() 没有找到任何内容,它将返回字符串的 end() 迭代器,并且在该迭代器上调用 erase() 是未定义的行为)。如果超过 1 个字符被“删除”到字符串的末尾,则剩余的未擦除字符将被留下。这就是您在输出中看到的内容。
要清除所有“已删除”的字符,您需要使用 std::string::erase() 的重载,它需要 2 个表示范围的迭代器,例如:
std::string step1(std::string plaintext)
{
plaintext.erase(
std::remove_if(plaintext.begin(), plaintext.end(),
std::not1(std::ptr_fun(std::isalpha))
),
plaintext.end()
);
plaintext.erase(
std::remove_if(plaintext.begin(), plaintext.end(),
std::ptr_fun(std::ispunct)
),
plaintext.end()
);
return plaintext;
}
请注意,使用 std:::isalpha() 删除所有非字母字符将包括所有空格和标点字符,因此您使用 std::ispunct() 进行的第二次搜索将找不到任何要删除的内容,因此您可以完全放弃该搜索,例如:
std::string step1(std::string plaintext)
{
plaintext.erase(
std::remove_if(plaintext.begin(), plaintext.end(),
std::not1(std::ptr_fun(std::isalpha))
),
plaintext.end()
);
return plaintext;
}
话虽如此,如 cmets 中所述,std::not1() 和 std::ptr_fun 在现代 C++ 中已被弃用。在 C++11 及更高版本中,您可以使用 lambda:
std::string step1(std::string plaintext)
{
plaintext.erase(
std::remove_if(plaintext.begin(), plaintext.end(),
[](unsigned char ch){ return !std::isalpha(ch); }
),
plaintext.end()
);
return plaintext;
}
在 C++20 及更高版本中,您可以使用std::erase_if():
std::string step1(std::string plaintext)
{
std::erase_if(plaintext,
[](unsigned char ch){ return !std::isalpha(ch); }
);
return plaintext;
}