【问题标题】:Explaining a string trimming function解释字符串修剪功能
【发布时间】:2014-08-16 23:42:36
【问题描述】:

我遇到了下面的代码,但在理解代码方面需要一些帮助。假设字符串 s 两边都有空格。

string trim(string const& s){
   auto front = find_if_not(begin(s), end(s), isspace);
   auto back = find_if_not(rbegin(s), rend(s), isspace);
   return string { front, back.base() };
}

作者说 back 指向最后一个空格的末尾,而 front 指向第一个非空白字符。所以 back.base() 被调用了,但我不明白为什么。

return 语句后面的字符串后面的花括号代表什么?

【问题讨论】:

  • 没有对整个事情进行概要分析,这实际上看起来像是一段整洁的代码。
  • 此代码将在仅包含一个或多个空白字符的输入上崩溃,因为迭代器会交叉。
  • @j_random_hacker 我同意。它需要一个安全阀来确保back.base() 大于front,否则只返回空字符串。原则上我还是喜欢这个主意。我认为它实际上会抛出一个长度异常,因为last - first 的结果将为负数。

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


【解决方案1】:

大括号是新的 C++11 初始化。

.base() 和反向迭代器

.base() 是取回底层迭代器(back 是一个reverse_iterator),以便从有效范围内正确构造新字符串。

一张图片。字符串的正常迭代器位置(关于rend() 的工作原理,它比这稍微复杂一点,但无论如何在概念上......)

        begin                                 end
          v                                    v
        -------------------------------------
        | sp | sp | A | B | C | D | sp | sp |
        -------------------------------------
      ^                                   ^
    rend                                rbegin

一旦你的两个 find 循环完成,这些迭代器在这个序列中的结果将被定位在:

                  front
                    v
        -------------------------------------
        | sp | sp | A | B | C | D | sp | sp |
        -------------------------------------
                                ^
                              back

如果我们只使用那些迭代器并从中构造一个序列(我们不能,因为它们不匹配类型,但无论如何,假设我们可以),结果将是“从 A 开始复制,停止在 D" 但它不会在结果数据中包含 D

输入反向迭代器的back() 成员。它返回前向迭代器类的非反向迭代器,它位于后向迭代器“旁边”的元素处;即

                  front
                    v
        -------------------------------------
        | sp | sp | A | B | C | D | sp | sp |
        -------------------------------------
                                    ^
                               back.base()

现在当我们复制我们的范围 { front, back.base() } 时,我们从 A 开始复制并在第一个空格处停止(但不包括它),因此包括我们会错过的 D。 p>

它实际上是一段漂亮的小代码,顺便说一句。

一些额外的检查

在原始代码中添加了一些基本检查。

为了与原始代码的精神保持一致(C++1y/C++14 用法),添加了一些对空字符串和纯空格字符串的基本检查;

string trim_check(string const& s)
{
  auto is_space = [](char c) { return isspace(c, locale()); };
  auto front = find_if_not(begin(s), end(s), is_space);
  auto back = find_if_not(rbegin(s), make_reverse_iterator(front), is_space);
  return string { front, back.base() };
}

【讨论】:

  • 值得注意的是base()迭代器引用了反向迭代器引用的元素next to。在这种情况下,有点与std::next(back) 同义,但不是反向,而是底层序列的“正向”方向。
  • @WhozCraig 是的。作为附加参考,en.cppreference.com/w/cpp/iterator/reverse_iterator 包含一篇关于reverse_iterator 的精彩文章。
  • 感谢您的 cmets,但为什么 front 指向第一个非空白字符而 back 却不指向最后一个非空白字符?
  • @Smithy back 确实引用了最后一个非空白字符。但是,如果您将其作为 stopping 位置的副本的end 迭代器(或在您的情况下为迭代器范围构造函数)包含,并且您是一个槽 short i> (无论如何它都是错误的类型迭代器)。你不想停在那里,你想在那个位置之后停止一个“槽”。将其想象为类似于end() 在普通迭代器序列中引用“过去”最后一个元素的方式。请记住,在 C++ 中,迭代器端点意味着“到达这里”时停止,而不是“经过这里”时停止。我希望这是有道理的。
  • 我的 ascii-art 很蹩脚,但我希望能为这个答案带来一张照片(=1 btw)。
猜你喜欢
  • 2016-12-11
  • 1970-01-01
  • 2013-02-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多