Is it acceptable to initialize temporary strings to keep conditionals readable? - c++

This applies to any language, but for now, let's look at c++. Suppose we have two chars that must take a hexadecimal value:
char b, t;
do {
//some code
} while(((b<'0' || b>'9') && (b<'A' || b>'F')) || ((t<'0' || t>'9') && (t<'A' || t>'F')));
Phew that last conditional looks scary. Let's look at an alternate:
char b, t;
do {
//some code
} while(string(1,b).find_first_of("0123456789ABCDEF")==-1 || string(1,t).find_first_of("0123456789ABCDEF")==-1);
The code still looks very messy, but at least we can understand what is going on: a couple of strings are initialized to see if there is a hex character in them.
Assuming there is no difference in performance, is the second solution better than the first one, or is there another way to see if a char contains a hex value without inflating the code?
The chars' case can be changed in the loop to ALLCAPS or lowercase, if necessary (currently ALLCAPS).

I think you are looking for std::isxdigit.

Related

Why do I have to make a 2d array for this

I was solving a question online on strings where we had to perform run-length encoding on a given string, I wrote this function to achieve the answer
using namespace std;
string runLengthEncoding(string str) {
vector <char> encString;
int runLength = 1;
for(int i = 1; i < str.length(); i++)
{
if(str[i - 1] != str[i] || runLength == 9)
{
encString.push_back(to_string(runLength)[0]);
encString.push_back(str[i - 1]);
runLength = 0;
}
runLength++;
}
encString.push_back(to_string(runLength)[0]);
encString.push_back(str[str.size() - 1]);
string encodedString(encString.begin(), encString.end());
return encodedString;
}
Here I was getting a very long error on this particular line in the for loop and outside it when I wrote:
encString.push_back(to_string(runLength));
which I later found out should be:
encString.push_back(to_string(runLength)[0]);
instead
I don't quite understand why I have to insert it as a 2D element(I don't know if that is the right way to say it, forgive me I am a beginner in this) when I am just trying to insert the integer...
In stupid terms - why do I gotta add [0] in this?
std::to_string() returns a std::string. That's what it does, if you check your C++ textbook for a description of this C++ library function that's what you will read there.
encString.push_back( /* something */ )
Because encString is a std::vector<char>, it logically follows that the only thing can be push_back() into it is a char. Just a single char. C++ does not allow you to pass an entire std::string to a function that takes a single char parameter. C++ does not work this way, C++ allows only certain, specific conversions betweens different types, and this isn't one of them.
And that's why encString.push_back(to_string(runLength)); does not work. The [0] operator returns the first char from the returned std::string. What a lucky coincidence! You get a char from that, the push_back() expects a single char value, and everyone lives happily ever after.
Also, it is important to note that you do not, do not "gotta add [0]". You could use [1], if you have to add the 2nd character from the string, or any other character from the string, in the same manner. This explains the compilation error. Whether [0] is the right solution, or not, is something that you'll need to figure out separately. You wanted to know why this does not compile without the [0], and that's the answer: to_string() returns a std::string put you must push_back() a single char value, and using [0] makes it happen. Whether it's the right char, or not, that's a completely different question.

How do I check whether character constants conform to ASCII?

