【问题标题】:Assign numbers to characters C++将数字分配给字符 C++
【发布时间】:2016-05-25 03:41:07
【问题描述】:

我需要一种在 C++ 中为字母分配数字的方法,例如,'$' 将表示数字 1。我显然需要能够使用类似函数的东西从字符中获取数字,例如getNumFromChar('$') 将返回 1,getNumFromChar('#') 将返回 2。在 C++ 中是否有一种简单快捷的方法来执行此操作?

【问题讨论】:

  • 你可以使用std::map
  • "$""#" 不是字符,它们是字符串(准确地说是字符串文字)
  • 您是否只是在寻找某些字符的表示? c/c++中的所有字符基本上都是数字。
  • 它们需要是特定的数字还是字符的自然代码可以吗?

标签: c++


【解决方案1】:

最快的方法是编写一个包含 256 个条目的查找表,其中包含字符的 ASCII 索引中的映射值。这类似于isdigittolower 的工作方式,例如:

int getNumFromChar(char c)
{
    static const int table[256] = {/* table entries */};

    return table[c & 0xff];
}

【讨论】:

  • 你可能想让表静态/constexpr。
【解决方案2】:

如果您想自己分配值,请使用映射并将您的键存储到字母组合。如果您对映射到每个字母的预分配唯一值感到满意,并且只使用 ASCII 字符,则将它们类型转换为整数...例如)std::static_cast('$');

【讨论】:

    【解决方案3】:

    创建一个向量std::vector<int> v(256,0);,它由您的字符索引,最初它们的所有数字都是零,您可以将其视为无效数字。最后为每个“编号”字符分配一些数字,例如v['$'] = 1; v['#'] = 2; 使用字符实际上是从 0 到 255 的整数这一事实。

    【讨论】:

      【解决方案4】:

      正如 cmets 中所指出的,您可以通过以下方式使用 std::map

      #include <iostream>
      #include <map>
      #include <cstring>
      
      struct myComp
      {
          bool operator()(const char* s1, const char* s2) const
          {
          return strcmp(s1, s2) < 0;
          }
      };
      
      int main()
      {
          std::map<const char*, int, myComp> test;
      
          test["$"] = 1;
          test["#"] = 2;
      
          std::cout << "$ -> " << test["$"] <<"\n";
          std::cout << "# -> " << test["#"] <<"\n";
      
          return 0;
      }
      

      现场演示here

      仅当您最多存储 256 个值时,大多数其他答案才有效。但是,使用Maps,您可以存储任意数量的元素。

      【讨论】:

      • 如果您知道您的密钥空间最多有 256 个条目,那么映射可能是最慢的解决方案。
      • @MikeMB,我同意,但我相信 Map 查找比 Vector 查找更可取(正如上面 Mikhail Volskiy 所建议的那样)。我的信念基础是this。 OP 本身并没有使用std::find,但他仍在查找。此外,OP 没有提到他只使用 256 个值。
      • std::find 和基于索引的查找是两个完全不相关的操作。 std::find 总是 O(n),而数组和向量的索引基查找是常数时间(实际上编译成一些东西。比如 1-3 条指令)。更重要的是,std::map 是一个缓存和流水线的噩梦,并且(在现代处理器上)这通常比算法复杂性具有更高的性能影响。如果您对不同数据结构上的操作性能感兴趣,我可以推荐this talk 作为起点。
      • 256 个字符来自假设 OP 实际上想要使用 ASCII 字符而不是字符串。如果不是这样,std::vector 可能仍然比std::map 更好,但不一定。
      【解决方案5】:

      很多人都建议std::map&lt;char,int&gt;,这很好并且有效,但是没有依赖关系的更快(?)方法是使用大量的 switch 语句

      int getNumFromChar(char c){
          switch(c){
              case '$':
                  return 1;
              case '#':
                  return 2;
              //etc
          }
          return -1; //just in case of error, for style points
      }
      

      根据您对性能/内存使用的关注程度以及您必须编写多少个案例语句,这可能是也可能不是实现您想要的可行方法。只是想我会把它扔在那里,因为在撰写本文时我不相信有人这样做。

      编辑:另外,取决于每个单独字符的使用频率,如果您在使用此功能之前知道整个映射,或者如果您曾经更改映射,std::map 会更好,但我相信这是否则更快。

      【讨论】:

        【解决方案6】:

        你可以这样做:

        #include <map>
        #include <iostream>
        #include <exception>
        
        typedef std::map<char, int> easymap_type;
        
        class EasyMap {
        public:
            EasyMap() {}
            virtual ~EasyMap() {}
        
            void assign_int_to_char(const int& i, const char& c)
            {
                _map[c] = i;
            }
        
            int get_int_from_char(const char& c) const
            {
                easymap_type::const_iterator it = _map.find(c);
                if (it == _map.end())
                {
                    std::cerr << "EasyMap Error: uninitialized key - '" << c << "'" << std::endl;
                    throw std::exception();
                }
                return it->second;
            }
        
        private:
            easymap_type _map;
        };
        
        int main()
        {
        
            EasyMap ezmap;
        
            ezmap.assign_int_to_char(42, 'a');
        
            std::cout << "EasyMap[a] = " << ezmap.get_int_from_char('a') << std::endl;
            std::cout << "EasyMap[b] = " << ezmap.get_int_from_char('b') << std::endl;
        
            return 0;
        }
        

        我通过抛出异常来处理未初始化的键,但您可以采用不同的方式。

        【讨论】:

          【解决方案7】:

          如果你的编译器支持c++11特性,你可以使用std::unordered_map作为容器来存储char和double,比如std::unordered_map&lt;char,double&gt;。 无序映射是一个关联容器,其中包含具有唯一键的键值对。元素的搜索、插入和删除具有平均常数时间复杂度。在您的问题中,char 是键,double 是您的值,char-double 必须是存储在容器中的键值。

          【讨论】:

            【解决方案8】:

            已经有很多合理的答案了……我更喜欢static_cast&lt;int&gt;('#')

            而且总是有关于如何解决问题的最愚蠢无用的编译时模板想法。

            我知道这很愚蠢,而且我不擅长这种事情,但这是我在 c++11 中的尝试。别把我当回事。我只是不得不做一些愚蠢的事情。

            #include <string>
            #include <array>
            #include <utility>
            #include <iostream>
            
            constexpr uint  kNbChars {3};
            
            constexpr std::array<std::pair<char, int>, kNbChars> kCharToInt {
                  std::make_pair('$', 1)
                , std::make_pair('#', 2)
                , std::make_pair('@', 3)
            };
            
            template <char c>
            int getInt()
            {
                for (auto pair : kCharToInt)
                {
                    if (pair.first == c)
                    { return pair.second; }
                }
                return -1;
            }
            
            int     main()
            {
                std::cout << getInt<'#'>() << std::endl;
                std::cout << getInt<'g'>() << std::endl;
            }
            

            我认为你也可以在 c++14 中制作 getInt() constexpr,但我可能错了,现在无法检查。

            当然它真的没用,因为你必须在编译时知道字母,但你可以通过不将 getInt 设为模板函数来解决这个问题......

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-05-03
              • 1970-01-01
              • 2010-10-09
              • 2015-09-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多