从字符串向量中删除空元素

16
我正在尝试编写一个处理INI文件的小程序,以便在后续项目中使用。首先要将其加载到内存中并缩小其大小。 因此,其中vLine是包含文件内容的向量。
for (unsigned int i = 0; i < vLine.size(); i++)
{
   if (!vLine[i].find(';', 0))
   {
       vLine[i].erase();
   }
}

打印vLine后,我将得到一些空格,这些空格曾经是以分号开头的行所在的位置,例如:

1.    
2. property
3. property
4. 
5. property

使用resize()似乎会从列表中删除最后一个元素,而不是删除这些空的部分。当我使用erase()删除只包含空格的行时,同样存在这个问题。

是否可能在保留vLine顺序的同时删除这些空元素?

(很抱歉我没有使用迭代器。)


回复:(很抱歉这里没有使用迭代器)- 为什么不使用迭代器呢? - In silico
缺乏对它们正确使用的知识。似乎可以在没有它们的情况下完成这个任务。 - JGrey
1
你可以从向量中删除行,而不仅仅是擦除字符串的内容(这就是 vLine[i].erase() 所做的,对吧?调用 vLine.erase() 因为它从 vector 中擦除。然后重新写出文件。一个惯用的方法是使用 C++ 的 erase-remove 惯用法,尽管你需要使用 <algorithm> 中的 std::remove_if 来使用条件。 - wkl
4个回答

16

这个:

vLine[i].erase(); 

不会从向量中删除vLine[i]。表达式vLine[i]返回索引 i 处元素的引用。因此,假设vLine的类型为std :: vector&lt; std :: string&gt;,函数调用erase()实际上在元素上调用string :: erase(),而不是vector :: erase()在向量上调用。你所做的就是使这个特定的元素变成空白。

你可能想要的是这样:

vLine.erase(vLine.begin() + i);

这实际上从向量中移除了元素。这将使所有当前向量的迭代器无效,并且索引也将不再正确。在这种情况下,您确实需要使用迭代器。

std::vector<std::string>::iterator i = vLine.begin();
while(i != vLine.end())
{
    if(i->find(';', 0) != std::string::npos)
    {
        i = vLine.erase(i);
    }
    else
    {
        ++i;
    }
}

但是,有一种更简单的方法可以做到这一点:使用标准算法 std::remove_if() 和一个函数对象,然后调用vLine.erase()

struct HasSemicolon
{
    bool operator()(const std::string& s)
    {
        return s.find(';', 0) != std::string::npos;
    }
};

// ...

vLine.erase(std::remove_if(vLine.begin(), vLine.end(), HasSemicolon()), vLine.end());

如果您使用的是C++11编译器,那么您也可以使用lambda表达式来使代码更加简洁。

非常感谢,In Silico。我会抽出时间学习迭代器,并相应地修改我的应用程序。 - JGrey
1
你能否添加一个使用C++11 lambda表达式的例子?我已经试了大约一个小时,但是我很不擅长阅读C++文档。 - anon
@QPaysTaxes http://en.cppreference.com/w/cpp/algorithm/remove 有一个使用 lambda 表达式的示例。 - arekolek

11

你的问题在于删除元素的逻辑。当你遍历到想要删除的下标i时,你清除了它的值,但却没有将它从向量中移除。

实现你想要做的操作的一种标准简单方法是使用std::remove_if

vLine.erase(
    std::remove_if(
        vLine.begin(),
        vLine.end(),
        [](std::string const& s) { return s.size() != 0 && s.front() == ';'; }),
    vLine.end());

8

使用C++11中的erase/remove-idiom,最好使用lambda表达式:

foo.erase(std::remove_if(foo.begin(), foo.end(), 
                         [](const std::string& s) 
                         { return s.find(';', 0); }));

我认为 { return s.find(';', 0); })); 需要改成 { return s.find(';', 0); }), foo.end());,否则空间似乎没有从 foo 中释放。至少这是我得到的结果。@Mankarse 的答案(基本相同)似乎做到了这一点!:) - code_fodder

2

使用 C++20,你可以使用 std::erase,这相当于 erase-remove 惯用语。

std::erase_if(vLine, [](auto&& str){
    return str.find(';', 0) != std::string::npos;
})

今天这个答案对我来说是完全的胜利。 - Chris Reid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接