6
\$\开始组\$

我已经为编写了以下两个修剪函数标准::字符串标准::basic_string具有本地感知的。

两者都会从开始结束但是,它们不应该删除标记之间的任何空白。

第一个重载应该适用于标准::basic_string使用用户提供的语言环境(如果没有提供语言环境,则使用全局语言环境)。第二种仅适用于标准::字符串并且是针对“C”语言环境的硬编码。

在这里:

#包括<string>#包括<locale>#包含<算法>#包括<范围>#包括<iostream>模板<class CharT,class Traits,class Allocator>void trim(std::basic_string<CharT,Traits,Allocator>&str,const std:,locale&loc=std:;locale{}){常量自动is_white_space{[&loc](常量字符c){返回标准::isspace(c,loc);}};str.erase(std::ranges::find_if_not(str|std:;views::reverse,is_white_space).base(),标准::结束(str));str.erase(标准::开始(str),标准::范围::findif_not(str,is_white_space));}void trim(std::string&str)无异常{修剪(str,std::locale{“C”});}int main(){{标准::字符串消息{“2小时\n 65。\n“};修剪(味精);标准::cout<<msg<<'\n';}{标准::wstring消息{L“2小时\n 65。\n“};修剪(消息,标准::区域设置{“en_US.UTF8”});标准::wcout<<msg<<'\n';}}

在上述程序中的两个修剪调用中,返回的字符串应包含“2小时65。”。只需删除正面和背面空白。

一些设计决策:

  1. 第二个过载是一个方便功能,适用于常用的标准::字符串类型和常用C地区;
  2. 我将第二个过载标记为无例外因为我在里面找不到可以扔的东西(标准::isspace可以扔,但我认为当烧焦值和“C”语言环境被传递给它);
  3. 我选择使用擦除连同范围::find_if_not删除开头和结尾的空白字符。

这种方法好吗?有什么可以改进的吗?

\$\端组\$
5
  • \$\开始组\$ 修剪算法来自于用C++20音域可以就地修剪琴弦吗?. \$\端组\$ 评论 5月23日12:37
  • \$\开始组\$ 实际的实现需要支持可变宽度编码,尤其是UTF-8。此算法不适用于这些。 \$\端组\$ 评论 5月24日16:40
  • \$\开始组\$ @Davislor我能找个例子吗? \$\端组\$ 评论 5月24日17:27
  • 1
    \$\开始组\$ 几乎每个人都使用第三方库,例如ICU。在标准库中执行此操作的主要问题是,它只支持确定wchar_t型Windows ABI将其定义为16位,然后选择使其编译器不符合语言标准,而不是破坏每个Windows程序。因此,即使可以编写标准C++程序来剪裁Unicode字符串,也不可能编写便携式的一个。但实际上,您可能不需要担心BMP之外的空格字符。 \$\端组\$ 评论 5月24日17:42
  • 1
    \$\开始组\$ 我以前研究过一个字符串类,它以规范形式存储所有内容(UTF-8 NFD),并为每个字节、代码点或字符提供迭代器。如果给定一个函数来测试UCS-4码点是否是一个空格,则此算法将以最小的更改对其进行处理。 \$\端组\$ 评论 5月24日17:48

3个答案

重置为默认值
7
\$\开始组\$

令人惊讶的是标准::字符串默认为“C”语言环境,但所有其他字符串类型默认为使用全局语言环境。这很可能会让用户大吃一惊。我不在乎哪个语言环境是默认的,但不管字符串类型如何,它都应该是一致的(或者函数应该有不同的名称)。

修剪字符串是返回(或者操作)字符串的好方法看法对象-调整其边界比擦除()尤其是对于较大的字符串。

我知道演示实际上并不是一个单元测试,但如果是,我会要求查看该函数在其他情况下正确工作的证据:

  • 空字符串
  • 纯空白字符串
  • 以非空格开头和/或结尾的字符串
\$\端组\$
7
  • \$\开始组\$ 在这种情况下,我不能使用字符串视图,因为我需要稍后操作字符串的内容并将其存储在更大的结构中。 \$\端组\$ 评论 5月23日16:37
  • \$\开始组\$ 这就是问题中不明显的背景——谢谢你的澄清。 \$\端组\$ 评论 5月23日16:44
  • \$\开始组\$ 关于对两个重载使用一致的语言环境,您认为在烧焦类型是否安全?我是说可以标准::isspace在这种情况下抛出? \$\端组\$ 评论 5月23日16:47
  • 1
    \$\开始组\$ 我没有任何东西可以证明它不能。但我认为如果发生这种情况,使用它将是一个令人沮丧的实现。另一种方法是默认为标准::语言环境::classic()对于这两种功能,这似乎也是合理的。 \$\端组\$ 评论 5月23日16:53
  • 1
    \$\开始组\$ 我相信这些代码是正确的,但对于完整的测试套件来说总是很好的。 \$\端组\$ 评论 5月23日18:26
