代码之家  ›  专栏  ›  技术社区  ›  xian

C++:如何在字符串中提取有效字符串?

  •  3
  • xian  · 技术社区  · 15 年前

    问题是:

    细节:

    • 游戏名称最多可包含31个字符
    • 有三种游戏模式类别:主要、次要和杂项
      • 只能选择1个主要游戏模式
      • 某些主要游戏模式与某些次要游戏模式不兼容
      • 某些辅助游戏模式与其他辅助游戏模式不兼容
      • 其他游戏模式可与所有其他游戏模式组合

    以下是各种游戏模式的列表,图表显示了每种模式与哪些次要模式兼容(X表示不兼容):

    // Only 1 primary allowed
    static char *Primary[] = {
              // Compatible with > | dm | rv | mm | du | sh | aa | ai | as | id | em | np | sc | om | nt | nm | nb | ro | mo | sp | 
        "ap", // All Pick          |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "ar", // All Random        |    | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "tr", // Team Random       | X  | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "mr", // Mode Random       | X  | X  |    |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
        "lm", // League Mode       | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  |    |
        "rd", // Random Draft      | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
        "vr", // Vote Random       | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
        "el", // Extended League   | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  |    |
        "sd", // Single Draft      | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
        "cm", // Captains Mode     | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  |
        "cd"  // Captains Draft    | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
    };
    
    static char *Secondary[] = {
              // Compatible with > | dm | rv | mm | du | sh | aa | ai | as | id | em | np | sc | om | nt | nm | nb | ro | mo | sp | 
        "dm", // Death Match       |    | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
        "rv", // Reverse Mode      | X  |    |    |    | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "mm", // Mirror Match      | X  |    |    |    | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "du", // Duplicate Mode    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "sh", // Same Hero         | X  | X  | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "aa", // All Agility       | X  |    |    |    |    |    | X  | X  |    |    |    |    |    |    |    |    |    |    |    |
        "ai", // All Intelligence  | X  |    |    |    |    | X  |    | X  |    |    |    |    |    |    |    |    |    |    |    |
        "as", // All Strength      | X  |    |    |    |    | X  | X  |    |    |    |    |    |    |    |    |    |    |    |    |
        "id", // Item Drop         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "em", // Easy Mode         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "np", // No Powerups       |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "sc", // Super Creeps      |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | 
        "om", // Only Mid          |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "nt", // No Top            |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "nm", // No Middle         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
        "nb", // No Bottom         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | 
        "ro", // Range Only        | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | X  |    | 
        "mo", // Melee Only        | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | X  |    |    | 
        "sp"  // Shuffle Players   |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    };
    
    // These options are always available
    static char *Misc[] = {
        "ns", // No Swap
        "nr", // No Repick
        "ts", // Terrain Snow
        "pm", // Pooling Mode
        "oi", // Observer Info
        "mi", // Mini Heroes
        "fr", // Fast Respawn
        "so"  // Switch On
    };
    

    示例:

    “DOTA AREMDM美国信用证”->“aremdm”

    “DotA v6.60-美国ApEmDuSpId BL”->“Apemdupid”

    笔记: 解决方案不一定要提供实际代码、伪代码,甚至只是解释如何处理它也是可以接受和首选的。此外,解决方案需要足够灵活,我可以添加另一个游戏模式相当容易。同样可以安全地假设,在游戏名称中,游戏模式始终以主游戏模式开始。


    结果:

    #include <cstdarg>
    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <map>
    #include <vector>
    
    std::map<std::string, std::vector<std::string> > ModeCompatibilityMap;
    
    static const unsigned int PrimaryModesCount = 11;
    static char *PrimaryModes[] = { 
        "ap", "ar", "tr", "mr", "lm", "rd", "vr", "el", "sd", "cm", "cd"
    };
    
    static const unsigned int SecondaryModesCounts = 19;
    static char *SecondaryModes[] = {
        "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np",
        "sc", "om", "nt", "nm", "nb", "ro", "mo", "sp"
    };
    
    static const unsigned int MiscModesCount = 8;
    static char *MiscModes[] = {
        "ns", "nr", "ts", "pm", "oi", "mi", "fr", "so" 
    };
    
    std::vector<std::string> Vectorize( int count, ... ) {
        std::vector<std::string> result;
    
        va_list vl;
        va_start( vl, count );
    
        for ( int i = 0; i < count; ++i ) {
            char *buffer = va_arg( vl, char * );
            result.push_back( buffer );
        }
    
        va_end( vl );
    
        return result;
    }
    
    void InitializeModeCompatibilityMap() {
        // Primary
        ModeCompatibilityMap["ar"] = Vectorize( 1, "rv" );
        ModeCompatibilityMap["tr"] = Vectorize( 2, "dm", "rv" );
        ModeCompatibilityMap["mr"] = Vectorize( 8, "dm", "rv", "sh", "aa", "ai", "as", "ro", "mo" );
        ModeCompatibilityMap["lm"] = Vectorize( 18, "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np", "sc", "om", "nt", "nm", "nb", "ro", "mo" );
        ModeCompatibilityMap["rd"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
        ModeCompatibilityMap["vr"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
        ModeCompatibilityMap["el"] = Vectorize( 18, "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np", "sc", "om", "nt", "nm", "nb", "ro", "mo" );
        ModeCompatibilityMap["sd"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
        ModeCompatibilityMap["cm"] = Vectorize( 19, "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np", "sc", "om", "nt", "nm", "nb", "ro", "mo", "sp" );
        ModeCompatibilityMap["cd"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );   
        // Secondary
        ModeCompatibilityMap["dm"] = Vectorize( 8, "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
        ModeCompatibilityMap["rv"] = Vectorize( 2, "dm", "sh" );
        ModeCompatibilityMap["mm"] = Vectorize( 2, "dm", "sh" );
        ModeCompatibilityMap["sh"] = Vectorize( 3, "dm", "rv", "mm" );
        ModeCompatibilityMap["aa"] = Vectorize( 3, "dm", "ai", "as" );
        ModeCompatibilityMap["ai"] = Vectorize( 3, "dm", "aa", "as" );
        ModeCompatibilityMap["as"] = Vectorize( 3, "dm", "aa", "ai" );
        ModeCompatibilityMap["ro"] = Vectorize( 2, "dm", "mo" );
        ModeCompatibilityMap["mo"] = Vectorize( 2, "dm", "ro" );
    }
    
    std::vector<std::string> Tokenize( const std::string &string ) {
        std::vector<std::string> tokens;
        std::string token;
        std::stringstream ss( string );
    
        while ( ss >> token ) {
            tokens.push_back( token );
        }
    
        return tokens;
    }
    
    void SanitizeString( std::string &in ) {
        std::transform( in.begin(), in.end(), in.begin(), tolower );
    
        for ( size_t i = 0; i < in.size(); ++i ) {
            if ( in[i] < 'a' || in[i] > 'z' ) {
                in.erase( i--, 1 );
            }
        }
    }
    
    std::vector<std::string> SplitString( const std::string &in, int count ) {
        std::vector<std::string> result;
    
        if ( in.length() % count != 0 ) {
            return result;
        }
    
        for ( std::string::const_iterator i = in.begin(); i != in.end(); i += count ) {
            result.push_back( std::string( i, i + count ) );
        }
    
        return result;
    }
    
    bool IsPrimaryGameMode( const std::string &in ) {
        for ( int i = 0; i < PrimaryModesCount; ++i ) {
            if ( strcmp( PrimaryModes[i], in.c_str() ) == 0 ) {
                return true;
            }
        }
    
        return false;
    }
    
    bool IsSecondaryGameMode( const std::string &in ) {
        for ( int i = 0; i < SecondaryModesCounts; ++i ) {
            if ( strcmp( SecondaryModes[i], in.c_str() ) == 0 ) {
                return true;
            }
        }
    
        return false;
    }
    
    bool IsMiscGameMode( const std::string &in ) {
        for ( int i = 0; i < MiscModesCount; ++i ) {
            if ( strcmp( MiscModes[i], in.c_str() ) == 0 ) {
                return true;
            }
        }
    
        return false;
    }
    
    bool IsValidGameMode( std::string in, std::string &out ) {
        // 1. Strip all non-letters from the string and convert it to lower-case
        SanitizeString( in );
    
        // 2. Confirm that it is a multiple of 2
        if ( in.length() == 0 || in.length() % 2 != 0 ) {
            return false;
        }
    
        // 3. Split the string further into strings of 2 characters
        std::vector<std::string> modes = SplitString( in, 2 );
    
        // 4. Verify that each game mode is a valid game mode and is compatible with the others
        bool primaryModeSet = false;
    
        for ( size_t i = 0; i < modes.size(); ++i ) {
            if ( IsPrimaryGameMode( modes[i] ) || IsSecondaryGameMode( modes[i] ) ) {
                if ( IsPrimaryGameMode( modes[i] ) ) {
                    if ( primaryModeSet ) {
                        return false;
                    }
    
                    primaryModeSet = true;
                }
    
                if ( ModeCompatibilityMap.count( modes[i] ) > 0 ) {
                    std::vector<std::string> badModes = ModeCompatibilityMap[modes[i]];
    
                    for ( size_t j = 0; j < badModes.size(); ++j ) {
                        for ( size_t k = 0; k < modes.size(); ++k ) {
                            if ( badModes[j] == modes[k] ) {
                                return false;
                            }
                        }
                    }
                }
            } else if ( !IsMiscGameMode( modes[i] ) ) {
                return false;
            }
        }
    
        // 5. Assign the output variable with the game mode and return true
        out = in;
    
        return true;
    }
    
    std::string ExtractGameMode( const std::string &gameName ) {
        std::vector<std::string> tokens = Tokenize( gameName );
    
        std::string gameMode;
    
        for ( size_t i = 0; i < tokens.size(); ++i ) {
            if ( IsValidGameMode( tokens[i], gameMode ) ) {
                return gameMode;
            }
        }
    
        return "";
    }
    
    int main( int argc, char *argv[] ) {
        InitializeModeCompatibilityMap();
    
        std::string gameName = "DotA v6.60 -RDEM USA/CA LC";
        std::string gameMode = ExtractGameMode( gameName );
    
        std::cout << "Name: " << gameName << std::endl;
        std::cout << "Mode: " << gameMode << std::endl;
    
        return 0;
    }
    

    名称:DotA v6.60-RDEM USA/CA LC

    模式:rdem


    如果有人想查看此代码,并让我知道他们将更改什么,我们将不胜感激。

    谢谢

    4 回复  |  直到 15 年前
        1
  •  2
  •   Doug T.    15 年前

    创建bool数组,该数组复制已放入注释中的表。除“X”或空白外,输入“真”或“假”(因此“真”表示模式组合有效,“假”表示无效)。

    使用此表查找组合是否有效:

     bool IsSecondaryValidWithPrimary(unsigned int primaryIndex, unsigned int secondaryIndex)
     {
         static bool secondaryValidWithPrimary[numPrimaryModes][numSecondaryModes] = {...}
    
         if (primaryIndex < numPrimaryModes && secondaryIndex < numSecondaryModes)
         {
             return secondaryValidWithPrimary[primaryIndex][secondaryIndex]
         }
         else
         {
             //... this should never happen, throw your favorite exception
         }
     }
    

    对其他有效性检查(二级与另一二级)或具有兼容性规则的任何其他模式组合执行相同的操作。

        2
  •  1
  •   Dolphin    15 年前

    我可以尝试将每个模式放入一个带有 白名单 横断 下一个节点的白名单和工作白名单。继续,直到到达字符串末尾或白名单为空。如果白名单为空,并且您不在字符串末尾,则白名单无效,否则为好。

    misc模式可能不属于白名单(因为它们在每个白名单上)。

    您也可以尝试使用 黑名单 协会 在每个步骤中,如果模式在列表中,则退出。

        3
  •  1
  •   Game_Overture    15 年前

    真正地 要避免给最终用户更多的规则,您可以尝试以下方法。。。

    • ToLower()整个游戏名称字符串。
    • 使用空格“”分隔符拆分游戏名称。
    • 分析每个单词,做以下操作。如果有什么失败,请转到下一个单词。

    • strcmp()将接下来的2个字符与每个主要游戏类型匹配,直到找到匹配的字符。否则失败并进入下一个单词。

    这应该提取游戏类型,否则您可以责怪用户使用了错误的游戏名称。


    现在,更难的部分是验证游戏类型是否相互兼容。我建议您创建一个结构,其中包含一个布尔数据结构,表示每种次要游戏类型。可以使用枚举访问的std::map或单个布尔数组。

    现在需要一个数据对象来表示每个主要游戏类型 作为每个次要游戏类型。

    然后只需为每种游戏类型制作一个数组,包括主要游戏类型和次要游戏类型。请参阅代码示例:

    map<const char*, bool> mapSecondaryCompatibility;
    
    struct tGameType
    {
        char szName[3];
        mapSecondaryCompatibility m_compat;
    }
    

    正如你所看到的,从技术上讲,你的主要和次要游戏类型没有区别,它们都有相同的限制。。。可能与其他辅助游戏类型不兼容。

    哦,我是DotA的超级粉丝。。。继续努力!

        4
  •  0
  •   Kieveli    15 年前

    在内部,我将创建一个std::map<int,std::vector<int>&燃气轮机;存储可兼容模式的列表。一旦输入命令行,我就会将这两个字符串转换为枚举值。然后我会在兼容模式映射中进行查找,看看它是否是允许的模式。

    如何填充地图取决于您-我认为您可以有一个类来完成它-一个兼容性加载程序,或者如果希望最终用户能够修改可用模式,您可以从配置文件中驱动它。

    使用枚举会更好,这样您就可以在编译时执行大量检查,而不是运行时检查。当从输入字符串转换为枚举时,执行一次运行时检查,如果无效,则向用户返回失败消息。然后在编译时检查所有其他代码。