百科问答小站 logo
百科问答小站 font logo



C++ 的 string 为什么不提供 split 函数? 第1页

  

user avatar   wmx16835 网友的相关建议: 
      

2019-10-22 更新:感谢 @依云 在评论区提到了 Lazy Evaluation,这个特性在未来 C++ Coroutine 加入标准后将很容易、很优雅地实现。但据我所知,很多第三方库(不限于 C++ 语言)都提供了两套 API,支持实时计算和 Lazy Evaluation。我认为即使确实存在 Lazy Evaluation 可能在一定程度上提升性能的场景,实时计算也是存在充足的需求的,至少它省去了维护 Coroutine 相关数据结构的开销。


我猜应该很多人都手写过 split 吧,我也曾经在自己的项目中写过类似这样的函数。虽然我没有深入研究过 std::string 的标准,但根据我个人的经验,std::string 没有提供 split 成员函数可能有以下原因:

  1. 功能上,我发现很多第三方库(不限于 C++ 语言)都提供一些其他的特性,比如对正则的支持、对键值对的支持、选择性删除首尾空格、忽略空串(这个需求貌似不是很大) 等等。作为 C++ 标准,我们应该支持上述哪些特性、或者一个都不支持呢?
  2. 兼容性上,C++ 语言不仅有 std::basic_string,还有 std::basic_string_view,甚至 C++20 之后还可能加入一些 Concepts(我不确定),所以仅把 split 作为 std::basic_string 的成员函数有失偏颇。不仅被分割的字符串可能有各种各样的类型,分割符、保存结果的容器在用户手里都可能具有不同的类型。即使不支持第一点中所述任一功能,仅支持这些不同类型的语义就会十分复杂。
  3. 其他答案中也有提到,很多 C++ 第三方库都强依赖于 std::vector;这样对于大部分用户来说虽然很难产生性能问题,但对于 STL 来说这种强依赖是不能接受的。


不过即使上述问题客观存在,我相信办法总比问题多。即使 split 在短时间内可能很难加入标准,我们也可以设计出符合我们需求的 split 函数。


如果大家感兴趣可以参考下我之前项目中使用的版本(我自己手写的),不支持第一点中的那些特性,但基本解决第 2、3 点问题,算是抛砖引玉,欢迎大家吐槽。


首先是函数签名:

       template <class Str, class Spt, class C = std::vector</* see below */>> C split(const Str& str, const Spt& splitter, C result = C{});      

模板类型说明:

Str: 待分割字符串类型,可以是 std::basic_stringstd::string_view、C-style 字符串或其他具有 findsubstr 的自定义类型。

Spt: 分割符,可以是 Str 的任何类型或字符类型。

C: 结果容器,默认为 std::vector 实例,支持定制。


实现(C++17):

       #include <type_traits> #include <string> #include <string_view> #include <vector>  namespace detail {  template <class SFINAE, class Str> struct sfinae_is_general_string : std::false_type {};  template <class Str> struct sfinae_is_general_string<std::enable_if_t<std::is_convertible_v<     decltype(std::declval<Str>().length()), std::size_t>>, Str>     : std::true_type {};  template <class Str> decltype(auto) normalize_str(const Str& s) {   if constexpr (std::is_pointer_v<Str>) {     return std::basic_string<std::remove_pointer_t<Str>>{s};   } else if constexpr (std::is_array_v<Str>) {     return std::basic_string<std::remove_extent_t<Str>>{s};   } else {     return s;   } }  template <class Spt> decltype(auto) normalize_splitter(const Spt& s) {   if constexpr (std::is_pointer_v<Spt>) {     return std::basic_string_view<std::remove_pointer_t<Spt>>{s};   } else if constexpr (std::is_array_v<Spt>) {     return std::basic_string_view<std::remove_extent_t<Spt>>{s};   } else if constexpr (sfinae_is_general_string<void, Spt>::value) {     return s;   } else {     return std::basic_string<Spt>(1u, s);   } }  template <class Str> using normalized_str_t = std::decay_t<std::invoke_result_t<     decltype(normalize_str<Str>), Str>>;  template <class Str, class Spt, class Cp> void do_split(const Str& str, const Spt& splitter, Cp* result) {   std::size_t substr_begin = 0u, substr_end;   while ((substr_end = str.find(splitter, substr_begin)) != Str::npos) {     result->push_back(str.substr(substr_begin, substr_end));     substr_begin = substr_end + splitter.length();   }   result->push_back(str.substr(substr_begin, str.size())); }  }  // namespace detail  template <class Str, class Spt,     class C = std::vector<detail::normalized_str_t<Str>>> C split(const Str& str, const Spt& splitter, C result = C{}) {   detail::do_split(detail::normalize_str(str),       detail::normalize_splitter(splitter), &result);   return result; }      

