尝试从std :: map对象访问密钥时c ++应用程序崩溃

时间:2015-08-12 05:37:11

标签: c++ dictionary crash

首先,我写了一个模板日志函数如下:

void Utils::_logMap(std::map<K, V> map) {
    cout << "===================[map]=====================\n";
    for(auto it: map) {
        auto key = it.first;
        auto val = it.second;
        cout << key << " = " << val << endl;
    }

   // testing code
   cout << "\n>>> for testing: \n";
   cout << map.at("S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE") << endl;
   cout << map.at("S_HELLO") << endl;
   cout << map.at("S_TEST") << endl;
}

然后我创建一个std :: map对象,并从文件中读取文本内容(使用UTF-8编码的本地化语言txt文件)。

static std::map<string, string> localizedStrings;

然后我打印出它的所有键值。

Utils::_logMap(localizedStrings);

结果显示:

===================[map]=====================
S_HELLO = hello123
S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE = teest1312
S_TEST = Test777

>>> for testing:
teest1312
hello123
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: map::at:  key not found

最后一行out_of_range异常是由此代码引起的:

cout << map.at("S_HELLO") << endl; // for testing, app will crash

但这怎么可能!!?!? map对象确实包含一个名为S_HELLO的键。当我尝试通过键入常量字符串值来访问密钥时,为什么应用程序会出现异常!?我不明白!

更新: 好吧,这是主要的阅读内容功能代码:

string Utils::getLocalizedString(const string key) {
    LanguageType type = Application::getInstance()->getCurrentLanguage();
    const char* code = Application::getInstance()->getCurrentLanguageCode();
    cclog("language type: %d, code: %s", type, code);
    const char * fileName;
    switch (type) {
        case LanguageType::ENGLISH:
            fileName = "Localized_en.txt";
            break;
        case LanguageType::CHINESE:
            fileName = "Localized_zh.txt";
            break;
        default:
            fileName = "Localized_en.txt";
            break;
    }

    if (localizedStrings.empty()) {
        // Initialize variables needed
        ssize_t fileSize = 0;
        unsigned char * fileContents = NULL;
        string line, fullPath, contents;

        // Get absolute path of file
        fullPath = FileUtils::getInstance()->fullPathForFilename( fileName );
        cout << "fullPath: " << fullPath << endl;

        // Get data of file
        if( !fullPath.empty() ) {
            fileContents = CCFileUtils::getInstance()->getFileData( fullPath.c_str( ) , "rb", &fileSize );
            cout << "fileContents: " << fileContents << endl;
            contents.assign(fileContents,fileContents+fileSize-1);

            // Create a string stream so that we can use getline( ) on it
            istringstream fileStringStream( contents );

            // Get file contents line by line
            while ( std::getline( fileStringStream, line ) )
            {
                //filter the valid string of one line
                if (line.find("/*",0) == string::npos && line.find("//",0) == string::npos)                 {
                    std::string::size_type validPos= line.find('=',0);
                    if ( validPos != string::npos)
                    {
                        std::string keyStr = line.substr(0,validPos-1);
                        std::string subStr = line.substr(validPos+1,line.size()-1); // get valid string

                        //trim space
                        keyStr.erase(0, keyStr.find_first_not_of(" \t")); // remove head white-space
                        keyStr.erase(keyStr.find_last_not_of(" \t") + 1); // remove tail white-space

                        subStr.erase(0, subStr.find_first_not_of(" \t")); // remove head white-space
                        subStr.erase(subStr.find_last_not_of(" \t") + 1); // remove tail white-space

                        //trim \"
                        keyStr.erase(0, keyStr.find_first_not_of("\""));
                        keyStr.erase(keyStr.find_last_not_of("\"") + 1);
                        subStr.erase(0, subStr.find_first_not_of("\""));

                        //trim ; character and last \" character
                        subStr.erase(subStr.find_last_not_of(";") + 1);
                        subStr.erase(subStr.find_last_not_of("\"") + 1);

                        //replace line feed with \n
                        string::size_type pos(0);
                        string old_value("\\n");
                        if((pos=subStr.find(old_value))!=string::npos)
                        {
                            for(; pos!=string::npos; pos+=1)
                            {
                                if((pos=subStr.find(old_value,pos))!=string::npos)
                                {
                                    subStr.erase(pos, 2);
                                    subStr.insert(pos, 1, '\n');
                                }
                                else
                                    break;
                            }
                        }

                        localizedStrings.insert(std::pair<std::string, std::string>(keyStr,subStr));
                    }
                }
            }
        }

        //must delete fileContents
        if (fileContents!= NULL) {
            delete [] fileContents;
            fileContents = NULL;
        }
    }

    cout << "key: " + key << endl;
    logMap(localizedStrings);
    if( localizedStrings.find(key) != localizedStrings.end() ) {
        return localizedStrings.at(key);
    }
    cclog("return key instead");
    return key;
}

已更新 天啊,我发现它似乎与文件中文本的位置相关。只有FIRST行文件中的键值才会导致问题。 但我还是不知道为什么........

这是本地化字符串文件的内容:

S_TEST = Test777
S_HELLO = hello123
S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE = teest13124

见?如果我访问键S_HELLO和S_WARNINGxxx,它工作正常。但如果我访问密钥S_TEST,它将崩溃....

3 个答案:

答案 0 :(得分:1)

如果只有文件中的第一个键出现此问题,那么您的文件开头很可能是BOM(字节顺序标记)。这些是2个不可见的字节,许多Windows编辑器默认将这些字节插入UTF-8文件中。

使用Notepad ++删除BOM打开文件。在Encoding菜单中选择Encode in UTF-8 without BOM。然后再次保存文件。

答案 1 :(得分:0)

嗯......这个崩溃问题似乎是由于从第一行的本地化txt文件中读取了某些不可见的字符。

所以我做了一个解决方案:只需在第一行插入注释文本,然后解决问题。它可能是文件是UTF-8格式,因此它在文件开头包含一些UTF-8格式标头,不应该读入字符串映射。

// !!LEAVE THE FIRST LINE EMTPY!!
S_TEST = Test777
S_HELLO = hello123
S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE = Unable to put into werehouse

答案 2 :(得分:0)

map :: at和map :: []之间的区别在于它将检查该键是否存在于地图中。如果key不存在,则会抛出异常。 Operator []将在地图中添加新密钥。在您的情况下,地图不会崩溃但会抛出异常。将键添加到地图中或处理异常或使用operator []而不是at。