如何确保用户输入允许的枚举

时间:2014-09-24 17:28:29

标签: c++ enums

我必须编写一个带有Enum状态的程序,这是50个2个字母的州缩写(NY,FL等)。我需要制作一个程序,询问用户信息,用户需要输入与该状态对应的2个字母。如何检查其输入是否有效,即与枚举状态{AL,...,WY}中定义的2个字母状态匹配?我想我可以制作一个巨大的if语句来检查输入==“AL”|| ...... || input ==“WY”{do stuff} else {错误输入与状态不匹配},但必须为所有50个状态执行此操作会有点荒谬。有更简单的方法吗?

此外,如果将枚举状态定义为{AL,NY,FL},我如何将用户输入(即字符串)转换为状态?如果我将状态更改为{“AL”,“NY”,“FL”}那会更容易还是有其他方法可以做到?

4 个答案:

答案 0 :(得分:1)

不幸的是,C ++没有提供将枚举转换为字符串的可移植方式,反之亦然。可能的解决方案是填充std::map<std::string,State>(或哈希映射),并在转换时进行查找。您可以手动填充此类映射,也可以创建一个简单的脚本,该脚本将在.cpp文件中生成一个函数,以填充此映射并在构建过程中调用该脚本。您的脚本可以生成if / else语句,而不是填充map。

另一个更困难但更灵活和更稳定的解决方案是使用像llvm这样的编译器并制作一个插件,它将根据编译器生成的语法树生成这样的函数。

答案 1 :(得分:0)

最简单的方法是使用STL std::map,但是对于可能不允许的学术练习(例如,可能只需要使用课程材料中涵盖的技术)。

除非明确初始化,否则枚举从零开始按顺序整数编号。鉴于此,您可以扫描字符串的查找表,并将匹配的索引转换为枚举。例如:

enum eUSstate
{
    AL, AK, AZ, ..., NOSTATE
} ;

eUSstate string_to_enum( std::string inp )
{
    static const int STATES = 50 ;
    std::string lookup[STATES] = { "AL", "AK", "AZ" ... } ;

    int i = 0 ;
    for( i = 0; i < STATES && lookup[i] != inp; i++ )
    {
        // do nothing
    }

    return static_cast<eUSstate>(i) ;
}

如果您不想依赖于强力转换并以与枚举相同的顺序维护查找表,则可以使用具有字符串和匹配枚举的查找表

eUSstate string_to_enum( std::string inp )
{
    static const int STATES = 50 ;
    struct
    {
        std::string state_string ;
        eUSstate state_enum ;

    } lookup[STATES] { {"AL", AL}, {"AK", AK}, {"AZ", AL} ... } ;

    eUSstate ret = NOSTATE ;
    for( int i = 0; ret == NOSTATE && i < STATES; i++ )
    {
        if( lookup[i].state_string == inp )
        {
            ret = lookup[i].state_enum ;
        }
    }

    return ret ;
}

可以通过利用字母排序和执行二进制搜索来优化查找,但对于50个州来说,这几乎是不值得的。

答案 2 :(得分:0)

你需要的是一张桌子。因为枚举是线性的, 一个简单的字符串表就足够了:

char const* const stateNames[] =
{
    //  In the same order as in the enum.
    "NY",
    "FL",
    //  ...
};

然后:

char const* const* entry 
        = std::find( std::begin( stateNames ), std::end( stateNames ), userInput );
if (entry == std::end( stateNames ) ) {
    //  Illegal input...
} else {
    State value = static_cast<State>( entry - std::begin( stateNames ) );

或者,你可以有一个数组:

struct StateMapping
{
    State enumValue;
    char const* name;
    struct OrderByName
    {
        bool operator()( StateMapping const& lhs, StateMapping const& rhs ) const
        {
            return std::strcmp( lhs.name, rhs. name ) < 0;
        }
        bool operator()( StateMapping const& lhs, std::string const& rhs ) const
        {
            return lhs.name < rhs;
        }
        bool operator()( std::string const& lhs, StateMapping const& rhs ) const
        {
            return lhs < rhs.name;
        }
    };
};
StateMapping const states[] = 
{
    { NY, "NY" },
    //  ...
};

按键排序,然后使用std::lower_bound

StateMapping::OrderByName cmp;
StateMapping entry =
        std::lower_bound( std::begin( states ), std::end( states ), userInput, cmp );
if ( entry == std::end( states ) || cmp( userInput, *entry) {
    //  Illegal input...
} else {
    State value = entry->enumValue;
    //  ...
}

后者可能稍微快一点,但只有50 条目,我怀疑你会注意到差异。

当然,你不要手动编写这段代码;你生成了 它有一个简单的脚本。 (过去,我有代码可以 解析枚举定义的C ++源代码,并生成 从它们映射功能。它比听起来更简单, 因为你可以忽略大块的C ++代码,除了 跟踪各种嵌套。)

答案 3 :(得分:0)

解决方案很简单,但只对字符串中的2个字符(如您的情况):

#include <stdio.h>
#include <stdint.h>

enum TEnum
{
    AL = 'LA',
    NY = 'YN',
    FL = 'LF'
};


 int _tmain(int argc, _TCHAR* argv[])
{

    char* input = "NY";
    //char* input = "AL";
    //char* input = "FL";



    switch( *(uint16_t*)input )
    {
        case AL:
            printf("input AL");
            break;
        case NY:
            printf("input NY");
            break;
        case FL:
            printf("input FL");
            break;
    }
    return 0;
}

在上面的例子中,我使用了带有双字符代码的枚举(它是合法的),并将一个输入字符串传递给switch语句。我测试了它的最终工作!请注意枚举中的单词alignment。

Ciao