2

受 C++20 修剪的可爱 cppreference示例的启发,我编写了以下代码(我已将返回类型更改为void和 arg 为,std::string&因为我的“问题”(我正在发明学习 C++20 的问题)不存在于使用std::string_viewarg 并返回的原始代码std::string)。

void trim(std::string&  in)
{
    auto view
        = std::views::all(in)
        | std::views::drop_while(isspace)
        | std::views::reverse
        | std::views::drop_while(isspace)
        | std::views::reverse;
        std::string result{view.begin(), view.end()};
        in = std::move(result);
}

这里的问题是这不是就地的,这意味着创建了新字符串。我可以编写更丑陋的代码来就地执行此操作,并且我知道传统的 C++ 算法不知道容器的存在,但我想知道 C++20 是否有一些技巧可以让我以优雅的方式进行修剪,但也是就地的。

这也是我丑陋的就地修剪(不确定它是否可以正常工作,但想法是它会就地修剪):

void trim2(std::string& s) {
    // ugly and error prone, but inplace
    const auto it1 = std::ranges::find_if_not(s, isspace);
    const auto it2 = std::ranges::find_if_not(s.rbegin(), s.rend(), isspace);
    const size_t shift = (it1==s.end()) ? 0: std::distance(s.begin(), it1);
    const size_t result_size = s.size() - shift - ((it2==s.rend()) ? 0 : std::distance(s.rbegin(), it2));
    std::shift_left(s.begin(), s.end(), shift);
    s.resize(result_size);
}

神螺栓

编辑:最初这个问题声称in.assign是 UB,但 TC 纠正了我。但根据我对 C++23 草案分配的理解,仍然会导致创建临时字符串。

4

3 回答 3

3

也许是这样的?

void trim(std::string& s) {
    auto not_space = [](unsigned char c){ return !std::isspace(c); };

    // erase the the spaces at the back first
    // so we don't have to do extra work
    s.erase(
        std::ranges::find_if(s | std::views::reverse, not_space).base(),
        s.end());

    // erase the spaces at the front
    s.erase(
        s.begin(),
        std::ranges::find_if(s, not_space));
}
于 2021-04-01T02:26:21.850 回答
2

assign使用任意迭代器需要创建一个临时的(因为改变字符串可以以任意方式影响迭代器的结果,并且因为抛出迭代器操作必须保持原始字符串保持不变),但是当给定字符串自己的迭代器时,每个主要实现都会做正确的事情.

所以我们可以解开两个级别的reverse_iterator-ness:

auto view
    = in
    | std::views::drop_while(isspace)
    | std::views::reverse
    | std::views::drop_while(isspace)
    | std::views::reverse;

in.assign(view.begin().base().base(), view.end().base().base());

(不过,我自己会去找巴里的答案。)

此外,很少需要显式使用views::all; 适配器会自动执行此操作。

于 2021-04-01T03:02:02.187 回答
0

是的,有可能:

void trim(std::string& s) {
  auto view = s
      | std::views::drop_while(isspace)
      | std::views::reverse
      | std::views::drop_while(isspace)
      | std::views::reverse;
  auto [in, out] = std::ranges::copy(view, s.begin());
  s.erase(out, s.end());
}

演示

于 2021-04-01T01:52:10.543 回答