【问题标题】:Efficient way to check if std::string has only spaces检查 std::string 是否只有空格的有效方法
【发布时间】:2021-04-22 19:32:31
【问题描述】:

我只是在和一位朋友谈论检查 std::string 是否只有空格的最有效方法。他需要在他正在从事的嵌入式项目上执行此操作,显然这种优化对他很重要。

我想出了以下代码,它使用strtok()

bool has_only_spaces(std::string& str)
{
    char* token = strtok(const_cast<char*>(str.c_str()), " ");

    while (token != NULL)
    {   
        if (*token != ' ')
        {   
            return true;
        }   
    }   
    return false;
}

我正在寻找有关此代码的反馈,也欢迎以更有效的方式执行此任务。

【问题讨论】:

  • strtok(const_cast&lt;char*&gt;(str.c_str()), " ");... 认真的吗?
  • codereview.stackexchange.com 也可能有助于查看此代码。
  • @Mat 你有什么建议?说真的。
  • 您的朋友是否做过任何分析,表明简单地循环字符串的字符(使用索引或迭代器)效率太低?
  • @Blindly:这正是我的想法。 @karlphillip:strtok 修改它是第一个参数,如果它找到令牌。这就是为什么不需要const char* 以及为什么你的const_cast 是完全错误的做法。

标签: c++ string optimization whitespace


【解决方案1】:
if(str.find_first_not_of(' ') != std::string::npos)
{
    // There's a non-space.
}

【讨论】:

  • +1 很棒,一个标准库函数! (正要发布那个。)
  • 根据实际目的,这可能需要是isspace(),而不是硬编码的' '
  • @peterchen 要模拟isspace(),您可以发送一个字符串作为参数,例如str.find_first_not_of(" \t\n\v\f\r") != std::string::npos 忽略空格、制表符、换行符、垂直制表符、提要和回车。
  • @N.N.:这可能适用于所有实际目的(尤其是在谈论嵌入式时),但我希望这些也被视为空格:en.wikipedia.org/wiki/Space_(punctuation)#Spaces_in_Unicode
  • @peterchen 我试图在 Mark B 的代码中用 isspace() 替换 ' ' 但我得到“isspace(int) 的参数太少”。
【解决方案2】:

在 C++11 中,可以使用all_of 算法:

// Check if s consists only of whitespaces
bool whiteSpacesOnly = std::all_of(s.begin(),s.end(),isspace);

【讨论】:

  • 在非 C++11 编译器上,您可以使用 boost::algorithm::all_of()(在 C++11 编译器上回退到 std::all_of())。
  • 如果 gcc 给您以下错误:“模板参数推导/替换失败:无法推导模板参数'_Predicate'” - 请参阅:stackoverflow.com/questions/21578544/…
  • @Sundae 有两个 isspace() 函数,来自 的一个是我们这里需要的,而另一个来自 的则不是。解决方法是使用 ::isspace 作为谓词。
  • 如果字符串为 unicode,这可能会崩溃,请选择接受的答案。
【解决方案3】:

为什么这么多工作,这么多打字?

bool has_only_spaces(const std::string& str) {
   return str.find_first_not_of (' ') == str.npos;
}

【讨论】:

  • 我喜欢将其分离到它自己的函数中,这样当你说出if (has_only_spaces (str)) { ... }之类的内容时,你所做的事情就会立即清晰可见
  • 有点挑剔,但此答案中的代码 sn-p 缺少开头括号,因此导致语法错误。对于使用这个 sn-p 的人来说很容易解决,但只是指出来。很好的问题,可以作为很好的反应的催化剂!!!
【解决方案4】:

这样做不是更容易吗:

bool has_only_spaces(const std::string &str)
{
    for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
    {
        if (*it != ' ') return false;
    }
    return true;
}

这具有在找到非空格字符后立即返回的优点,因此它比检查整个字符串的解决方案效率略高。

【讨论】:

  • +1 喜欢。只要您想保持便携性,它就可以完成最少的必要工作。在最坏的情况下,您将不得不查看所有内容,但是一旦您知道一切都结束了,您就可以停下来。 (我会在上面撒很多 consts。)
  • 我能想到的唯一可能的低级优化是比较寄存器宽度的块,例如在 x86 上一次 4 个字节,并与 0x20202020 进行比较。但这太疯狂了。
【解决方案5】:

在 c++11 中检查字符串是否只有空格:

bool is_whitespace(const std::string& s) {
  return std::all_of(s.begin(), s.end(), isspace);
}

在 c++11 之前的版本中:

bool is_whitespace(const std::string& s) {
  for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
    if (!isspace(*it)) {
      return false;
    }
  }
  return true;
}

