如果字符串包含集合中的任何字符,如何擦除字符串 [c++]



我是C++新手,无法在任何帖子中找到解决方案。

我有一个strings的vector,如果它包含以下任何符号,我希望从此vector中删除string{'.', '%', '&','(',')', '!', '-', '{', '}'}.

我知道find(),它只需要一个字符来搜索; 但是,我想遍历字符串向量中的每个单词,如果它们包含这些字符中的任何一个,则删除它们。 例如find('.')是不够的。

我已经尝试了多种途径,例如创建所有这些字符的char向量,并将每个向量作为find()参数循环。但是,这种逻辑是非常有缺陷的,因为如果向量中只有一行带有".",或者留下一些带有不需要的字符的字符串,它会导致中止陷阱。

vector<std::string> lines = {"hello..","Hello...", "hi%", "world","World!"}
vector<char> c = {'.', '%', '&','(',')', '!', '-', '{', '}'};
for (int i=0; i < lines.size(); i++){
for(int j=0; j < c.size(); j++){
if (lines.at(i).find(c.at(j)) != string::npos ){
lines.erase(lines.begin() + i);
}
}
}

我还尝试在向量"行"循环中find_first_of(),它产生的结果与上面的代码相同。

if (lines.at(i).find_first_of(".%&()!-{}") != string::npos ){
lines.erase(lines.begin() + i);

有人可以帮我这个逻辑吗?

编辑:

当我在擦除行后放入--i时,没有显示任何内容,并且我有一个中止陷阱,因为它在矢量范围之外循环。

代码中有两个问题,当找到匹配项时,它们都"在"内部"循环for循环中。

首先,你继续检查同一个向量元素是否有(进一步的)匹配,即使在你擦除它之后;要解决这个问题,请在if块中添加一个break;语句,以防止在找到匹配项并进行erase()调用后进一步运行该内部循环。

其次,当您擦除元素时,您需要递减i索引(该索引将在下一个外部循环开始之前递增),这样您就不会跳过对i在擦除将编制索引的元素的检查。

下面是代码的固定版本:

#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> lines = { "hello..", "Hello...", "hi%", "world", "World!" };
std::vector<char> c = { '.', '%', '&','(',')', '!', '-', '{', '}' };
for (size_t i = 0; i < lines.size(); i++) {
for (size_t j = 0; j < c.size(); j++) {
if (lines.at(i).find(c.at(j)) != std::string::npos) {
lines.erase(lines.begin() + static_cast<ptrdiff_t>(i));
i--; // Decrement the i index to avoid skipping next string
break; // Need to break out of inner loop as "i" is now wrong!
}
}
}
for (auto l : lines) {
std::cout << l << std::endl;
}
return 0;
}

但是,正如其他答案中所指出的,您可以通过更多地使用标准库提供的功能来显着改进代码。

你有一个错误,在向量缩小后增加向量索引 - 在你删除 i:th 元素后,曾经位于 i+1 的元素现在位于 i,所以你跳过它.
如果你删除了最后一个元素,你就走出了向量。

您可以通过提高抽象级别并更多地使用algorithm来避免此类问题。

像这样:

const std::set<char> symbols = {'.', '%', '&','(',')', '!', '-', '{', '}'};
bool invalid(const std::string& s)
{
return std::find_if(s.begin(),
s.end(),
// From C++ 20, use 'contains' instead of 'count'.
[](char c) { return symbols.count(c) != 0; })
!= s.end();
}
int main()
{
std::vector<std::string> data = {"abc", "abc.", "def", "d&ef", "!ghi", "ghi"};
auto end = std::remove_if(data.begin(), data.end(), invalid);
data.erase(end, data.end());
for (const auto& s: data)
{
std::cout << s << std::endl;
}
}

您可以使用std::remove_if来解决此问题。

vector<std::string> lines = {"hello..","Hello...", "hi%", "world","World!"};
vector<char> ign_c = {'.', '%', '&','(',')', '!', '-', '{', '}'};
std::transform(lines.begin(), lines.end(), lines.begin(), [&](std::string str){
str.erase(std::remove_if(str.begin(), str.end(), 
[&](char c){
return std::find(ign_c.begin(), ign_c.end(), c) != ign_c.end();
}), 
str.end());
return str;
});

你查看字符串的逻辑不是最优的,但仍然正确,但是你改变了你正在迭代的容器(lines),而不改变迭代器特征。相反,您应该将迭代器转换为 for 循环参数,并在每个erase上相应地更新它。此外,如果您已经擦除了元素,则无需保留内部迭代:

std::vector<std::string> lines { "hello..","Hello...", "hi%", "world","World!" };
constexpr std::array chars { '.', '%', '&','(',')', '!', '-', '{', '}' };
for (auto it = lines.cbegin(); it != lines.cend();) {
auto str = *it;
auto found = false;
for(auto &ch : chars) {
if (str.find(ch) != std::string::npos) {
// If element is found erase it and update the iterator
it = lines.erase(it);
found = true;
// breaks inner loop
break;
}
}
// If nothing was erased, increment the iterator to point to the next element
if (!found) {
++it;
}
}

如果使用正确的抽象,则可以获得可读性如下的代码:

auto result = lines | remove_if([](std::string s){
return any_of(c, is_contained_in(s));
});

这离删除字符串不远了s如果c中的任何项目包含在s中,这就是您想要做的。

为了使该东西无需自己编写任何特殊代码即可工作,您需要几个库。

下面是完整的示例:

#include <boost/hana/functional/curry.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <range/v3/view/remove_if.hpp>
#include <range/v3/algorithm/contains.hpp>
#include <range/v3/algorithm/any_of.hpp>
std::vector<std::string> lines = {"hello..","Hello...", "hi%", "world","World!"};
std::vector<char> c = {'.', '%', '&','(',')', '!', '-', '{', '}'};
using namespace ranges;
using namespace ranges::views;
using namespace boost::hana;
auto is_contained_in = curry<2>(ranges::contains);
int main() {
auto result = lines | remove_if([](std::string s){
return any_of(c, is_contained_in(s));
});
for (auto i : result) {
std::cout << i << std::endl;
}
}

请注意,此代码中的任何位置都没有低级逻辑。我还没有写过一个函数。我刚刚将现有库中经过良好测试的函数插入在一起。

最新更新