在std :: string中去除内部空格

5
我正在寻找一种优雅的方式将std::string从类似以下内容的格式转换为另一种格式:
std::string text = "   a\t   very  \t   ugly   \t\t\t\t   string       ";

至:

std::string text = "a very ugly string";

我已经使用boost :: trim(text);修剪了外部空格。
[编辑] 因此,多个空格和制表符被减少为一个空格 [/编辑]
去除外部空格很简单。但是否有一种优雅的方法可以去除内部空格,而不涉及手动迭代和比较前一个和下一个字符?也许是我错过了boost中的某些东西?

只是一点提醒,我并没有真正使用过 boost::splitboost::join,但在Python中编写这个的明显方法是 ' '.join(text.split()),类似的方式也应该是可行的。虽然它不一定像将字节直接复制到最终位置那样高效,但它简洁明了。 - Steve Jessop
如果你不介意复制的话,split和join非常好用;但如果你担心效率(在这种情况下),编写自己的循环可能是最好的选择。 - Marshall Clow
@Marshall:我基于问题中提到的“优雅”,而不是“快速但丑陋”来进行工作。 - Steve Jessop
6个回答

8
您可以使用std::uniquestd::remove,以及::isspace将多个空格字符压缩成单个空格:
std::remove(std::unique(std::begin(text), std::end(text), [](char c, char c2) {
    return ::isspace(c) && ::isspace(c2);
}), std::end(text));

它不能解决他的问题。test还包含'\t',而'\t'' '不相等。 - Nawaz
这样做会不会像“letting” - > “leting” 一样,跳过 \t 对? - Travis Gockel
糟糕,我又修好了。之前它无法合并相邻的空格和制表符,但现在可以了。 - Seth Carnegie
2
这会导致样例输入的结果是"a\tvery ugly string",这不正确吧?你可以添加一个transform的步骤(或者也许是一个boost::transform_iterator?)来将所有的空格替换为空格字符,但有时候放弃并编写循环也是可以的;-) - Steve Jessop
1
为什么要使用std::remove?在使用std::unique后,您需要使用std::replace_if\t字符替换为' ',但它仍然无法删除前导和尾随空格。这个答案并没有解决OP所问的问题。 - Fernando Silveira

7
std::istringstream iss(text);
text = "";
std::string s;
while(iss >> s){
     if ( text != "" ) text += " " + s;
     else text = s;
}
//use text, extra whitespaces are removed from it

1
啊,做法很有趣,加一分。虽然我不知道你的方式和我的哪个更有效(或者对于小字符串或“冷”代码区域是否重要),但它很有趣。 - Seth Carnegie
我认为,在else块中使用text.append(" " + s);会稍微快一些。 - Nawaz
那不会做同样的事情,对吧?(现在它用operator=覆盖了以前的内容,但append就像将其更改为+=;我认为这可能是原始代码中的一个打字错误) - Seth Carnegie
@SethCarnegie:但这正是我们想要的。抱歉,应该是+=而不是+。我不知道为什么人们在它并不完全正确时投了票 :P - Nawaz
4
另外一个吹毛求疵的注意事项,最好使用if (!text.empty())而不是if (text != "") - Seth Carnegie
我认为你可以在 while 循环之前执行 iss >> text 来改进这个程序。这样就不需要在循环内部使用 if else 语句块了,而是可以直接使用 text += ' ' + s; - Dillydill123

5
#include <boost/algorithm/string/trim_all.hpp>
string s;
boost::algorithm::trim_all(s);

4

我会做的大部分与@Nawaz已经发布的内容相似——从istringstream中读取字符串以获取没有空格的数据,然后在这些字符串之间插入一个空格。但是,我会使用来自先前答案infix_ostream_iterator来获取(在我看来)稍微更清晰/更易懂的代码。

std::istringstream buffer(input);

std::copy(std::istream_iterator<std::string>(buffer),
          std::istream_iterator<std::string>(),
          infix_ostream_iterator<std::string>(result, " "));

1

如果你查看https://svn.boost.org/trac/boost/ticket/1808,你会看到一个请求(几乎)完全相同的功能,并提供了一个建议的实现:

std::string trim_all ( const std::string &str ) {
return boost::algorithm::find_format_all_copy(
    boost::trim_copy(str),
    boost::algorithm::token_finder (boost::is_space(),boost::algorithm::token_compress_on),
    boost::algorithm::const_formatter(" "));
}

尝试添加代码块但没有成功...添加了一个答案,但我认为这是正确的方向。 - caktux

0
这是一个可能的版本,使用正则表达式。我的GCC 4.6还没有regex_replace,但Boost.Regex可以作为替代品:
#include <string>
#include <iostream>
// #include <regex>
#include <boost/regex.hpp>
#include <boost/algorithm/string/trim.hpp>

int main() {
  using namespace std;
  using namespace boost;
  string text = "   a\t   very  \t   ugly   \t\t\t\t   string       ";
  trim(text);
  regex pattern{"[[:space:]]+", regex_constants::egrep};
  string result = regex_replace(text, pattern, " ");
  cout << result << endl;
}

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