C++ - 如何提取字符串中的有效字符串?
问题:我想从使用C++的游戏名称中提取古代防御(DotA)的有效游戏模式。C++ - 如何提取字符串中的有效字符串?
详情:
- 游戏的名字就可以了,最多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 v6.60 -RDSOSP USA/CA LC!” - > “rdsosp”
“DOTA AREMDM美国LC” - > “aremdm”
“的DotA v6.60 -ApEmDuSpId美国BL” - > “apemduspid”
注:溶液没有按不一定必须提供实际的代码,伪代码,甚至只是解释你如何处理它是可以接受的和首选的。此外,解决方案需要足够灵活,以便我可以轻松添加其他游戏模式。假设在游戏名称中,游戏模式总是以主游戏模式开始也是安全的。
结果:
#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
如果有人想查看这段代码,并让我知道他们会改变什么,那将不胜感激。
谢谢。
如果没有更多规则,从主机的游戏名称中提取游戏类型将很困难。如果你真的想为了避免给最终用户更多的规则,你可以尝试以下...
- ToLower将()整个游戏名称字符串。
- 使用空格分隔符分隔游戏名称。
- 分析每个单词,请执行以下操作。如果有任何失败,请转到下一个单词。
- 取[0],并确定其是否具有的97-122(确保它是一个字母)的ASCII值。如果它不在这些值中,则转到下一个字符,直到它出现(显然不超过数组边界)。这将删除像连字符一样的任何用户格式。
- strcmp()接下来的每个主要游戏类型的2个字符,直到您匹配。否则失败并转移到下一个单词。
- 使用剩余的字符,strcmp每个下一对字符与每个辅助或misc游戏类型。如果有任何不匹配失败到下一个单词,或者如果只剩下1个字符,则失败到下一个单词
这应该提取游戏类型,或者您可以责怪用户使用糟糕的游戏名称。
现在对于更难的部分,验证游戏类型是否相互兼容。我建议你制作一个结构体,它保存代表每种次要游戏类型的布尔值数据结构。一个std :: map或一个可以使用enum访问的布尔数组。
现在你需要一个数据对象来表示每个主要游戏类型以及作为每个辅助游戏类型。
然后,只需制作一个包含主要和次要游戏类型的数组。请参阅代码示例:
map<const char*, bool> mapSecondaryCompatibility;
struct tGameType
{
char szName[3];
mapSecondaryCompatibility m_compat;
}
正如你看到的,有技术上是不是你的主要和次要的游戏类型之间的区别,它们都具有相同的限制......可以不与其他辅助游戏类型兼容。
有了这个,我相信你可以找出其余的。我希望它有帮助,我得回去工作:)
哦,我是DotA的忠实粉丝......继续努力!
创建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字符串转换为正确的数组索引进行测试。循环播放每种可能的组合并检查其有效性。我怀疑你认真关心这个设置的性能,所以这应该很好地工作。
对其他有效性检查(辅助辅助辅助功能)或其他兼容性规则的任何其他模式组合执行相同操作。
我会倾向于将游戏模式类型转换为枚举。我甚至可以将模式包装在一个可以存储当前游戏模式状态的类中,并为游戏的其他部分提供友好访问器以快速查询当前模式。
在内部我会创建一个std :: map < int,std :: vector < int>>来存储一系列可兼容模式。只要输入命令行,我会将两个字符串转换为枚举值。然后,我会在兼容模式映射中查看它是否是允许的模式。
你如何填写地图取决于你 - 我认为你可以有一个类来完成它 - 兼容性加载器,或者你可以从配置文件中驱动它,如果你想让最终用户能够修改可用的模式。
枚举会更好,因此您可以在编译时进行大量检查而不是运行时检查。当您从输入字符串转换为枚举时,您会执行一次运行时检查,并在用户无效时向用户返回失败消息。然后在编译时检查所有其他代码。
我可能会尝试将每个模式放入一个std :: set中,其中白名单与给定节点兼容。当你想解析一个模式字符串时,你需要制作一个主白名单的副本。然后你逐步穿过绳子。如果下一个节点不在白名单中,那么你有一个无效的模式字符串。如果下一个模式在白名单中,则与相交,将下一个节点的白名单与您的工作白名单相交。继续,直到到达字符串末尾或白名单为空。如果白名单是空的,而你不在字符串的末尾,那么它是无效的,否则它是好的。
misc模式可能不属于白名单(因为它们在每个白名单中)。
您也可以尝试,而不是使用黑名单并在每一步的方式创造一个工会,再想逃出来,如果模式是在列表中。
我完全不明白,你需要:1.从游戏名称中提取游戏模式。 2.验证它们是否相互兼容? – 2009-06-12 12:44:47
这是正确的。 – xian 2009-06-12 12:55:44