A comment on an earlier version of this answer of mine alerted me to the fact that I can't assume that 'A', 'B', 'C' etc. have successive numeric values. I had sort of assumed the C or C++ language standards guarantee that this is the case.
So, how should I determine whether consecutive letter characters' values are themselves consecutive? Or rather, how can I determine whether the character constants I can express within single quotes have their ASCII codes for a numeric value?
I'm asking how to do this both in C and in C++. Obviously the C way would work in C++ also, but if there's a C++ish facility for doing this I'm interested in that as well. Also, I'm asking about the newest relevant standards (C11, C++17).
You can use the preprocessor to check if a particular character maps to the charset:
#include <iostream>
using namespace std;
int main() {
#if ('A' == 65 && 'Z' - 'A' == 25)
std::cout << "ASCII" << std::endl;
#else
std::cout << "Other charset" << std::endl;
#endif
return 0;
}
The drawback is, you need to know the mapped values in advance.
The numeric chars '0' - '9' are guaranteed to appear in consecutive order BTW.
... (2) I expect to be able to obtain the distance in number-of-letters between two letters ...
This comment specifying your goal makes much more sense than your actual question! Why didn't you ask about that? You can use strchr on an array of characters, and strchr doesn't care what the native character set is, meaning your code won't care what the native character set is... For example:
char alphabet[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
ptrdiff_t fubar = strchr(alphabet, 'y') - strchr(alphabet, 'X');
printf("'X' and 'y' have a distance of %tu and a case difference of %tu\n", fubar / 2, fubar % 2);
... how should I determine whether consecutive letter characters' values are themselves consecutive?
Consecutive letter characters' values are consecutive, by definition, because they're consecutive letter characters. I know this isn't what you meant, but your actual question illustrates a lack of planning and thought, and... a stupid question warrants a stupid answer.
You're much better off programming in such a way that you don't care what values they have. Nonetheless, create an array containing the characters you care about, loop through the elements and test for inconsistencies. For example:
int is_consecutive(char const *alphabet) {
for (size_t x = 0; alphabet[x] && alphabet[x] + 1 == alphabet[x + 1]; x++);
return !alphabet[x];
}
... how can I determine whether the character constants I can express within single quotes have their ASCII codes for a numeric value?
Again with the lack of sense, and again with the caring about values... Alternatively, build two translation tables, native_to_ascii and ascii_to_native, and work it out from there. I won't help you with this, as it's a silly exercise involving the use of magic numbers that most likely aren't necessary for your actual goal.

How To Compare Strings In C++?

I am just messing around in C++ with some things I recently learned and I wanted to know how to correctly compare two strings to each other. I looked at a previous thread for help, but I am not sure I am getting the variables right and there was a repeating error. (P.S. This is executed to the command prompt.)
string Users = "Username1";
//Set an empty string.
string UserChoice;
//Print out a line that warns the user to type a user.
std::cout << "Username: ";
std::cin >> UserChoice;
//If the user types out whatever "Users" is, run the code below.
if (strcmp(Users, UserChoice) == 0){
//Do Stuff
}
You want:
if (Users == UserChoice) {
The std::string class (well, really std::basic_string) overloads the == operator (and many others) to do what you want. You should not be using C functions like strcmp in C++ code, and in any case they cannot be directly applied to C++ std::strings.
Comparing strings is the same as comparing int values, char values, etc... . You should use the following method:
string a
string b
if (a == b)
{
// Do something
}
In your case, 'a' and 'b' would be replaced by 'Users', 'UserChoices'. But the basic format of comparing 2 variables of the same type stays the same regardless of the type (I'm not sure whether there are any exceptions to this rule or not).
It is also recommended, just as #latedeveloper mentioned, not to use c-language functions in a c++ program. The 2 languages are NOT interchangeable!
** Helpful tip: Always strive to keep your code as simple as possible. With some exceptions possible, the more complicated you make your code, the more hard you will make it for others to understand your code. To connect it to your case, why use a function strcmp() when you can keep it simple by using the == sign? This is just my 2 bits based on personal experience.
c style:
string a
string b
if(strcmp(a.c_str(),b.c.str()) == 0)

Converting strings to ints or doubles

I know this is a repeated question, and I a have looked at a lot of answers for it but none have really been able to help me so far.
I need to be able to pass various strings into two methods one returns a double and the other a int.
The main problem is that I need strict error checking on both methods so that if I pass a string that dose not contain a number and only a number the method does not make a conversion. As I said I've seen a few solutions but the only good one I've seen (that was able to follow) was using Boost which I do not want to use. As for the answer I was not able to follow here is a part of it copied from
How to parse a string to an int in C++?
The best solution
Fortunately, somebody has already solved all of the above problems. The C standard library contains strtol and family which have none of these problems.
enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };
STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
char *end;
long l;
errno = 0;
l = strtol(s, &end, base);
if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
return OVERFLOW;
}
if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
return UNDERFLOW;
}
if (*s == '\0' || *end != '\0') {
return INCONVERTIBLE;
}
i = l;
return SUCCESS;
}
If anyone can explain it a little more I think it is the answer Im looking for I just cant get enough of a grasp of what its doing so that i can apply the idea to my code.
Using strtol (and strtod for doubles) sounds like the right approach. The implementation that you quoted flows straight from the documentation for strtol:
It sets errno to zero before the call (strtol does not change errno on success)
It examines errno upon return of strtol to see if an error has been signaled
The final condition checks that the input is not empty, and that the entire input has been consumed by strtol.
An implementation for doubles would look the same, except you'd use strtod.
As a learning exercise, you might want to try to write this on your own. I suggest starting with some examples. You might want to start by getting a firm grasp on how numbers are represented in writing. What does "42" mean? In other words, what does the 2 represent and what does the 4 represent? Similarly, what does "1000000" mean? Once you get a grasp on these concepts, write down the steps (in English) you would take to convert a string into an integer. From there, start converting it into code. If you get stuck anywhere along the way, feel free to come back with more questions.
I re-worded this question in another location and finally got the answer I was looking for. For any wondering here is the link:
Basics of strtol?

