将 c++ 字符串映射到枚举以便获取用户输入

Mapping c++ string to an enum in order to take user input

本文关键字:获取 用户 输入 枚举 c++ 字符串 映射      更新时间:2023-10-16

我正在尝试设计一个类来表示一张牌,我决定使用枚举来表示等级和花色。但是,我需要能够从用户那里获取输入,不幸的是,不可能直接从 cin 获取枚举。

出于这个原因,我打算接受一个字符串并使用std::map将每个字符串映射到它所表示的枚举值(如本问题中所述)。我也不希望我的输入区分大小写,所以我创建了一个正则表达式,应该在匹配之前将所有字符转换为小写。

我想出的代码在这里:

istream& operator>>(istream& is, Card& d)
{
std::map<std::string,Card::Rank> mr;
std::map<std::string,Card::Suit> ms;
mr[std::regex("/two/i")] = Card::TWO;
mr[std::regex("/three/i")] = Card::THREE;
mr[std::regex("/two/i")] = Card::FOUR;
mr[std::regex("/two/i")] = Card::FIVE;
mr[std::regex("/two/i")] = Card::SIX;
mr[std::regex("/two/i")] = Card::SEVEN;
mr[std::regex("/two/i")] = Card::EIGHT;
mr[std::regex("/two/i")] = Card::NINE;
mr[std::regex("/two/i")] = Card::TEN;
mr[std::regex("/two/i")] = Card::JACK;
mr[std::regex("/two/i")] = Card::QUEEN;
mr[std::regex("/two/i")] = Card::KING;
ms[std::regex("/clubs/i")] = Card::CLUBS;
ms[std::regex("/diamonds/i")] = Card::DIAMONDS;
ms[std::regex("/hearts/i")] = Card::HEARTS;
ms[std::regex("/spades/i")] = Card::SPADES;
string srank, ssuit;
char c1;
if (is >> srank >> c1 >> ssuit)
{
    if (c1 == 'of')
    {
        Card::Rank rank = mr[srank];
        Card::Suit suit = ms[ssuit];
        d = Card(rank, suit);
    }
    else
    {
       is.clear(ios_base::failbit);
    }
}
return is;
}

但是,在编译时我收到此错误:

error C2679: binary '[' : no operator found which takes a right-hand operand of type 'std::basic_regex<_Elem>' (or there is no acceptable conversion)
      with
      [
          _Elem=char
      ]
      c:program files (x86)microsoft visual studio 11.0vcincludemap(173): could be 'Card::Rank &std::map<_Kty,_Ty>::operator [](std::basic_string<_Elem,_Traits,_Alloc> &&)'
      with
      [
          _Kty=std::string,
          _Ty=Card::Rank,
          _Elem=char,
          _Traits=std::char_traits<char>,
          _Alloc=std::allocator<char>
      ]
      c:program files (x86)microsoft visual studio 11.0vcincludemap(190): or       'Card::Rank &std::map<_Kty,_Ty>::operator [](const std::basic_string<_Elem,_Traits,_Alloc> &)'
      with
      [
          _Kty=std::string,
          _Ty=Card::Rank,
          _Elem=char,
          _Traits=std::char_traits<char>,
          _Alloc=std::allocator<char>
      ]
      while trying to match the argument list '(std::map<_Kty,_Ty>, std::basic_regex<_Elem>)'
      with
      [
          _Kty=std::string,
          _Ty=Card::Rank
      ]
      and
      [
          _Elem=char
      ]
etc.

以前从未使用过地图,我真的不明白我做错了什么;从我设法挖掘的说明来看,这应该有效。

您声明mr是从字符串到等级的映射。您正在尝试使用regex作为密钥。但是正则表达式不是字符串。选择一个,然后始终如一地使用它。

基于您拥有的解决方案:

std::regex不是std::string,这些是完全不同的类型。我也怀疑,如果您可以使用std::regex作为地图键获得任何合理的结果。更重要的是,std::map 永远不会像你预期的那样工作。如果你想根据正则表达式列表测试一个字符串并选择匹配的字符串,你必须这样做:迭代所有正则表达式并测试每个正则表达式。如果您要放松这些正则表达式,这就是您所需要的。

更简单的方法是将所有读取字符串转换为小写,然后使用标准的字符串映射。像这样:

#include <cctype>
static inline void stringToLower(std::string &s) {
    for (uint i = 0; i < s.size(); ++i) {
        s[i] = tolower(s[i]);
    }
}
istream& operator>>(istream& is, Card& d)
{
    std::map<std::string,Card::Rank> mr;
    std::map<std::string,Card::Suit> ms;
    mr["two"] = Card::TWO;
    mr["three"] = Card::THREE;
    ...
    mr["king"] = Card::KING;
    ms["clubs"] = Card::CLUBS;
    ...
    string srank, ssuit, c1;
    if (is >> srank >> c1 >> ssuit)
    {
        stringToLower(c1);
        if (c1 == "of")
        {
            stringToLower(srank);
            stringToLower(ssuit);
            Card::Rank rank = mr[srank];
            Card::Suit suit = ms[ssuit];
            d = Card(rank, suit);
        }
        else
        {
            is.clear(ios_base::failbit);
        }
    }
    return is;
}

请注意,我将 c1 更改为字符串。在 yout 版本中,c1 将是 srank 之后的第一个字符(这将是一些空间)。此外,char值永远不能等于 'of''of' 的类型为 int)。我将其更改为 "of"(双引号),类型为 const char * .请记住,如果 c1 char*,它将无法正常工作。

顺便说一下,为每个<<调用分别创建mrms映射是非常低效的。考虑使用某种单例。可能的解决方案可能是这样的:

static std::map<std::string,Card::Rank> createmr() {
    std::map<std::string,Card::Rank> mr;
    mr["two"] = Card::TWO;
    mr["three"] = Card::THREE;
    ...
    mr["king"] = Card::KING;
    return mr;
}
static std::map<std::string,Card::Rank> createms() {
    std::map<std::string,Card::Rank> ms;
    ms["clubs"] = Card::CLUBS;
    ...
    return ms;
}
istream& operator>>(istream& is, Card& d)
{
    static std::map<std::string,Card::Rank> mr = createmr(); // note `static` keyword
    static std::map<std::string,Card::Suit> ms = createms();
    ...
}

说明:我在这里使用了static关键字,在两种不同的地方。一个是在函子声明之前。这意味着static,此函数在其他源文件中将不可用。我还在mrms声明中使用它。这意味着,这些变量对于定义它们的所有函数调用都是通用的(在本例中为 operator>> )。这样做的一个结果是,createmr() 和 createms() 只会在第一次调用运算符时被调用一次。

可能更好的方法是在函数外部定义mrms

static std::map<std::string,Card::Rank> mr = createmr(); // note `static` keyword
static std::map<std::string,Card::Suit> ms = createms();
istream& operator>>(istream& is, Card& d)
{
    ...
}

这里的关键字static意味着这些变量(现在是全局变量)将无法从此源文件外部访问。这里可以省略它,但最好标记static所有函数和全局变量,这些函数和全局变量不会在外部使用。