0

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....

10
  • 2
    Works fine, show your complete code. Commented Aug 12, 2015 at 5:44
  • Sorry for a first comment about more complete code. I think (but will have to check) that the issue is that a temporary is constructed from the literal, and a reference to that temporary is then used when the temporary has been destroyed. Commented Aug 12, 2015 at 5:45
  • No, the temporary idea doesn't hold up (there is such a problem with std::pair, but it doesn't manifest here). Please post complete code, and info about your compiler and the build command. Commented Aug 12, 2015 at 5:53
  • 1
    Actually I'm thinking there's just a difference in S_HELLO as compiled and the localized version in his data file... could be some encoding thing. Might need to view the binary for the string. To test this theory, just insert "S_HELLO" into the map right before dumping the map, and see if it comes out twice. This would prove there's some invisible difference. Commented Aug 12, 2015 at 5:55
  • Thought 1: Make sure 0 is not O. Thought 2: See if std::string("S_HELLO") equals what you believe is the key that it should match. Commented Aug 12, 2015 at 6:04

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.