How can I find the type of the value present in vector of type string? - c++

I have a config file:
#X,Name,hostid,processid,executecommand,Max Runs, Max failure time
X,Main_XYZ_service,1,1,/opt/bada/bin,3,6,300
I parsed the above config file and stored each of the values in a vector of type string.
This is stored in vector<string> scanned:
//scanned[0]=X
//scanned[1]=Main_XYZ_service.........
long H_hostid = atoi(scanned[5].c_str());
how can I detect the type of the elements present in the vector?
If I call atoi() with a string that doesn't have a number, atoi() returns 0, but it also returns 0 if the string contains the number 0. How can I correctly assign a value to H_hostid?

In an absolute sense, you can't. If you encounter the string "0", you
can't know whether the user intended a string, an integer or a floating
point value. On the other hand, if you know what you need, you can try
to convert (say by using boost::lexical_cast), and generate an error
if it doesn't match. Alternatively, you can use regular expressions for
pattern matching, and decide what type you want as a result of what
pattern matches.
For a configuration file, I'd suggest the former. Just keep everything
as a string until you know what you need, then try the conversion (using
something reasonable for the conversion, which will report an error, and
not atoi).

Don't use atoi() - as you say, there is no way to detect errors. Use std::istringstream in C++, or strtol() in C.
long H_hostid;
std::istringstream stream(scanned[5]);
if (!(stream >> H_hostid)) {
// handle error
}
You could also use boost::lexical_cast, which does the same thing as that example, and throws an exception if the conversion fails.

If this is the data stored as a single string:
X,Main_XYZ_service,1,1,/opt/bada/bin,3,6,300
then the solution is, split this string using , as separator, and store each token in an array of size 8, then you can interpret each token based on the index as shown below:
char,string, int, int, string, int, int, int
0 1 2 3 4 5 6 7
Code would look like this:
std::string s = "X,Main_XYZ_service,1,1,/opt/bada/bin,3,6,300";
std::vector<std::string> tokens = split(s); //write the function yourself!
char c = tokens[0]; //token with index 0 is char
std::string service = tokens[1]; //token with index 1 is string
std::string path = tokens[4]; //token with index 4 is string
int int_indices[] = {2,3,5,6,7}; //rest of the index has int : total 5
for(int i = 0 ; i < 5 ; i++ )
{
try
{
int value = boost::lexical_cast<int>(tokens[int_indices[i]]);
//store value
}
catch(const bad_lexical_cast &)
{
std::cout << tokens[int_indices[i]] <<" is not an int" << std::endl;
}
}

Whenever you write a config file to be used by your application, you know in advance in what order the values will appear in that file. Otherwise, an xml or key value coding will be a better option to write a config file for general case. Personally, I would never create a config file as you have shown in your example.

Related

In c++, how do you get the input of a string, float and integer from 1 line?

