从std :: string中删除空格的最佳方法

时间:2015-08-12 08:08:50

标签: c++ c++11 c++14

我已将文本文件内容加载到std :: string。我想从加载的字符串中删除空格。

  1. 需要使用以下哪种方法才能获得更好的性能?
  2. 以下哪种方法可以是最佳做法。
  3. 否则有更好的方法可以达到这个目的吗?
  4. 方法1:
    在循环语句中使用string.find()扫描字符串,并使用string.erase()删除空格;

    方法2:
    在循环语句中使用string.find()扫描字符串,并使用string.append()将非空白字符复制到新的string()变量;

    方法3:
    在循环语句中使用string.find()扫描字符串,并使用string.replace()将非空白字符复制到新字符串(size_t n,char c)变量;

    方法4:
    分配char *(使用malloc [源字符串的大小])
    在循环语句中使用string.find()扫描字符串,并使用strcpy将非空格字符复制到char *变量,然后使用strcat();
    最后将char *复制到一个新的字符串
    免费char *

6 个答案:

答案 0 :(得分:11)

编辑已升级为区域设置感知特征。谢谢用户657267!

标准算法很整洁!

s.erase(std::remove_if(
    begin(s), end(s),
    [l = std::locale{}](auto ch) { return std::isspace(ch, l); }
), end(s));

Live on Coliru

这是为了就地修改,但是如果你需要保留原始字符串:

std::string s2;
s2.reserve(s.size());
std::remove_copy_if(
    begin(s), end(s),
    std::back_inserter(s2),
    [l = std::locale{}](auto ch) { return std::isspace(ch, l); }
);

Live on Coliru

答案 1 :(得分:3)

为了便于阅读,我会选择boost string algorithm library

#include <boost/algorithm/string.hpp>
std::string str1="Hello  Dolly,   Hello World!"
boost::erase_all(str1, " ");

编辑: 所有空白字符的示例:

std::string str1="Hello  Dolly,\n   Hello\t World!\t";
boost::find_format_all(str1, boost::token_finder(::isspace), boost::const_formatter(""));

EDIT2: 我运行了一些基准测试,看看这种方法与Quentin的答案相比如何。我使用区域设置感知版本和非区域设置感知版本的isspace运行了100个样本。微秒的平均时间是:

| method                            | avg. time (μs) |
|-----------------------------------|----------------|
| boost::find_format_all w/locale   | 2.02429        |
| boost::find_format_all w/o locale | 0.578105588    |
| std::remove_if w/locale           | 1.197737742    |
| std::remove_if w/o locale         | 0.190661227    |

答案 2 :(得分:3)

恕我直言,你可以通过方法2获得最佳性能,但在追加之前,你需要调用std::string::reserve方法将新字符串的容量设置为初始字符串的大小。这需要在附加时防止不必要的重新分配。

答案 3 :(得分:1)

为了性能,最好一次读取字符串一个平台字(64位平台上的8个字节),然后从寄存器读取中提取每个字符,测试它是否为空白,如果它不是空格,将其添加到下一个要写入的平台字宽寄存器中。当要写入的寄存器已满(存储的字符数可以容纳在寄存器中)时,将其写入预先分配的输出缓冲区。对于单字节字符串,字符逐字符扫描速度要慢8倍。

最后,字符串的尾部可能包含的字符数少于占用完整平台字的字符数。然后需要一个计数器来知道最后处理的平台字中有多少个字符。

答案 4 :(得分:1)

方法5:使用库覆盖所有边缘情况(包括当前区域设置),并且当且仅当分析显示它是一个问题时进行调整。

#include <algorithm>
#include <functional>
#include <iostream>
#include <locale>
#include <string>

template<typename CharT, typename Traits, typename Allocator>
std::basic_string<CharT, Traits, Allocator>
strip_whitespace(std::basic_string<CharT, Traits, Allocator> str)
{
  str.erase(
    remove_if(str.begin(), str.end(), bind(
      std::isspace<CharT>, std::placeholders::_1, std::locale{}
    )),
    str.end()
  );
  return str;
}

int main()
{
  std::string str{"A string with \n whitespace"};
  std::cout << str << '\n' << strip_whitespace(str) << '\n';
}

答案 5 :(得分:1)

目前尚不清楚“删除空格”究竟是什么意思 - 它是打算让文本不可读并破坏源代码,还是指“任何多余的空格”?

由于有一个答案提示第三方库,我将使用Qt的方法,它将仅删除“多余的空白”:

QString s("Test\n new line,   multiple spacebars \t tab!");

qDebug() << s;
qDebug() << s.simplified();

输出:

"Test
 new line,   multiple spacebars      tab!"

"Test new line, multiple spacebars tab!"

其他大部分答案都集中在删除所有空格键,但严格来说,还有其他字符可以分类为空格,例如制表符或换行符。

查看simplified()方法的代码,我认为它非常有效:

QString QString::simplified() const
{
    if (d->size == 0)
        return *this;
    QString result(d->size, Qt::Uninitialized);
    const QChar *from = (const QChar*) d->data;
    const QChar *fromend = (const QChar*) from+d->size;
    int outc=0;
    QChar *to   = (QChar*) result.d->data;
    for (;;) {
        while (from!=fromend && from->isSpace())
            from++;
        while (from!=fromend && !from->isSpace())
            to[outc++] = *from++;
        if (from!=fromend)
            to[outc++] = QLatin1Char(' ');
        else
            break;
    }
    if (outc > 0 && to[outc-1] == QLatin1Char(' '))
        outc--;
    result.truncate(outc);
    return result;
}

新的字符串是在没有初始化的情况下预先分配的,因此避免了任何初始化和任何重新分配,然后类似于方法2(记住追加而不保留空间将导致许多和缓慢的重新分配),只有有用的数据被复制到结果字符串,最后,它被修剪得更短,以消除任何浪费的内存。

您可以按照此逻辑有效地为std::string

实施它

直接来自Qt source code