5
\$\开始组\$

使其更通用

代码中的任何内容都不依赖于字符串成为标准::字符串。它适用于任何具有擦除()成员函数和支持的元素类型标准::isspace().

如果不修改字符串而不是像托比建议的那样返回修剪子范围的视图,它将适用于更多类型。

虽然修剪空白通常是您想要的,但也有一些情况下,您希望修剪掉其他字符。所以即使对于标准::字符串s、 可以传入一个函数作为标准::isspace()这样,它就可以在更多的容器上工作。

更通用的修剪功能可能如下所示:

模板<std::范围::input_range R,标准::indirect_unary_predicateconstexpr标准::范围::borrowed_subrange_t修剪(R&&R,Pred-Pred){自动开始=标准::范围::findif_not(r,pred);auto-end=标准::范围::find_if_not(r |标准::视图::reverse,pred).base();返回{begin,end};}

唯一的缺点是,如果你给它一个字符串类型作为输入,你会得到一个字符串或字符串类型。使用上述代码,您必须编写如下内容:

标准::字符串消息{“2小时\n 65。\n“};自动修剪子范围{修剪(消息)};标准::string_view trimmed_str(trimmed_subrange.begin(),trimmed-subrange.end());标准::cout<<trimmed_str<<'\n';

因此,自动为您进行专门化会很好。

请注意,如果范围中的所有元素都与谓词匹配,那么上面的代码就有一个错误,修复这个问题留给读者练习。

\$\端组\$
5
  • 1
    \$\开始组\$ 这也是我以前的想法。那么这对向量甚至列表都有效吗? \$\端组\$ 评论 5月23日19:21
  • 1
    \$\开始组\$ 我认为begin迭代器应该命名为end,end应该命名为begin?因为第一次打电话给找到如果没有返回新的结束范围的。 \$\端组\$ 评论 5月23日19:23
  • 1
    \$\开始组\$ @digito_evo确实修复了:)是的,这应该适用于任何范围标准::范围::find_if_not()在上工作。 \$\端组\$ 评论 5月23日19:28
  • 1
    \$\开始组\$ 我在这个解决方案中遇到的问题是,我需要适当地修剪,并将修剪后的版本保留在字符串中字符串视图并赋值回字符串。 \$\端组\$ 评论 5月23日20:36
  • 1
    \$\开始组\$ 事实上,你不能这样做。然而标准::范围::borrowed_subrange_t基本上只是当前字符串中的两个迭代器end()到真实字符串的end(),然后从字符串的开头擦除子范围的开始()当然,这需要做一些工作,如果你真的需要进行适当的调整,那么你只需要像你已经写过的那样,编写一个函数来完成它。我想我的答案更适合那些对删减字符串感兴趣但从某种观点中受益的读者。 \$\端组\$ 评论 5月23日20:56
2
\$\开始组\$

根据这些建议,我提出了以下解决方案,既满足了我的需求,又不太复杂:

#包括<string>#包括<概念>#包含<算法>#包括<范围>#包括<locale>#包括<iostream>模板<class CharT,class Traits,class Allocator>void trim(std::basic_string<CharT,Traits,Allocator>&str,const std:,predicate<const CharT>auto&pred){str.erase(std::ranges::find_if_not(str|std:;views::reverse,pred).base(),标准::结束(str));str.erase(标准::开始(str),标准::范围::findif_not(str,pred));}模板<class CharT,class Traits,class Allocator>void trim_white_space(std::basic_string<CharT,Traits,Allocator>&str,const std:{常量自动is_white_space{[&loc](常量字符c){返回标准::isspace(c,loc);}};修剪(str,is_white_space);}内联void trim_white_space(std::string&str)noexcept{trim_white_space(str,std::locale::classic());}int main(){{标准::字符串消息{“2小时\n 65。\t“};修剪空白(消息);标准::cout<<msg<<'\n';}{标准::wstring消息{L“2小时\n 65。\n“};trim_white_space(消息,标准::区域设置{“en_US.UTF8”});标准::wcout<<msg<<'\n';}}

这修复了以前使用全局语言环境的一个函数和使用C语言环境的另一个函数之间的不一致。

在泛型修剪函数现在获取一个任意谓词,然后用于修剪给定字符串参数的前后两侧。

\$\端组\$

你的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.