An input file is entered with the following data:
Juan Dela Cruz 150.50 5
'Juan Dela Cruz' is a name that I would like to assign to string A,
'150.50' is a number I would like to assign to float B
and 5 is a number I would like to assign to int C.
If I try cin, it is delimited by the spaces in between.
If I use getline, it's getting the whole line as a string.
What would be the correct syntax for this?
If we analyze the string, then we can make the following observation. At the very end, we have an integer. In front of the integer we have a space. And in front of that the float value. And again in fron of that a space.
So, we can simply look from the back of the string for the 2nd last space. This can easily be achieved by
size_t position = lineFromeFile.rfind(' ', lineFromeFile.rfind(' ')-1);
We need a nested statement of rfind please see here, version no 3.
Then we build a substring with the name. From start of the string up to the found position.
For the numbers, we put the rest of the original string into an std::istringstream and then simply extract from there.
Please see the following simple code, which has just a few lines of code.
#include <iostream>
#include <string>
#include <cctype>
#include <sstream>
int main() {
// This is the string that we read via getline or whatever
std::string lineFromeFile("Juan Dela Cruz 150.50 5");
// Let's search for the 2nd last space
size_t position = lineFromeFile.rfind(' ', lineFromeFile.rfind(' ')-1);
// Get the name as a substring from the original string
std::string name = lineFromeFile.substr(0, position);
// Put the numbers in a istringstream for better extraction
std::istringstream iss(lineFromeFile.substr(position));
// Get the rest of the values
float fValue;
int iValue;
iss >> fValue >> iValue;
// Show result to use
std::cout << "\nName:\t" << name << "\nFloat:\t" << fValue << "\nInt:\t" << iValue << '\n';
return 0;
}
Probably simplest in this case would be to read whole line into string and then parse it with regex:
const std::regex reg("\\s*(\\S.*)\\s+(\\d+(\\.\\d+)?)\\s+(\\d+)\\s*");
std::smatch match;
if (std::regex_match( input, match, reg)) {
auto A = match[1];
auto B = std::stof( match[2] );
auto C = std::stoi( match[4] );
} else {
// error invalid format
}
Live example
As always when the input does not (or sometimes does not) match a strict enough syntax, read the whole line and then apply the rules which to a human are "obvious".
In this case (quoting comment by john):
Read the whole string as a single line. Then analyze the string to work out where the breaks are between A, B and C. Then convert each part to the type you require.
Specifically, you probably want to use reverse searching functions (e.g. https://en.cppreference.com/w/cpp/string/byte/strrchr ), because the last parts of the input seem the most strictly formatted, i.e. easiest to parse. The rest is then the unpredictable part at the start.
either try inputting the different data type in different lines and then use line breaks to input different data types or use the distinction to differentiate different data types like adding a . or comma
use the same symbol after each data package, for example, Juan Dela Cruz;150.50;5 then you can check for a ; and separate your string there.
If you want to use the same input format you could use digits as an indicator to separate them

MFC newbie: how to determine if a character is hexadecimal using "FindOneOf()"

I'm new at MFC and I need to do something which sounds extremely simple: determining if a string only contains hexadecimal characters.
For that, I browse through the string (it's a CString) and I verify all characters using the FindOneOf() method, as follows:
int iTest = CString(pszText[i]).FindOneOf((LPCWSTR)"0123456789ABCDEFabcdef");
For some bizarre reason, I always get -1 as a result.
What am I doing wrong?
P.s.1 I prefer not to use the SpanIncluding() method, I find the FindOneOf() quite readable but I don't know how to use it.
P.s.2 Also simple STL seems not to be working: I tried to work with std::isxdigit(pszText[i]) but in order to get this to work, I need to include <locale> and then this function is asking for a second parameter, next to the character I want to check, and a null-pointer is not allowed there (std::isxdigit(pszText[i], nullptr) does not work).
There are several problems in your code:
This is wrong:
int iTest = CString(pszText[i]).FindOneOf(LPCWSTR)"0123456789ABCDEFabcdef");
It should be:
int iTest = CString(pszText[i]).FindOneOf(L"0123456789ABCDEFabcdef");
The cast will simply make the compiler believe that "0123..." is a wide string but it isn't. You need to use the L prefix to indicate that the string is a wide string.
But even then your algorithm won't work because FindOneOf will simply find the first occurrence of any of the characters in the parameter.
Example:
int iTest = CString(L"Z223Zbc").FindOneOf(L"0123456789ABCDEFabcdef");
"Z223Zbc" is obviously not a hexadecimal string, but iTest will contain 1 because the first character of "Z223Zbc" being part of "0123456789ABCDEFabcdef" is '2' and that's at position 1.
iTest will only contain -1 if the string to be tested doesn't contain any hexadecimal characters as for example "xyz".
Therefore this solution is appropriate:
#include <cwctype>
...
WCHAR string[] = L"123abcX";
bool ishexstring = true; // assume the string is a hex string
for (int i = 0; ; i++)
{
WCHAR c = string[i];
if (c == 0)
break; // end of string => we exit the looop
if (!std::iswxdigit(c))
{
ishexstring = false; // c is no hex digit
break; // exit loop
}
}
This algorithm should be put into a function, but I'm leaving this as an exercise for the reader.
Solution using SpanIncluding (less efficient because we need tot construct a temporary CString):
bool ishexstring = CString(string).SpanIncluding(L"0123456789ABCDEFabcdef") == str;

Ogre3d having unique node names error

I am working on city generation for a pcg game of mine. I have a for loop which makes 3 cities in random locations, I assign parentIteration to get that "id" for the city and do the same in the for loop where I make a building
for (int i = 0; i < 3; i++)
{
parentIteration = i;
std::srand(i);
_rootNode = GameManager::getSingletonPtr()->getSceneManager()->getRootSceneNode();
_cityNode = _rootNode->createChildSceneNode("cityNode " + parentIteration);
generateCity(std::rand() % 10000 + 10, std::rand() % 10000 + 10, std::rand() % 11 +1);
}
building
for (int i = 0; i < _numberOfBuildings; i++)
{
childIteration = i;
printf(" parent %d and child %d \n", parentIteration, childIteration);
Ogre::SceneNode* buildingNode = _cityNode->createChildSceneNode("citybuildingNode"+childIteration+parentIteration );
}
However when I try to launch the game it will crash on creating the second city. Saying it already has a name similar to what it is trying to write. Yet my printf clearly show that the numbers at that point are all unique. Anyone know how to resolve this issue? (added picture for proof of output)
The "itybuildingNode" in the error message suggests that
"citybuildingNode"+childIteration+parentIteration
is not working quite the way you wanted.
This is because of a couple things working against you:
"citybuildingNode" is a String Literal, and not a string object. It is litteraly just a bunch of characters in a row terminated by a null character and represented as a const char *, a pointer to that array of characters. It is low-level voodoo, the sort of stuff you might make a string class around. For more information see String Literals
Because it's not a string object, you can't pull any of the usual string object tricks like concatenating with a + and comparing with ==. But because it is a pointer, the compiler interprets + as an attempt to perform pointer arithmetic and reference another location in the array. It compiles, but note how it turned "citybuildingNode" into "itybuildingNode". Oops.
What this looks like is something like:
const char* temp = "citybuildingNode"
_cityNode->createChildSceneNode(temp + childIteration + parentIteration);
which resolves to
const char* temp = "citybuildingNode"
_cityNode->createChildSceneNode(&temp[childIteration + parentIteration]);
Even if it was a string object, the C++ standard string object, std::string does not allow you to add numbers to strings. It only adds strings together to build a bigger string. To add a number to a std::string, you have to turn the number into a std::string. std::to_string can help you here, but there is a cleaner-looking way to do this with std::stringstream
Eg:
std::stringstream nodename("citybuildingNode");
// builds a string stream around the string literal
nodename << childIteration << parentIteration;
// writes the numbers into the stream the same way `cin << number;` would
// turning the number into a string for you
Ogre::SceneNode* buildingNode = _cityNode->createChildSceneNode(nodename.str());
// gets the assembled string from the stringstream
// null-terminated string like ogre expects
This gets you started in the right direction, but still allows for collision between child 1 and parent 10 ("citybuildingNode110") and child 11 and parent 0 (also "citybuildingNode110") and similar. So you really want something more like
nodename << childIteration << '_' << parentIteration;
to force a separator between the two numbers.
Documentation for std::stringstream.
There is also another possible nasty. The string we just supplied to ogre will only exist for as long as std::stringstream nodename exists and it will die at the end of the loop that generates it. I do not see anything in a quick perusal of the documentation that says ogre makes its own copy of this string. So play around a bit to make sure that you don't have to store this name somewhere to prevent it from falling out of scope, being destroyed, and leaving ogre with a dangling reference.

C++ Converting Vector items to single String Error?

So I have a function, where KaylesPosition is a class with a vector<int> called piles:
// Produces a key to compare itself to equivalent positions
std::string KaylesPosition::makeKey(){
std::vector<int> temp(piles.size());
for (int i = 0;i<piles.size();i++){
temp[i]=piles[i];
}
std::sort (temp.begin(),temp.end());
std::string key = "" + temp.at(0);
for (int i=1 ; i<temp.size() ; i++){
key.push_back('.');
key.push_back(temp.at(i));
}
return key;
}
My expected output should be all of the elements in piles in order, separated by periods. However instead, I get key return as "_M_range_check". I have tried this using std::string.append() and I get either an empty string or a period. How do I get this function to return a string of all of the values in piles as expected?
The problem seems to be here:
key.push_back(temp.at(i));
You are trying to append an integer to a string without getting the string representation of the integer first. Try replacing that line with:
key += std::to_string(temp.at(i)); // This will only work if your compiler supports C++11
If your compiler doesn't support C++11, try this (don't forget to #include <sstream>):
std::ostringstream o;
o << temp.at(i);
key += o.str();
Or, if you have the option to use Boost (http://boost.org/ ), try its lexical_cast:
key += boost::lexical_cast<std::string>(temp.at(i));
The reason this code compiled in the first place, is because push_back accepts a char as its parameter and you're passing an int which gets converted to char (although I would expect a warning from the compiler in this case).
P.S.: Same applies for the line
std::string key = "" + temp.at(0);

How to check the length of an input? (C++)

I have a program that allows the user to enter a level number, and then it plays that level:
char lvlinput[4];
std::cin.getline(lvlinput, 4)
char param_str[20] = "levelplayer.exe "
strcat_s(param_str, 20, lvlinput);
system(param_str);
And the level data is stored in folders \001, \002, \003, etc., etc. However, I have no way of telling whether the user entered three digits, ie: 1, 01, or 001. And all of the folders are listed as three digit numbers. I can't just check the length of the lvlinput string because it's an array, so How could I make sure the user entered three digits?
Why not use std::string?
This makes storage, concatenation, and modification much easier.
If you need a c-style string after, use: my_string.c_str()
Here is a hint: To make your input 3 characters long, use std::insert to prefix your number with 0's.
You are really asking the wrong question. Investigate the C++ std::string class and then come back here.
Eh? Why do they need to enter 3 digits? Why not just pad it if they don't? If you really want to check that they entered 3 digits, use strlen. But what I recommend you do is atoi their input, and then sprintf(cmd, "levelplayer.exe %03d", lvlinput_as_integer)
Here's how you could do this in C++:
std::string lvlinput;
std::getline(std::cin, lvlinput);
if (lvlinput.size() > 3) { // if the input is too long, there's nothing we can do
throw std::exception("input string too long");
}
while (lvlinput.size() < 3) { // if it is too short, we can fix it by prepending zeroes
lvlinput = "0" + lvlinput;
}
std::string param_str = "levelplayer.exe ";
param_str += lvlinput;
system(param_str.c_str());
You've got a nice string class which takes care of concatenation, length and all those other fiddly things for you. So use it.
Note that I use std::getline instead of cin.getline. The latter writes the input to a char array, while the former writes to a proper string.
What do you mean you can't check the length of the string? getline generates a NULL terminated c-string so just use strlen(lvlinput).
Neil told you where you should start, your code might look like this.
std::string level, game = "levelplayer.exe ";
std::cout << "Enter the level number : ";
std::cin >> level;
if(level.size() != 3)
{
// Error!
}
else
{
// if you have more processing, it goes here :)
game += level;
std::system(game.c_str());
}
You can check the length of your NULL terminated string that getline returns by using:
int len = strlen(lvlinput);
This works because getline returns a NULL-terminated string.
However, this is besides the point to your problem. If you want to stay away from std::string (and there isn't any particular reason why you should in this case), then you should just convert the string to an integer, and use the integer to construct the command that goes to the system file:
char lvlinput[4];
std::cincin.getline(lvlinput, 4);
char param_str[20];
snprintf(param_str, 20, "levelplayer.exe %03d", atoi(lvlinput));
system(param_str);