user avatar    网友的相关建议: 
      

来自Stack Overflow

For what it's worth, here's another way to extract tokens from an input string, relying only on standard library facilities. It's an example of the power and elegance behind the design of the STL.

         #include <iostream> #include <string> #include <sstream> #include <algorithm> #include <iterator>  int main() {     using namespace std;     string sentence = "And I feel fine...";     istringstream iss(sentence);     copy(istream_iterator<string>(iss),          istream_iterator<string>(),          ostream_iterator<string>(cout, "
")); }       

Instead of copying the extracted tokens to an output stream, one could insert them into a container, using the same generic copy algorithm.

         vector<string> tokens; copy(istream_iterator<string>(iss),      istream_iterator<string>(),      back_inserter(tokens));       

... or create the vector directly:

         vector<string> tokens{istream_iterator<string>{iss},                       istream_iterator<string>{}};        
Split a string in C++?

user avatar   frmf 网友的相关建议: 
      

C++11以前有很多原因不能提供一个通用的split,比如说需要考虑split以后的结果存储在什么类型的容器中,可以是vector、list等等包括自定义容器,很难提供一个通用的;再比如说需要split的源字符串很大的时候运算的时间可能会很长,所以这个split最好是lazy的,每次只返回一条结果。

C++11之前只能自己写,我目前发现的史上最优雅的一个实现是这样的:

       void split(const string& s, vector<string>& tokens, const string& delimiters = " ") {     string::size_type lastPos = s.find_first_not_of(delimiters, 0);     string::size_type pos = s.find_first_of(delimiters, lastPos);     while (string::npos != pos || string::npos != lastPos) {         tokens.push_back(s.substr(lastPos, pos - lastPos));//use emplace_back after C++11         lastPos = s.find_first_not_of(delimiters, pos);         pos = s.find_first_of(delimiters, lastPos);     } }      

从C++11开始,标准库中提供了regex,regex用来做split就是小儿科了,比如:

       std::string text = "Quick brown fox."; std::regex ws_re("\s+"); // whitespace std::vector<std::string> v(std::sregex_token_iterator(text.begin(), text.end(), ws_re, -1),      std::sregex_token_iterator()); for(auto&& s: v)     std::cout<<s<<"
";      

C++17提供的string_view可以加速上面提到的第一个split实现,减少拷贝,性能有不小提升,参看此文:Speeding Up string_view String Split Implementation

从C++20开始,标准库中提供了ranges,有专门的split view,只要写str | split(' ')就可以切分字符串,如果要将结果搜集到vector<string>中,可以这样用(随手写的,可能不是最简):

       string str("hello world test split"); auto sv = str     | ranges::views::split(' ')      | ranges::views::transform([](auto&& i){         return i | ranges::to<string>(); })      | ranges::to<vector>();      for(auto&& s: sv) {     cout<<s<<"
"; }      

其实C语言里面也有一个函数strtok用于char*的split,例如:

       #include <string.h> #include <iostream> #include <string> using namespace std; int main()  {     string str = "one two three four five";     char *token = strtok(str.data(), " ");// non-const data() needs c++17     while (token != NULL) {         std::cout << token << '
';         token = strtok(NULL, " ");     } }  //如你所愿,输出如下: one two three four five      

这里要注意的是strtok的第一个参数类型是char*而不是const char*,实际上strtok的确会改变输入的字符串。

参考文献:

史上最优雅的split来自这里

ranges for C++20的用法参考我写的教程

欢迎关注个人公众号「树屋编程」,专注分享C++的学习知识与程序员的职场体验


user avatar   li-ze-zheng-60 网友的相关建议: 
      

首先这是Fed一月 memo

先说结论:

FOMC 维持利率在 0-0.25% 不变。且确定 3 月完全停止 QE,同时 3 月加息也是箭在弦上,基本会后声明皆符合市场预期,没有太多的意外。

Powell 记者会确实是偏一点点的小鹰派,但我也认为,Powell 的说法不至于拉升市场加息预期至 5次 、并拉升缩表预期至上半年,反而比较像是在强化加息 4 次之预期。

另外我个人觉得,一些中文媒体似乎误读了Powell 记者会的部分片段,下面 Allen 再进一步说明。


1. 3 月加息停止 QE 早已定价

本次会议 Fed 再次确认 3 月将准备第一次加息,并同时停止 QE。

Fed 也再次重申,货币政策是要支持美国经济达到充分就业、与通膨长期均值维持 2.0% 的两大目标。

这部分我想市场早已定价,这裡完全不会是问题,所以我们不讨论太多。


2.未来加息在每次会议都可能发生 (?)

Powell 的原文说法是:Won't Rule Out Hike Every Meeting.

但我有看到部分中文媒体写:不排除每次会议都加息的可能性。

上述我想或许是误读了 (还是其实是我自己误会中文的意思 ?)

我的理解是:Powell 是说加息在未来每场会议都可能发生,指的是“不会在特定月份才加息”,不是说每场都要加息。

Powell 说得很合理,经济本来就是动态的,加息本就不会侷限在什麽月份才启动,端看当时的经济状况而定。

我认为Powell 上述说法,并未延展今年加息预期至五次或更多,若有这种想法,那绝对是误读了。


3.更大规模的缩表?

Powell 在记者会上提到,Fed 需要更大规模的缩表,但请大家不要恐慌,因为我又觉得部份中文媒体过度解读了。

我认为Powell 说到的“更大规模缩表”,在思维上指的是:

因为当前 Fed 资产负债表高达 8.9 万美元,这是新冠疫情爆发之前的两倍大,显然在绝对规模上是非常巨大的。

而上一轮 2017-2019 年 Fed 缩减资产负债表,是自 4.4 万亿美元缩到 3.7 万亿美元停止,缩表的幅度大概是 15.9%,共缩减了约 7000 亿美元。

确实每次缩表的经济背景绝对是不一样的,所以幅度也绝对不会相同,但我们随便抓,假设本轮缩表将缩减 10% 资产负债表规模,那麽这也要降低 8900 亿美元,规模当然很大。

但我认为,不需要过度恐慌在“更大规模缩表”这几个字上。更重要的,我认为是“Fed 缩表的速率是多少?”

我相信缩表没问题,缩表太快才是问题,因为缩表速度若太快,将直接影响的会是美债殖利率升速、以及殖利率曲线的斜率。

这点Powell 也非常清楚,Powell 在记者会上也不断强调,联准会内部尚未具体讨论到一切缩表的进度,要等到 3 月再说。


4.缩表比较可能落在下半年

Powell 在记者会上说明,希望在加息至少一次之后,再来开会讨论缩表的事情,且委员会至少将讨论一次,才会做最终拍板。

更重要的,Powell 希望缩表的进程是有秩序的、是可被预见的过程。

从上述Powell 丢出的时间表看,我个人认为缩表将落在 2022 下半年,最快可能是 6 月份,因为在 3 月加息后,Fed 才会来讨论缩表。

我个人相信 Fed 现在内部早已在讨论缩表,但委员会显然尚未准备好来与市场沟通缩表的前瞻指引。

而缩表这麽大的事情,我个人认为 Fed 需要起次跟市场沟通 2 次,并把缩表规划说得非常清楚之后,才会开始进行,所以比较合理的缩表时间,估计将会落在下半年。


5.最大风险:高通膨

Powell 在记者会上,大概提到了 800 万次的“高通膨压力”,并认为目前美国通膨风险仍在上升阶段,但预计 2022 通膨还是会回落。

Powell 说明,目前美国通膨居高不下,主要仍是供应链所致,白话来说就是供需仍然失衡,且供给侧 (Supply Side) 改善的速度是低于预期。

Powell 强调,目前美国高通膨持续存在,而美国经济要的是长期扩张,所以若要长期扩张,物价势必需要保持稳定。

这边开始进入正题了,我认为这是本次会议的最重要核心,是让我体感上,觉得 Fed 鹰派的地方。我认为 Fed 承认自己落后给菲利浦曲线 (Behind the curve),简单而言,Fed 这次的加息速度大幅落后给通膨。

由于 Fed 在 2021 年对于通膨的误判,先前 Fed 在 2021 年认为通膨在年底就可望自然回落,但也就是因为这件事没有发生,反而通膨还更为严重,所以目前才有使用加息来追赶通膨的压力。但当前宏观环境看,通膨的压力是来自于缺工、供应链紧俏等问题,再加上拜登政府的大力推行财政刺激在那边推波助澜~

所以这一次的通膨是来自于实体经济上的供需失衡问题,并不是金融市场过度投机、企业超额投资等问题,我认为 Fed 在这次的通膨问题上,能做得空间非常有限。

这裡将产生一个不确定性的较大风险,就是 Fed 只能靠货币紧缩去压通膨预期,但实体经济的根本性通膨问题,还是没有获得解决。变成最终 Fed 只能再用更剧烈的紧缩政策,去引导通膨预期走低后,尝试来压低实际通膨率,所以这裡将让 Fed 的紧缩路径,存在著较大不确定性。

比较好的处理方式,应该是直接去解决实体经济上的缺工和供应链/例如我之前提到的塞港问题,让实际通膨率自己走低、而不是靠 Fed 挤压通膨预期之后去引导。

谁可以去把坐在白宫裡疑似患有阿兹海默的白髮老头一巴掌打醒...还我特~


结论:我个人认为 Fed 今年将加息四次,不至于加息五次,而加息四次之预期,相信市场应该已经定价;至于缩表,相信市场尚未定价,估计将落在 2022 下半年,最快可能是 6 月。

如果 Fed 今年加息五次,我会感到非常意外,因为这意味著 Fed 很可能在 2023 年底、2024 年初,就因为美国经济放缓太快而需要降息,Fed 这波操作就会变得非常韭。

最后说说股市的想法目前 Nasdaq 已经插水一段时日,抑制通胀是当务之急,而股市所谓修正才多久已出现V转。对通胀而言意义不大,修正数月才可能有帮助~所以我之前一直描述为“恐慌”。因此对白髮老头而言,怎麽做才有利于中期选举就很清晰了。

最好还是坚持认为市场或已定价加息四次之预期,但缩表预期则是尚未定价的观点。

配置上美股我倾向持有科技权值股,一些 Megacap 的估值我认为合理、前景确定性较高,而这样也可以让你的收益贴著 QQQ 走。

考虑到一堆成长股腰斩,我也愿意加仓接刀成长股,但建议佔据投资组合的比例,或许不要超过 15%,如果选股功力不错,这裡就会开始让你的收益拉开与 QQQ 之类的差距。

最后,我相信人人都会想在市场下跌的环境裡接刀,接刀不是不行,但若接刀失败,斩缆我建议速度要快,我个人不考虑价投的话一次斩缆的比例都是 50% 以上。




  

相关话题

  C++ 有提供像是 C 中 realloc() 这样的函数的新版本吗? 
  大括号不换行的坏处有什么?为什么有人不换行? 
  在C语言中,math.h中定义的各种数学函数在电脑上具体是怎么实现的? 
  怎么形象地理解MFC编程框架? 
  为什么现在招聘C++程序员这么难? 
  怎么形象地理解MFC编程框架? 
  C++ 几十年来为什么没给 break 语句加上参数? 
  为什么一些人很瞧不起 Java? 
  在C++中,“?:”这个符号叫什么名字? 
  std::list::sort 用了什么算法?为什么速度这么快? 

前一个讨论
耳机不建议手机直推是什么意思?
下一个讨论
求装机大神指教组装电脑需要了解什么 ,怎么匹配硬件性能最高 ,及怎么买最实惠?





© 2024-06-27 - tinynew.org. All Rights Reserved.
© 2024-06-27 - tinynew.org. 保留所有权利