firestoke
firestoke

Reputation: 1059

c++ application crashed when trying to access the key from a std::map object

first, I write a template log function as the following:

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;
}

then I create a std::map object, and read the text content from a file(a localized language txt file with UTF-8 encoding).

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

then I print out all of its key-value.

Utils::_logMap(localizedStrings);

the result shows:

===================[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

the last line out_of_range exception is caused by this code:

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

but how can this be!!?!? the map object indeed contains a key named S_HELLO. why the app gets exception when I try to access the key via typing a constant string value!? I don't get it!

UPDATED: Well, this is the primary reading content function codes:

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;
}

UPDATED OMG, I found it seems to be relative with the position of text in the file. only the key-value in the FIRST line of file will cause the problem. but I still don't know why........

this is the content of the localized string file:

S_TEST = Test777
S_HELLO = hello123
S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE = teest13124

see? if I access the key S_HELLO and S_WARNINGxxx, it works fine. but if I access the key S_TEST, it will crash....

Upvotes: 0

Views: 1954

Answers (3)

Sleafar
Sleafar

Reputation: 1526

If you have this problem only with the first key in your file, then you have most likely a BOM (Byte Order Mark) at the beginning of your file. These are 2 invisible bytes, which are inserted by default in UTF-8 files by many Windows editors.

To remove the BOM open the file with Notepad++. In the Encoding menu select Encode in UTF-8 without BOM. Then save the file again.

Upvotes: 1

AlexTheo
AlexTheo

Reputation: 4184

the difference between map::at and map::[] is that at will check if the key exists in a map. If key does not exist it will throw an exception. Operator [] will add a new key in a map instead. In your case the map doesn't crash but throws exception. Either add a key into the map or handle exception or use operator [] instead of at.

Upvotes: 0

firestoke
firestoke

Reputation: 1059

Well... this crash problem seems to be due to reading certain invisible characters from the localized txt file at the first line.

so I do a work-around solution: just insert a comment text at first line, then problem solved. It might be the file is UTF-8 format, so it contains some UTF-8 format header at the beginning of file, and it should not be read into the string map.

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

Upvotes: 0

Related Questions