c++ isalnum endless loop

Greetings!
Lets cut the excessive intro this time and get straight to the point.
I have a problem in C++ using the isalnum method.
the code:
int playAgainst = 0;
do
{
cout << "Who do you want to play against?(1/2)\n";
cout << "1: Human player\n";
cout << "2: Computer player\n";
cin >> playAgainst
} while(!isalnum(playAgainst) && playAgainst != 0);
As seen in the code, I'm providing the user with a choice. Play against human or play against a computer.
What I want is, as long as the user enters anything else then an integer value(cin >> playAgainst) to repeat the question. However, If i enter a char, or string value, it keeps looping endlessly. I am not 100% sure, but it would be obvious, if the problem is, that the non int value is already saved as the value for playAgainst.. How can I check in this bit of code if the input value is int before saving it?
Or is the only possibility to save as a char/string and then check?
If the latter is the case, a new problem arises. isalnum only accepts int as parameter, atleast from what I know. How will I check if that string or char is an int?
Thank you for taking the time to read. And hopefully Ill be accepting a reply as answer soon ^^
Thanks everyone for the answers.
I have gotten what I wanted, and everything has been solved.
The reason I chose for the accepted answer, is well... because initially, it made my code work the way I want it to. I want to accept multiple answers though..
Make playAgainst a char and compare against '0', not 0. Right now, the user has to enter the ASCII (or whatever your character set is) code for the character '1' or '2'.
isalnum won't work on ints outside the valid range of char, except for EOF. (The fact that it takes an int argument is a leftover from C, which has different integer promotions rules than C++. to accomodate for EOF.)
Remove the isalnum() check. cin >> playAgainst will convert anything except a number to zero, which will be caught by the second check. Note that this is only an option because zero is not a valid input.
isalnum() is useful if you're interpreting the characters yourself, but in this case the stream has already done that for you.
This is how the compiler will implement isalnum:
int isalnum (int ch)
{
return (ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9');
}
So you might as well write that code snippet in your own code, and it will be equivalent with an inlined version of isalnum.
It's because you don't clear the buffer. When the input is invalid, you need to clear it from the buffer, and then you can move on to the next input, else you're trying to extract the same input every time (which fails, because it's the same bad input), and thus enter an infinite loop.
The problem is that you're inputing an int, and not a char. And if the
text in the input isn't an int, then the input fails. In which case,
playAgainst isn't modified, and the failure is memorized in std::cin
until you explicitly clear the error. And inputting from a stream in an
error state is a no-op. What you probably want to do is
Input a single character: if you don't want to skip spaces, using
`std::cin.get( ch )` or `ch = std::cin.get()`. (In the latter
case, `ch` should be an `int`, since it must also handle `EOF`.
On the other hand, you can use `::isalnum` on it directly, which
you can't do if `ch` is a `char`.
Fully check for valid input: not just `::isalnum`, but rather
whether the input is a legal selector in your list. Something
along the lines of:
ch != EOF && std::find( legalChars.begin(), legalChars.end(), (char)ch ) != legalChars.end()
In case of error, clear any remaining input, say with:
std::cin.ignore(INT_MAX, '\n');
In practice, you'll probably want to treat EOF differently from
an erroneous command. (If you don't clear the input after EOF, you
won't be able to read anything else. But presumably, if you got EOF,
it's because the user gave up, and doesn't want to try any more.)
Finally, it's probably preferrable to keep all of the information in
a common location, using a table of:
struct Command
{
char op;
char const* prompt;
void (* func)();
};
You then loop over a table of these to output the prompt, search it to
see if the character was legal, and finally, call the function on the
entry you found. Or define an abstract base class, a concrete class
deriving from it for each command, and use an std::map<char,
AbstractBase*> for the mapping, etc. Very C++, but perhaps a bit
overkill for such a simple case.
Why not use isdigit().