Reputation: 1059
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
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
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
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