修剪字符串向量

Posted

技术标签:

【中文标题】修剪字符串向量【英文标题】:Trimming a vector of strings 【发布时间】:2011-01-09 22:43:36 【问题描述】:

我有一个 std::vectorstd::strings 包含与此类似的数据:

[0] = ""
[1] = "Abc"
[2] = "Def"
[3] = ""
[4] = "Ghi"
[5] = ""
[6] = ""

如何获得包含从 1 到 4 的 4 个字符串的向量? (即我想从向量的开头和结尾修剪所有空白字符串):

[0] = "Abc"
[1] = "Def"
[2] = ""
[3] = "Ghi"

目前,我使用正向迭代器到达"Abc",并使用反向迭代器返回"Ghi",然后使用这些迭代器构造一个新向量。这种方法有效,但我想知道是否有更简单的方法来修剪这些元素。

附:我是 C++ 菜鸟。

编辑

另外,我应该提到,向量可能完全由空白字符串组成,在这种情况下,0 大小的向量将是所需的结果。

【问题讨论】:

我正要建议迭代器,直到我读到你的最后一段。你目前正在做的事情听起来像是 C++ 的方式。 您可以使用 std::find_if 来避免编写循环。 (但请注意,这会阻止您从结束到开始进行同时迭代来检测冗长的向量仅由空字符串组成) @villintehaspam:同时做这件事有什么意义?当它为空时,您需要检查所有元素。还不如在 find_if 里做。。 @dreamlax:你的编译器是什么?这正是使用 lambdas 变得容易得多的东西.. 那应该是 Visual Studio 2010 的编译器,所以你也可以像 std::find_if(vec.begin(), vec.end(), [](const std::string& s) return !s.empty();); 这样使用 std::find_if ,这样更整洁。 【参考方案1】:

这个怎么样,有一个谓词:

class StringNotEmpty

  bool operator()(const std::string& s)  return !s.empty(); 
;

现在修剪:

vec.erase(std::find_if(vec.rbegin(), vec.rend(), StringNotEmpty()).base(), vec.end());
vec.erase(vec.begin(), std::find_if(vec.begin(), vec.end(), StringNotEmpty()));

.base() 调用中可能存在不合时宜的情况,但总体思路应该可行。

【讨论】:

【参考方案2】:

你的方法是合理的。另一种方法是找到第一个字符串,然后将所有连续的字符串复制到向量中的开始(和后面)元素,然后修剪向量的结尾。

除非这是拖慢您的应用程序的关键代码,否则它的工作原理比您拥有最有效的实现更为重要。

【讨论】:

是的,我根本不关心性能,我只想要最容易理解的方法。我正在查看我的迭代器方法,并认为我可能不记得为什么它会在一年后起作用(它只是似乎混乱)。【参考方案3】:

你做的很好。但是,如果您想“就地”缩短单个容器而不是复制子范围,您可以 erasevector 中的其他范围。

// Probably similar to what you already have: Find iterators for the range to keep.
std::vector<std::string>::iterator start=strs.begin(), stop=strs.end();
start = strs.begin();

while (start != stop && start->empty()) ++start;
if (start == stop) 
  // all strings were empty!
  strs.clear();
 else 
  while (start != --stop && stop->empty()) ;
  ++stop;

  // std::vector<std::string>(start, stop) is the desired subrange
  strs.erase(stop, strs.end());
  strs.erase(strs.begin(), start);

但我同意@Nathan 的观点:如果您已经拥有的东西对您来说比这更有意义,请保留它,除非您知道或强烈怀疑其中会涉及大量字符串。

【讨论】:

【参考方案4】:
typedef std::vector<std::strings> Strings;

Strings myStrings;
//... currently myStrings contains "","Abc","Def","","Ghi","",""

Strings::iterator itNewBegin = myStrings.begin();
Strings::iterator itNewEnd = myStrings.end();

std::advance(itNewBegin,1);
std::advance(itNewEnd,4);

String myTrimmedStrings(itNewBegin,itNewEnd);
//... currently myTrimmedStringscontains "Abc","Def","","Ghi"

出于好奇,我想看看您使用反向迭代器的代码。 我不明白你从两个具有不同方向的迭代器构造新向量的方式。

【讨论】:

我使用反向迭代器的.base() 成员将其转回正向迭代器。 哦,thanx,我已经很久没有在正向和反向迭代器之间来回播放了,这对我来说太头疼了。在我的代码中,我向您展示了函数 td::advance 的用法,它有时很有用。它对应的 std::distance 告诉你迭代器在容器中的当前位置。【参考方案5】:

迭代器绝对是要走的路。它们使代码更具可读性和直观性。

#include <algorithm>

typedef std::vector<std::string> strVec_t;

bool notEmpty(std::string s)
  return !s.empty();


void trim(strVec_t& vec)

  //get the first and last non-empty elements
  strVec_t::const_iterator        first = std::find_if(vec.begin(),  vec.end(),  notEmpty);
  strVec_t::const_reverse_iterator last = std::find_if(vec.rbegin(), vec.rend(), notEmpty);

  //if every element is an empty string, delete them all
  if (first == vec.end())
    vec.clear();

  //make a new vector from the first to the last non-empty elements
   else 
    vec = strVec_t(first, last.base());
  

【讨论】:

以上是关于修剪字符串向量的主要内容,如果未能解决你的问题,请参考以下文章

如何使用特定的成员变量修剪我的向量?

将字符串修剪为R中特定数量的字符

从向量集中修剪非公共元素

在Java中修剪字符串,同时保留完整的单词

从字符串中修剪字符串[重复]

使用 actionscript 3 修剪字符串