【讨论】:

  • 据我所知,我相信这行:return std::all_of(s.begin(), s.end(), isspace); 是未定义的行为。首选:std::all_of(s.begin(), s.end(), [](unsigned char c){ return std::isspace(c); });
  • @ViralTaco_ 为什么这是未定义的行为?
  • @jrh 有两个原因: 1. 传递标准库函数的地址(在这种情况下 std::isspacestd::all_of 2. [quote] 如果 std::isspace 的行为未定义参数的值既不能表示为 unsigned char 也不等于 EOF。为了安全地使用纯字符(或有符号字符)这些函数,应首先将参数转换为 unsigned char [/quote] confer (cppreferecence)
【解决方案6】:

这里只使用 STL(需要 C++11)

inline bool isBlank(const std::string& s)
{
    return std::all_of(s.cbegin(),s.cend(),[](char c) { return std::isspace(c); });
}

它依赖于如果字符串为空 (begin = end) std::all_of 也返回 true 的事实

这是一个小测试程序:http://cpp.sh/2tx6

【讨论】:

    【解决方案7】:

    这样使用 strtok 是不好的风格! strtok 修改它标记的缓冲区(它用\0替换分隔符)。

    这是一个非修改版本。

    const char* p = str.c_str();
    while(*p == ' ') ++p;
    return *p != 0;
    

    如果您在机器字块中迭代它,它可以进一步优化。为了便于携带,您还必须考虑对齐。

    【讨论】:

    • 我知道strtok() 破坏了缓冲区。并感谢您的回答。
    • 不需要两次测试。 while (*p == ' ') ++p; 在这里也可以正常工作。
    【解决方案8】:

    我不赞成你上面的 const_casting 和使用 strtok。

    一个 std::string 可以包含嵌入的空值,但我们假设它在你命中 NULL 终止符之前都是 ASCII 32 个字符。

    解决这个问题的一种方法是使用一个简单的循环,我将假设为 const char *。

    bool all_spaces( const char * v )
    {
       for ( ; *v; ++v )
       {
          if( *v != ' ' )
              return false;
       }
       return true;
    }
    

    对于较大的字符串,您可以一次检查一个单词,直到到达最后一个单词,然后假设 32 位单词(比如)将是 0x20202020,这可能会更快。

    【讨论】:

    • 每次检查单词时要小心。字符串的开头可能不一定在单词边界上。在许多架构上,它会产生故障,而在其他架构上,它会显着降低性能。
    • 如果字符串的开头是用 malloc 或 new 分配的,那么它总是在单词边界上,就像它发生的那样。通常,您不能用 std::string 保证它,因为即使您使用自己的自定义分配器,也不能保证以这种方式分配数据,因为某些字符串实现具有内部缓冲区。显然,如果您进行一次单词检查,您会在字符串的开头和结尾进行必要的一次字节检查
    【解决方案9】:

    类似:

    return std::find_if(
                str.begin(), str.end(),
                std::bind2nd( std::not_equal_to<char>(), ' ' ) )
        == str.end();
    

    如果您对空白感兴趣,而不仅仅是空格字符, 那么最好的办法就是定义一个谓词并使用它:

    struct IsNotSpace
    {
        bool operator()( char ch ) const
        {
            return ! ::is_space( static_cast<unsigned char>( ch ) );
        }
    };
    

    如果你正在做任何文本处理,那么简单的集合 谓词将是无价的(而且它们很容易生成 自动从&lt;ctype.h&gt; 中的函数列表中获取)。

    【讨论】:

      【解决方案10】:

      您不太可能为此击败编译器优化的朴素算法,例如

      string::iterator it(str.begin()), end(str.end())    
      for(; it != end && *it == ' '; ++it);
      return it == end;
      

      编辑:实际上 - 有一种更快的方法(取决于字符串的大小和可用的内存)..

      std::string ns(str.size(), ' '); 
      return ns == str;
      

      编辑:实际上上面并不快..它很愚蠢...坚持天真的实现,优化器将无处不在...

      再次编辑:该死,我想最好看看std::string中的函数

      return str.find_first_not_of(' ') == string::npos;
      

      【讨论】:

        【解决方案11】:

        我在编程作业中遇到了类似的问题,这是我在查看其他人后提出的另一种解决方案。在这里,我只是创建一个没有新空格的新句子。如果有双空格,我会忽略它们。

        字符串句子; 字符串消息; //重构新句子 字符串 dbl = " ";

        getline(cin, sentence);
        
        int len = sentence.length();
        
        for(int i = 0; i < len; i++){
        
        //if there are multiple whitespaces, this loop will iterate until there are none, then go back one.
            if (isspace(sentence[i]) && isspace(sentence[i+1])) {do{ 
                i++;
            }while (isspace(sentence[i])); i--;} //here, you have to dial back one to maintain at least one space.  
            
            
            newsent +=sentence[i];
        }       
        

        cout

        【讨论】:

          【解决方案12】:

          嗯...我会这样做:

          for (auto i = str.begin(); i != str.end() ++i)
              if (!isspace(i))
                 return false;
          

          伪代码,isspace 位于 C++ 的 cctype 中。

          编辑:感谢 James 指出 isspace 在有符号字符上的行为未定义。

          【讨论】:

          • auto 是 C++0x。 OP没有说明使用了这个。请,除非问题被标记为 C++0x,否则不要只使用尚未正式发布的东西。至少在你的回答中提到它,以免其他人感到困惑。
          • 这就是为什么我说它是伪代码。我从来没有遍历过这样的字符串,我也不知道“正确”的类型,即使这听起来有点傻。
          • 另外,在&lt;ctype.h&gt; 中使用isspace char 是未定义的行为。您必须先将 char 转换为 unsigned char
          【解决方案13】:

          如果你使用CString,你可以这样做

          CString myString = "    "; // All whitespace
          if(myString.Trim().IsEmpty())
          {
              // string is all whitespace
          }
          

          这有利于修剪所有换行符、空格和制表符。

          【讨论】:

            猜你喜欢
            • 2015-04-25
            • 2014-04-11
            • 1970-01-01
            • 1970-01-01
            • 2014-05-08
            • 2010-10-25
            • 2020-06-11
            • 2021-11-06
            相关资源
            最近更新 更多