Removing Empty Elements from a Vector of Strings
I'm attempting to make a small program that processes INI files, for use in a later project, first by reducing its size once loaded into memory. Thus,
where vLine is a vector containing the file contents
for (unsigned int i = 0; i < vLine.size(); i++)
{
if (!vLine[i].find(';', 0))
{
vLine[开发者_JS百科i].erase();
}
}
Upon printing vLine, I'll be left with spaces where once a line beginning with a semi-colon existed, such as
1.
2. property
3. property
4.
5. property
Using resize() appears to remove the last element from the list rather than remove these empty portions. The same problem exists where I remove lines that only contain whitespace with erase().
Is it possible to remove these empty elements while preserving the order of vLine?
(Apologies for not using iterators in this.)
This:
vLine[i].erase();
does not erase vLine[i]
from the vector. The expression vLine[i]
returns a reference to the element at index i
. So assuming that vLine
is of type std::vector<std::string>
, the function call erase()
actually calls string::erase()
on the element, not vector::erase()
on the vector. All you're doing is making that particular element blank.
What you probably want is something like this:
vLine.erase(vLine.begin() + i);
This actually removes the element from the vector. Now, this does invalidate all current iterators to the vector, and the indices won't be right anymore. This is a situation where you really need to use iterators.
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;
}
}
But there's an even easier way to do this: use the standard algorithm std::remove_if()
with a functor then call 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());
If you can use a C++11 compiler, then you can also use lambda expressions to be even more concise.
The problem is in your logic for removing the elements. When you come across an element at index i
that you want to erase, you clear its value, but you do not remove it from the vector.
The standard and simple way to do what you want to do is std::remove_if
:
vLine.erase(
std::remove_if(
vLine.begin(),
vLine.end(),
[](std::string const& s) { return s.size() != 0 && s.front() == ';'; }),
vLine.end());
Use the erase/remove-idiom, preferably with a lambda from C++11:
foo.erase(std::remove_if(foo.begin(), foo.end(),
[](const std::string& s)
{ return s.find(';', 0); }));
With C++20 you can use std::erase which is equivalent to the erase-remove idiom.
std::erase_if(vLine, [](auto&& str){
return str.find(';', 0) != std::string::npos;
})
精彩评论