【问题标题】:Best way to compare std::strings比较 std::strings 的最佳方法
【发布时间】:2011-01-23 04:33:58
【问题描述】:

比较std::strings 的最佳方法是什么?显而易见的方法是使用if/else

std::string input;
std::cin >> input;

if ( input == "blahblahblah" )
{
    // do something.
}

else if ( input == "blahblah" )
{
    // do something else.
}

else if ( input == "blah" )
{
    // do something else yet.
}

// etc. etc. etc.

另一种可能性是使用std::mapswitch/case。在进行大量(例如 8、10、12 次以上)这些比较时,最好的方法是什么?

【问题讨论】:

  • 是的,只需使用从字符串到函数的映射。
  • @Ben 你能发布一个例子作为答案吗?

标签: c++ string comparison io string-comparison


【解决方案1】:

这是一个使用 std::map 的示例。

#include <map>
#include <string>
#include <iostream>
#include <utility>

void first()
{
  std::cout << "first\n";
}

void second()
{
  std::cout << "second\n";
}

void third()
{
  std::cout << "third\n";
}


int main()
{
  typedef void(*StringFunc)();
  std::map<std::string, StringFunc> stringToFuncMap;

  stringToFuncMap.insert(std::make_pair("blah", &first));
  stringToFuncMap.insert(std::make_pair("blahblah", &second));
  stringToFuncMap.insert(std::make_pair("blahblahblah", &third));

  stringToFuncMap["blahblah"]();
  stringToFuncMap["blahblahblah"]();
  stringToFuncMap["blah"]();
}

输出是:

second
third
first

这种方法的好处是:

  • 它易于扩展。
  • 它迫使您将字符串处理例程分解为单独的函数(有意编程)。
  • 函数查找是 O(log n),而您的示例是 O(n)

考虑使用 boost::function 使语法更好一些,尤其是类成员函数。

【讨论】:

  • 这看起来不错。但是有一个问题,你为什么用stringToFuncMap.insert(std::make_pair("blah", &amp;first)); 而不是stringToFuncMap["blah"] = &amp;first
  • operator[] 可以很好地初始化地图。我只是习惯于使用 insert 方法初始化地图。它的效率略高,因为 operator[] 将首先创建一个 std::pair 并初始化“第二个”默认值。我认为 Meyers 的“Effective STL”深入探讨了这一点。
【解决方案2】:

使用operator== 非常好,但如果性能真的很关键,你可以根据你的用例来改进它。如果目标是选择几个选项之一并执行特定操作,您可以使用TRIE。此外,如果字符串足够不同,您可以执行以下操作:

switch(s[0]) {
case 'a':
    // only compare to strings which start with an 'a'
    if(s == "abcd") {

    } else if (s == "abcde") {

    }
    break;
case 'b':
    // only compare to strings which start with an 'b'
    if(s == "bcd") {

    } else if (s == "bcde") {

    }
    break;
default:
    // we know right away it doesn't match any of the above 4 choices...
}

基本上使用字符串中具有良好唯一性的某个字符(如果所有字符串的长度至少为 N,则不必是第一个字符,N 之前的任何字符都可以!)做一个switch 然后做一个系列of 比较与该独特特征匹配的字符串子集

【讨论】:

    【解决方案3】:

    “12”并不多……但无论如何。

    您只能将switch 用于整数类型(charint 等),因此对于std::string 来说这是不可能的。使用地图可能更具可读性。

    再说一遍,这完全取决于您如何定义“最佳”。

    【讨论】:

    • 他们是否曾经修复过 c++0x 以使 hash<:string>("foo") 成为 constexpr?然后您可以打开字符串的哈希值。
    • @KitsuneYMG 即使这样,如果发生碰撞怎么办?您最终会得到多个具有相同值的标签。我认为散列值不会成为开关的好标签。
    • @EtiennedeMartel 您可以通过static_assertassert 进行确认。
    【解决方案4】:

    这个问题的答案完全取决于问题。你列举了两个例子。您可以在选项中添加诸如哈希表、正则表达式等内容...

    【讨论】:

      【解决方案5】:

      通过 8 次、10 次甚至 12 次比较,您仍然可以使用 if ... else if ... 方案,这没什么不好。如果你想要 100 或其他东西,我建议编写一个函数来计算字符串的哈希值(即使通过简单的异或所有字符,但其他一些好的方法会更好地分配),然后将其结果切换为 Evan建议的。如果函数为所有可能的输入字符串返回唯一的数字——那就更好了,不需要额外的比较。

      【讨论】:

        【解决方案6】:

        如果您的意思是“最好”的“最有效”,请继续阅读。

        如果真的很多,我建议使用以下方法。
        Switch 中的字符串实际上将在 Java 7 中出现。(作为Project Coin 的一部分)

        根据proposal,这就是Java语言实现它的方式。
        首先,计算每个字符串的哈希值。那么这个问题就是一个“switch int”问题,它在大多数当前语言中都是可用的,并且是有效的。在每个 case 语句中,然后检查这是否真的是字符串(在极少数情况下,不同的字符串可能会散列到相同的 int)。
        我个人有时不会在实践中做最后一步,因为它的必要性取决于您特定程序所处的情况,即可能的字符串是否在程序员的控制之下以及程序需要有多健壮。

        对应的示例伪代码

        String s = ...
        switch(s) {
         case "quux":
            processQuux(s);
            // fall-through
        
          case "foo":
          case "bar":
            processFooOrBar(s);
            break;
        
          case "baz":
             processBaz(s);
            // fall-through
        
          default:
            processDefault(s);
            break;
        }
        

        来自the fore-mentioned proposal,帮助你理解。

        // Advanced example
        {  // new scope for synthetic variables
          boolean $take_default = false;
          boolean $fallthrough = false;
          $default_label: {
              switch(s.hashCode()) { // cause NPE if s is null
              case 3482567: // "quux".hashCode()
                  if (!s.equals("quux")) {
                      $take_default = true;
                      break $default_label;
                  }
                  processQuux(s);
                  $fallthrough = true;
                        case 101574: // "foo".hashCode()
                  if (!$fallthrough && !s.equals("foo")) {
                      $take_default = true;
                      break $default_label;
                  }
                  $fallthrough = true;
              case 97299:  // "bar".hashCode()
                  if (!$fallthrough && !s.equals("bar")) {
                      $take_default = true;
                      break $default_label;
                  }
                  processFooOrBar(s);
                  break;
        
              case 97307: // "baz".hashCode()
                  if (!s.equals("baz")) {
                      $take_default = true;
                      break $default_label;
                  }
                  processBaz(s);
                  $fallthrough = true;
        
              default:
                  $take_default = true;
                  break $default_label;
              }
          }
          if($take_default)
              processDefault(s);
        }
        

        【讨论】:

        • 问题是针对 c++ (std::string) :)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-16
        • 2018-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多