如何从std :: string中删除重复的字符

时间:2011-03-01 10:17:39

标签: c++ stl

我有std::string这样:

std::string fileName;

其中fileName就像/tmp/fs////js//config.js 它来自某个地方,我需要存储它。但是当我存储它时,我需要从路径中删除额外的'/'字符,基本上只需要一个目录名和文件名之间的分隔符。

我可以通过一次迭代字符串一个字符并与下一个字符进行比较来删除它们,但效率不高。

有人能建议一些有效的方法吗?

4 个答案:

答案 0 :(得分:8)

删除重复的相邻元素是std::unique的工作。在这种情况下,你需要提供自己的谓词,但它是O(n)而且很简单。

struct both_slashes {
    bool operator()(char a, char b) const {
        return a == '/' && b == '/';
    }
};

std::string path("/tmp/fs////js//config.js");

path.erase(std::unique(path.begin(), path.end(), both_slashes()), path.end());

答案 1 :(得分:5)

你不会找到比这更有效的东西 - 考虑一下 - 你需要删除连续的重复字符 - 即使在最好的情况下,你也必须要看每个角色至少一次。

答案 2 :(得分:3)

我认为std::unique即使您的字符串未排序也会有效,因为它删除的所有内容都是连续重复的。

当然,它不会知道/在这里是一个特殊的字符,你可能会发现包含双字母的文件名也会被意外修改为单一字母,这显然是令人讨厌的。

它也是O(N),但你无法避免。

一个运行良好的算法是std :: remove_if,因为你可以放入你自己的“functor”,它可以保持状态,这样就可以知道最后一个字符是什么。

struct slash_pred
{
  char last_char;

  slash_pred()
   : last_char( '\0' ) // or whatever as long as it's not '/'
  {
  }

  bool operator()(char ch)
  {
      bool remove = (ch == '/') && (last_char == '/');
      last_char = ch;
  }
};

path.erase( std::remove_if( path.begin(), path.end(), 
      slash_pred() ), path.end() );

O(N)但应该有效。

对于认为remove_if可能是O(N ^ 2)的持不同政见者,它可能会像这样实现:

template< typename ForwardIterator, typename Pred >
ForwardIterator remove_if( ForwardIterator read, ForwardIterator end, Pred pred )
{
   ForwardIterator write = read; // outside the loop as we return it
   for( ; read!=end; ++read )
   {
      if( !pred( *read ) )
      {
         if( write != read ) // avoid self-assign
         {
            *write = *read;
         }
         ++write;
      }
   }
   return write;
}

答案 3 :(得分:0)

时间为O(n)+内存为O(n)

void clean_path(std::string& path) {
    std::string new_path;
    char sep = '/';
    for (auto i = 0; i < path.size(); ++i) {
        if (path[i] == sep && !new_path.empty() && new_path.back() == sep)
            continue;
        new_path.push_back(path[i]);
    }
    path = new_path;
}