How do i check argv[1] doesn't have any alphabetical characters? - c++

This has been driving my entire C++ class nuts, none of us has been able to find a solid solution to this problem.
We are passing information to our program through the Terminal, via argv* [1]. We would call our program ./main 3 and the program will run 3 times.
The problem comes when we are validating the input, we are trying to cover all of our bases and for most of them we are good, like an alphabetical character entered, a negative number, 0, etc. But what keeps passing through is an int followed by a str for example ./main 3e or ./main 1.3. I've tried this
( Ashwin's answer caught my eye ) but it doesn't seem to work or at least I can't implement it in my code.
This is my code now:
int main(int argc, char * argv[]){
if (!argv[1]) exit(0);
int x = atoi(argv[1]);
if (!x or x <= 0) exit(0);
// I would like to add another exit(0); for when the input mixes numbers and letters or doubles.
for (int i = 0; i < x; i++){
// rest of the main func.
}

Despite the title, it sounds like you really want to do is check whether every single character in the input argument is a digit. You can achieve this by iterating over it, checking that every element is a digit using std::isdigit.
Here's a sketch using the std::all_of algorithm:
size_t len = strlen(argv[1]);
bool ok = std::all_of(argv[1], argv[1] + len,
[](unsigned char c) { return std::isdigit(c); } );
You can add an extra check for the first element being '0' if needed.

If you want to convert a string to a number and verify that the entire string was numeric, you can use strtol instead of atoi. As an additional bonus, strtol correctly checks for overflow and gives you the option of specifying whether or not you want hexadecimal/octal conversions.
Here's a simple implementation, with all the errors noted (printing error messages from a function like this is not a good idea; I just did it for compactness). A better option might be to return an error enum instead of the bool, but this function returns a std::pair<bool, int>: either (false, <undefined>) or (true, value):
std::pair<bool, int> safe_get_int(const char* s) {
char* endptr;
bool ok = false;
errno = 0; /* So we can check ERANGE later */
long val = strtol(s, &endptr, 10); /* Don't allow hex or octal. */
if (endptr == s) /* Includes the case where s is just whitespace */
std::cout << "You must specify some value." << '\n';
if (*endptr != '\0')
std::cout << "Argument must be an integer: " << s << '\n';
else if (val < 0)
std::cout << "Argument must not be negative: " << s << '\n';
else if (errno == ERANGE || val > std::numeric_limits<int>:max())
std::cout << "Argument is too large: " << s << '\n';
else
ok = true;
return std::make_pair(ok, ok ? int(val) : 0);
}
In general, philosophical terms, when you have an API like strtol (or, for that matter, fopen) which will check for errors and deny the request if an error occurs, it is better programming style to "try and then check the error return", than "attempt to predict an error and only try if it looks ok". The second strategy, "check before use", is plagued with bugs, including security vulnerabilities (not in this case, of course, but see TOCTOU for a discussion). It also doesn't really help you, because you will have to check for error returns anyway, in case your predictor was insufficiently precise.
Of course, you need to be confident that the API in question does not have undefined behaviour on bad input, so read the official documentation. In this case, atoi does have UB on bad input, but strtol does not. (atoi: "If the value cannot be represented, the behavior is undefined."; contrast with strtol)

Related

bulletproof use of from_chars()

I have some literal strings which I want to convert to integer and even double. The base is 16, 10, 8, and 2.
At this time, I wonder about the behavior of std::from_chars() - I try to convert and the error code inside from_chars_result return holds success - even if it isn't as shown here:
#include <iostream>
#include <string_view>
#include <charconv>
using namespace std::literals::string_view_literals;
int main()
{
auto const buf = "01234567890ABCDEFG.FFp1024"sv;
double d;
auto const out = std::from_chars(buf.begin(), buf.end(), d, std::chars_format::hex);
if(out.ec != std::errc{} || out.ptr != buf.end())
{
std::cerr << buf << '\n'
<< std::string(std::distance(buf.begin(), out.ptr), ' ') << "^- here\n";
auto const ec = std::make_error_code(out.ec);
std::cerr << "err: " << ec.message() << '\n';
return 1;
}
std::cout << d << '\n';
}
gives:
01234567890ABCDEFG.FFp1024
^- here
err: Success
For convenience also at coliru.
In my use case, I'll check the character set before but, I'm not sure about the checks to make it bulletproof. Is this behavior expected (maybe my English isn't sufficient, or I didn't read carefully enough)? I've never seen such checks on iterators on blogs etc.
The other question is related to different base like 2 and 8. Base of 10 and 16 seems to be supported - what would be the way for the other two bases?
Addendum/Edit:
Bulletproof here means that I can have nasty things in the string. The obvious thing for me is that 'G' is not a hex character. But I would have expected an appropriate error code in some way! The comparison out.ptr != buf.end() I've never seen in blogs (or I didn't read the right ones :)
If I enter a crazy long hex float, at least a numerical result out of range comes up.
By bulletproof I also mean that I can find such impossible strings by length, for example, so that I can save myself the call to from_chars() - for float/doubles and integers (here I would 'strlen' compare digits10 from std::numeric_limits).
The from_chars utility is designed to convert the first number it finds in the string and to return a pointer to the point where it stopped. This allows you to parse strings like "42 centimeters" by first converting the number and then parsing the rest of the string yourself for what comes after it.
The comparison out.ptr != buf.end() I've never seen in blogs (or I didn't read the right ones :)
If you know that the entire string should be a number, then checking that the pointer in the result points to the end of the string is the normal way to ensure that from_chars read the entire string.

trouble with loops, functions, and organization´

and I would like to preface this by saying this is NOT a current homework assignment; but it is an assignment from 3 years ago before I dropped out of school. I am self teaching and am revisiting an old assignment. I am NOT asking for the entire program, I'm simply looking for help building the skeleton for the initial start of the game.
MORE INFO:
Player 1 will enter word(of any length / i have been using "Testing") for Player 2 to guess. Player 2 will have 5 letter guesses, and 5 word guesses. If Player 2 enters "Testing" it should be able to ignore the case between upper/lower (WITHOUT using toupper / tolower)
IF: Player 2 enters more than 1 letter for a guess: "aa" make them guess again until they only guess 1 letter "a".
The problems I'm facing is: I don't know where to place everything, I feel I'm mixing up or messing up the functions, and everytime I try to organize it, it only gets worse. I've restarted it several times, I'm just having trouble getting it all laid out.
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main()
{
string word, wordguess, lower, dashes;
string letterguess;
int i = 0;
bool GameOver = false, Validletterguess = true, Validwordguess = true;
cout << "Player 1, enter a word for Player 2 to guess: " << endl;
getline(cin, word);
cout << endl;
cout << "Player 2, you have 5 letter guesses, and 5 word guesses." << endl;
cout << "Guess your first letter: " << endl;
while (GameOver == false) // Start of Game. Setup for Round 1 letter guess and word guess.
{
while (letterguess.length() != 1) // 1 letter only. loop until they enter 1 letter
{
cout << endl << "Type a single letter and press <enter>: ";
cin >> letterguess; // enter letter guess
for (int i = 0; i < letterguess.length(); i++) //ignore case of letter guess
{
if (letterguess.at(i) >= 'A' && letterguess.at(i) <= 'Z')
{
lower += letterguess.at(i) + 32;
}
else
{
lower += letterguess.at(i);
}
}
if (letterguess.at(i) == word.at(i) && Validletterguess == true) //if Player2 guesses a correct letter, replace the dash with letter and display location: ex. If "T" then "You guessed the 1st and 4th letter"
{
cout << "You guessed the first letter right!" << endl; // figure out how to display dashes?
dashes.at(i) = letterguess.at(i);
cout << "Enter your first word guess: " << endl;
cin >> wordguess;
}
else
cout << "Wrong letter! Enter your first word guess: " << endl;
cin >> wordguess;
if (wordguess == word & Validwordguess = true)
{
cout << "You guessed the word correctly in 1 try! " << endl;
Gameover = true;
}
}
}
}
There are several things in C++ that can assist you. It's good to see that you're already using std::string and std::getline to deal with the user input. The problem seems to be that you've gotten tangled up in organizing the game logic so that it flows, and setting up structures that can help you.
I did actually go ahead and write a game just for kicks. The hope is that I can provide some of that and describe it so you can digest in chunks, and you can see how one can build a program up a bit at a time.
So let's start by making a stub for the function that will actually run the game. You can call this from main. It simplifies the actual running of the game by separating it from the other setup and shutdown stuff. It also means you can run several games in a row later, without having to modify the game loop.
enum GameResult {
None,
Win,
Loss,
};
GameResult PlayHangman(const std::string& target, int wrongLetterLimit, int wrongWordLimit)
{
return None;
}
And to illustrate that point, here is the full main that I ended up writing to invoke this game. Even though it's a one-off, you can see that it can be useful. In this case, I chose to read the game settings from the command line:
void ExitSyntaxMessage(int code = -1)
{
std::cerr << "Syntax: hangman <word> [guesses [wordGuesses]]\n";
exit(code);
}
int main(int argc, char** argv)
{
// Get game settings
std::string target;
int letterGuesses = 5;
int wordGuesses = 5;
try {
if (argc < 2) throw std::runtime_error("Not enough arguments");
target = argv[1];
if (argc > 2) letterGuesses = std::stoi(argv[2]);
if (argc > 3) wordGuesses = std::stoi(argv[3]);
}
catch(...)
{
ExitSyntaxMessage();
}
// Play game
GameResult result = PlayHangman(target, letterGuesses, wordGuesses);
// Deliver result and exit
switch(result)
{
case Win:
std::cout << "Congratulations!\n";
return 0;
case Loss:
std::cout << "Better luck next time!\n";
return 1;
default:
std::cout << "Game stopped.\n";
return -2;
}
}
So, now there's a simple framework for your game to run in. There's not much code, but it's something you can immediately start testing before moving on to fleshing out the game itself.
At this point, I should mention some headers that this program will be needing. Some will have been required already. Others are required for stuff we're about to do.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include <set>
On to the game... A helper function to turn a string into lowercase is always handy. We'll definitely make use of that. Note that this uses a lambda function. If you don't have a modern C++ compiler (with C++11 support) you can just use an ordinary function pointer instead.
std::string Lowercase(const std::string& s)
{
std::string lc(s);
std::transform(lc.begin(), lc.end(), lc.begin(),
[](char c)->char{ return std::tolower(c); });
return lc;
}
Now it's time to expand on the PlayHangman stub. It's still gonna be a stub, but we can set up a few things that we'll be needing and get those tested before proceeding.
GameResult PlayHangman(const std::string& target, int wrongLetterLimit, int wrongWordLimit)
{
GameResult result = None;
// Create lowercase target and add its characters to set of remaining letters
std::string lcTarget = Lowercase(target);
std::set<char> lettersRemaining(lcTarget.begin(), lcTarget.end());
std::set<std::string> guesses;
// Set up game parameters
int letterGuessesRemaining = wrongLetterLimit;
int wordGuessesRemaining = wrongWordLimit;
// Sanity-test to ensure game is winnable
if (wordGuessesRemaining == 0 && letterGuessesRemaining < lettersRemaining.size())
{
std::cout << "Game is not winnable...\n";
return None;
}
// Game loop until stream error or game finishes
bool done = false;
while (!done)
{
done = true; // The loop is a stub for now
}
//// ^^^ for now, just use this bit to test the game setup stuff.
//// Make sure that your lowercase bits are working and the set of
//// remaining letters works. You can add some output code to debug
//// their values and run tests from the command line to verify.
return result;
}
That is going to be the primary structure of a single game. So let's talk about it. Notice again how I'm still not going into detail. At this point, I've already thought about how I should logically be running the game.
Now, I should say that in reality, most people don't write code in a linear way like this from the outside in. It's more of an organic process, but I do take care to separate stuff out into logical bits, and reshuffle/organize stuff as I go. I also try not to do too much at once.
You'll see by the way I've presented this, that I'm encouraging you to develop a solid platform in which to write your game logic. By the time you're writing that logic, you should be able to trust that everything else already works because you tested it.
Some things happening up there are:
The target string is copied into a lowercase version of itself. This will be used to test the word-guesses. There are other ways to test strings ignoring case, but this is just a simple way.
Because we've built that string, we can also use it to construct a std::set containing exactly one of each unique character in that string. That's a one-liner, constructing the set from the string's iterators. Very neat and tidy!
We also have a set of strings called guesses -- this will track all the guesses (correct/incorrect inclusive) so that you don't get penalized for accidentally repeating something you already guessed.
There's a sanity check, which is a duplicate of what will eventually be the end-game test inside the loop. To be honest, that was one of the last things I added, but I've put it here because it's part of the pre-game setup, and apart from the stubbed loop, this is the entire "game" sequence.
Checkpoint : Game skeleton complete
At this point, you might have seen enough to go off and complete the game. There are some important concepts introduced up there. In particular, the idea of storing the remaining letters as a std::set might be just the kind of trick that makes everything click into place.
Reading from here on will complete the program. It's up to you whether you want to do that, or stop reading and have a crack at it yourself first.
Let's start fleshing out some of the game loop. First, you probably wanna deal with showing the current game state and requesting input. That happens in two steps. The first part builds a string by hiding characters that are not yet guessed and then outputs it. The second part is an input-validating loop that discards empty lines, ignores duplicate guesses and handles end-of-stream.
Note that the input is converted to lowercase. This just simplifies things. Especially when checking for duplicate guesses.
while (!done)
{
// Create prompt from original string with a dash for each hidden character
std::string prompt(target);
for(char& c : prompt)
{
if (lettersRemaining.count(std::tolower(c)) != 0) c = '-';
}
std::cout << prompt << "\n";
// Get input
std::string input;
for (bool validInput = false; !validInput && !done; )
{
std::cout << "> " << std::flush;
if (!std::getline(std::cin, input))
{
done = true;
}
else if (!input.empty())
{
input = Lowercase(input);
validInput = guesses.insert(input).second;
if (!validInput)
{
std::cout << "You already guessed that!\n";
}
}
}
if (done)
continue;
// TODO: Process guess, update game state, and check end-game conditions
}
Once again, we have expanded on the implementation and now have something to test. So make sure it all compiles and works the way you want it to. Obviously the game will run forever right now, but that's fine -- you can just terminate the process.
When you're happy, move on to the actual logic. This is where we start putting together everything that has already been set up.
Thanks to our input loop, we now know that the input is now a new guess comprising either 1 letter or a word. So I start by branching for either the letter guess or the word guess. You should start to see a pattern here, right? Once again, I write an empty section of code to do something, and then start actually filling it in...
// Check the guessed letter or word
bool correctGuess = false;
if (input.size() == 1)
{
if (letterGuessesRemaining == 0)
{
std::cout << "No more letter guesses remain.\n";
}
else
{
// Test the guessed letter
}
}
else
{
if (wordGuessesRemaining == 0)
{
std::cout << "No more word guesses remain.\n";
}
else
{
// Test the guessed word
}
}
So, the letter test... Recall we already built the lettersRemaining set and tested it. And those are the only ones obscured by dashes in the prompt. So it then becomes trivial to determine whether they guessed one. If it's in the set, they guessed correctly and you remove it from the set. Otherwise, they burn up one of their guesses.
Because the input is already lowercase, we can use the letter verbatim to search within the values stored in the set (which are also lowercase).
// Test the guessed letter
char letter = input[0];
if (lettersRemaining.count(letter) != 0)
{
correctGuess = true;
lettersRemaining.erase(letter);
}
else
{
std::cout << "Nope!\n";
--letterGuessesRemaining;
}
The word test is even easier. Recall that we stored a lowercase version of the target word already, and the input was also converted to lowercase. So we just compare. You see how all this lowercase business has actually made life less complicated?
// Test the guessed word
if (input == lcTarget)
{
correctGuess = true;
lettersRemaining.clear(); //<-- we can use this to test for a win
}
else
{
std::cout << "Nope!\n";
--wordGuessesRemaining;
}
We are quite literally almost done! The only thing left to do is check whether the game should stop due to being won or lost. That's the last part of the game loop.
Because the code handling a correct word guess is also polite and clears the lettersRemaining set, we can use that as a test for a winning condition regardless of whether a letter or word was guessed.
You'll also see that bit of logic again for the game losing condition. Recall that from before the main loop where we checked if it was even possible to win.
// If guessed incorrectly, show remaining attempts
if (!correctGuess)
{
std::cout << "\nAttempts remaining: "
<< letterGuessesRemaining << " letters, "
<< wordGuessesRemaining << " words.\n";
}
// Check if game is complete
if (lettersRemaining.empty())
{
std::cout << target << "\n";
result = Win;
done = true;
}
else if (wordGuessesRemaining == 0 && letterGuessesRemaining < lettersRemaining.size())
{
std::cout << target << "\n";
result = Loss;
done = true;
}
I hope this has been helpful, that you've been able to follow along, and that you understand the breakdown and explanation. This is generally how I approach programming. I like to build up pieces of code that I can rely on, instead of getting lost in some details and overlooking more fundamental things.
There may be some techniques, language features or parts of the standard library used here that you have not encountered before. That's fine -- you can use that to learn, experiment and research online. Keep https://cppreference.com bookmarked in your browser.
If nothing else, I hope that this gives you some insight in breaking down tasks into small bits that you care about now, and other stuff that you can worry about later. Building up a program iteratively this way enables you to test code regularly and increases your chances of finding silly mistakes that could hamstring you later. It is so common to see beginners just write a whole program in one hit, run it, then freak out because it doesn't "work".

How can I read accented characters in C++ and use them with isalnum?

I am programming in French and, because of that, I need to use accented characters. I can output them by using
#include <locale> and setlocale(LC_ALL, ""), but there seems to be a problem when I read accented characters. Here is simple example I made to show the problem :
#include <locale>
#include <iostream>
using namespace std;
const string SymbolsAllowed = "+-*/%";
int main()
{
setlocale(LC_ALL, ""); // makes accents printable
// Traduction : Please write a string with accented characters
// 'é' is shown correctly :
cout << "Veuillez écrire du texte accentué : ";
string accentedString;
getline(cin, accentedString);
// Accented char are not shown correctly :
cout << "Accented string written : " << accentedString << endl;
for (unsigned int i = 0; i < accentedString.length(); ++i)
{
char currentChar = accentedString.at(i);
// The program crashes while testing if currentChar is alphanumeric.
// (error image below) :
if (!isalnum(currentChar) && !strchr(SymbolsAllowed.c_str(), currentChar))
{
cout << endl << "Character not allowed : " << currentChar << endl;
system("pause");
return 1;
}
}
cout << endl << "No unauthorized characters were written." << endl;
system("pause");
return 0;
}
Here is an output example before the program crashes :
Veuillez écrire du texte accentué : éèàìù
Accented string written : ʾS.?—
I noticed the debugger from Visual Studio shows that I have written something different than what it outputs :
[0] -126 '‚' char
[1] -118 'Š' char
[2] -123 '…' char
[3] -115 '' char
[4] -105 '—' char
The error shown seems to tell that only characters between -1 and 255 can be used but, according to the ASCII table the value of the accented characters I used in the example above do not exceed this limit.
Here is a picture of the error dialog that pops up : Error message: Expression: c >= -1 && c <= 255
Can someone please tell me what I am doing wrong or give me a solution for this? Thank you in advance. :)
char is a signed type on your system (indeed, on many systems) so its range of values is -128 to 127. Characters whose codes are between 128 and 255 look like negative numbers if they are stored in a char, and that is actually what your debugger is telling you:
[0] -126 '‚' char
That's -126, not 126. In other words, 130 or 0x8C.
isalnum and friends take an int as an argument, which (as the error message indicates) is constrained to the values EOF (-1 on your system) and the range 0-255. -126 is not in this range. Hence the error. You could cast to unsigned char, or (probably better, if it works on Windows), use the two-argument std::isalnum in <locale>
For reasons which totally escape me, Windows seems to be providing console input in CP-437 but processing output in CP-1252. The high half of those two code pages is completely different. So when you type é, it gets sent to your program as 130 (0xC2) from CP-437, but when you send that same character back to the console, it gets printed according to CP-1252 as an (low) open single quote ‚ (which looks a lot like a comma, but isn't). So that's not going to work. You need to get input and output to be on the same code page.
I don't know a lot about Windows, but you can probably find some useful information in the MS docs. That page includes links to Windows-specific functions which set the input and output code pages.
Intriguingly, the accented characters in the source code of your program appear to be CP-1252, since they print correctly. If you decide to move away from code page 1252 -- for example, by adopting Unicode -- you'll have to fix your source code as well.
With the is* and to* functions, you really need to cast the input to unsigned char before passing it to the function:
if (!isalnum((unsigned char)currentChar) && !strchr(SymbolsAllowed.c_str(), currentChar)) {
While you're at it, I'd advise against using strchr as well, and switch to something like this:
std::string SymbolsAllowed = "+-*/%";
if (... && SymbolsAllowed.find(currentChar) == std::string::npos)
While you're at it, you should probably forget that you ever even heard of the exit function. You should never use it in C++. In the case here (exiting from main) you should just return. Otherwise, throw an exception (and if you want to exit the program, catch the exception in main and return from there).
If I were writing this, I'd do the job somewhat differently in general though. std::string already has a function to do most of what your loop is trying to accomplish, so I'd set up symbolsAllowed to include all the symbols you want to allow, then just do a search for anything it doesn't contain:
// Add all the authorized characters to the string:
for (unsigned char a = 0; a < std::numeric_limits<unsigned char>::max(); a++)
if (isalnum(a) || isspace(a)) // you probably want to allow spaces?
symbolsAllowed += a;
// ...
auto pos = accentedString.find_first_not_of(symbolsAllowed);
if (pos != std::string::npos) {
std::cout << "Character not allowed: " << accentedString[pos];
return 1;
}

Trying to ignore all whitespace up to the first character (desperately needing a simple nudge)

I'll be flat out honest, this is a small snippet of code I need to finish my homework assignment. I know the community is very suspicious of helping students, but I've been racking my head against the wall for the past 5 hours and literally have accomplished nothing on this assignment. I've never asked for help on any assignments, but none have given me this much trouble.
All I'm having trouble with is getting the program to strip the leading whitespace out. I think I can handle the rest. I'm not asking for a solution to my overall assignment, just a nudge on this one particular section.
I'll post the full assignment text here, but I am NOT posting it to try to get a full solution, I'm only posting it so others can see the conditions I have to work with.
"This homework will give you more practice in writing functions and also how numbers are read into a variable. You need to write a function that will read an unsigned integer into a variable of type unsigned short int. This will have a maximum value of 65535, and the function needs to take care of illegal numbers. You can not use "cin >>", inside the function.
The rules for numeric input are basically as follows:
1) skip all leading white spaces
2) first character found must be numeric else an error will occur
3) numeric characters are then processed one at a time and combine with number
4) processing stops when non-numeric found
We will follow these rules and also add error handling and overflow. If an illegal entry is made before a numeric than an error code of "1" will be sent back, if overflow occurs, that is number bigger then 65535, then error code of "2" will be sent back. If no error then "0" is sent back.
Make sure the main function will continue to loop until the user enters a “n” or “N” for NO, the main should test the error code returned from the function called “ReadInt” and display appropriate error messages or display the number if there is no error. Take care in designing the “ReadInt” function, it should be value returning and have a reference parameter. The function needs to process one character at a time from the input buffer and deal with it in a correct fashion. Once the number has been read in, then make sure the input buffer is empty, otherwise the loop in main may not work correct. I know this is not how the extraction works, but lets do it this way.
You do not need to turn in an algorithm with this assignment, but I would advise you to write one. And the debugger may prove helpful as well. You are basically rewriting the extraction operator as it works on integers."
A majority of my code won't make sense as I've been deleting things and adding things like crazy to try everything I can think of.
#include <iostream>
#include <CTYPE.h>
using namespace std;
int ReadInt (unsigned short int &UserIn);
int main()
{
int Error;
unsigned short int UserInput;
char RepeatProgram;
do
{
Error=ReadInt(UserInput);
if (Error==0)
cout << "Number is " << UserInput << endl;
else if (Error==1)
cout << "Illegal Data Entry\n";
else if (Error==2)
cout << "Numerical overflow, number too big\n";
cout << "Continue? n/N to quit: ";
cin >> RepeatProgram;
cout << endl;
} while (RepeatProgram!='N' && RepeatProgram!='n');
}
int ReadInt (unsigned short int &UserIn)
{
int Err=0;
char TemporaryStorage;
long int FinalNumber=0;
cout << "Enter a number: ";
//cin.ignore(1000, !' '); this didn't work
cin.get(TemporaryStorage);
cout << TemporaryStorage;//I'm only displaying this while I test my ideas to see if they are working or not, before I move onto the the next step
cout << endl;
return Err;
}
I really appreciate any help I may get and hope I don't give the impression that I'm looking for a full free solution to the whole problem. I want to do this on my own, I'm just lot on this beginning.
As a preface, I want to state that this is a question made by a student, but unlike most of their type, it is a quality question that merits a quality answer, so I'll try to do it ;). I won't try to just answer your concrete question, but also to show you other slight problems in your code.
First of all, let's analyze your code step by step. More or less like what a debugger would do. Take your time to read this carefully ;)...
#include <iostream>
#include <CTYPE.h>
Includes headers <iostream> and <ctype.h> (the uppercase works because of some flaws/design-decisions of NTFS in Windows). I'ld recommend you to change the second line to #include <cctype> instead.
using namespace std;
This is okay for any beginner/student, but don't get an habit of it! For the purposes of "purity", I would explicitly use std:: along this answer, as if this line didn't existed.
int ReadInt (unsigned short int &UserIn);
Declares a function ReadInt that takes a reference UserIn to type unsigned short int and returns an object of type int.
int main()
{
Special function main; no parameters, returns int. Begin function.
int Error;
unsigned short int UserInput;
char RepeatProgram;
Declares variables Error, UserInput, and RepeatProgram with respective types int, unsigned short int, and char.
do
{
Do-while block. Begin.
Error=ReadInt(UserInput);
Assign return value of ReadInt of type int called with argument UserInput of type int& to variable Error of type unsigned short int.
if (Error==0)
std::cout << "Number is " << UserInput << endl;
If Error is zero, then print out UserInput to standard output.
else if (Error==1)
std::cout << "Illegal Data Entry\n";
else if (Error==2)
std::cout << "Numerical overflow, number too big\n";
Otherwise, if an error occurs, report it to the user by means of std::cout.
std::cout << "Continue? n/N to quit: ";
std::cin >> RepeatProgram;
Query the user if he/she wants to continue or quit. Store the input character in RepeatProgram of type char.
std::cout << std::endl;
Redundant, unless you want to add padding, which is probably your purpose. Actually, you're better off doing std::cout << '\n', but that doesn't matters too much.
} while (RepeatProgram!='N' && RepeatProgram!='n');
Matching expression for the do-while block above. Repeat execution of the given block if RepeatProgram is neither lower- or uppercase- letter N.
}
End function main. Implicit return value is zero.
int ReadInt (unsigned short int &UserIn)
{
Function ReadInt takes a reference UserIn to unsigned short int and returns an object of type int. Begin function.
int Err=0;
char TemporaryStorage;
long int FinalNumber=0;
Declares variables Err, TemporaryStorage, and FinalNumber of respective types int, char, and long int. Variables Err and FinalNumber are initialized to 0 and 0, respectively. But, just a single thing. Didn't the assignment said that the output number be stored in a unsigned short int? So, better of this...
unsigned short int FinalNumber = 0;
Now...
std::cout << "Enter a number: ";
//std::cin.ignore(1000, !' '); this didn't work
Eh? What's this supposed to be? (Error: Aborting debugger because this makes no logic!**). I'm expecting that you just forgot the // before the comment, right? Now, what do you expect !' ' to evaluate to other than '\0'? istream::ignore(n, ch)will discard characters from the input stream until either n characters have been discarded, ch is found, or the End-Of-File is reached.
A better approach would be...
do
std::cin.get(TemporaryStorage);
while(std::isspace(TemporyStorage));
Now...
std::cin.get(TemporaryStorage);
This line can be discarded with the above approach ;).
Right. Now, where getting into the part where you obviously banged your head against all solid objects known to mankind. Let me help you a bit there. We have this situation. With the above code, TemporaryStorage will hold the first character that is not whitespace after the do-while loop. So, we have three things left. First of all, check that at least one digit is in the input, otherwise return an error. Now, while the input is made up of digits, translate characters into integers, and multiply then add to get the actual integer. Finally, and this is the most... ahem... strange part, we need to avoid any overflows.
if (!std::isdigit(TemporaryStorage)) {
Err = 1;
return Err;
}
while (std::isdigit(TemporaryStorage)) {
unsigned short int OverflowChecker = FinalNumber;
FinalNumber *= 10; // Make slot for another digit
FinalNumber += TemporaryStorage - '0'; '0' - '0' = 0, '1' - '0' = 1...
// If an unsigned overflows, it'll "wrap-around" to zero. We exploit that to detect any possible overflow
if (FinalNumber > 65535 || OverflowChecker > FinalNumber) {
Err = 2;
return Err;
}
std::cin.get(TemporaryStorage);
}
// We've got the number, yay!
UserIn = FinalNumber;
The code is self-explanatory. Please comment if you have any doubts with it.
std::cout << TemporaryStorage;//I'm only displaying this while I test my ideas to see if they are working or not, before I move onto the the next step
cout << endl;
return Err;
Should I say something here? Anyway, I already did. Just remember to take that std::couts out before showing your work ;).
}
End function ReadInt.
You can skip leading whitespace from a stream using std::ws. For example:
std::cin >> std::ws;
This use of >> just invokes the manipulator std::ws on the stream. To meet the teacher's requirements you can invoke it directly:
std::ws(std::cin);
Formatted input automatically skips whitespace. Note that should also always check whether input was successful:
if (std::cin.get(TemporaryStorage)) {
...
}

Cannot get ispunct or isspace to work, but isupper works fine. Can you help me?

This code only outputs the number of capital letters. It always outputs numMarks and numSpaces as 0. I've also tried sentence.c_str() with the same results. I cannot understand what's happening.
cout << "Please enter a sentence using grammatically correct formatting." << endl;
string sentence = GetLine();
int numSpaces = 0;
int numMarks = 0;
int numCaps = 0;
char words[sentence.length()];
for(int i = 0; i < sentence.length(); ++i)
{
words[i] = sentence[i];
if(isspace(words[i]) == true)
{
numSpaces++;
}
else if(ispunct(words[i]) == true)
{
numMarks++;
}
else if(isupper(words[i]) == true)
{
numCaps++;
}
}
cout << "\nNumber of spaces: " << numSpaces;
cout << "\nNumber of punctuation marks: " << numMarks;
cout << "\nNumber of capital letters: " << numCaps;
Edit: Fixed the problem. My compiler is weird. All I had to do was remove == true And it worked perfectly. Thanks for the information though. Now I know for the future
The functions isspace, ispunct, isupper that you are using have return type int. They return 0 if it is not a match, and non-zero if it is a match. They don't necessarily return 1, so testing == true may fail even though the check succeeded.
Change your code to be:
if ( isspace(words[i]) ) // no == true
and it should start working properly (so long as you don't type any extended characters - see below).
Further info: there are two different isupper functions in C++ (and the same for the other two functions). The are:
#include <cctype>
int isupper(int ch)
and
#include <locale>
template< class charT >
bool isupper( charT ch, const locale& loc );
You are currently using the first one, which is a legacy function coming from C. However you are using it incorrectly by passing a char; the argument must be in the range of unsigned char. Related question.
So to fix your code properly, choose one of the following two options (including the right header):
if ( isupper( static_cast<unsigned char>(words[i]) ) )
or
if ( isupper( words[i], locale() ) )
Other things: char words[sentence.length()]; is illegal in Standard C++; array dimensions must be known at compile-time. Your compiler is implementing an extension.
However this is redundant, you could just write sentence[i] and not use words at all.
Please change your code to
char c;
...
c = sentence[i];
if(isspace(c))
{
++numSpaces;
}
...
isspace returns zero if it is not a space or tab, but you can not assume that it is always returns 1 if it a space or tab. From http://www.cplusplus.com/reference/cctype/isspace/, it says, "A value different from zero (i.e., true) if indeed c is a white-space character. Zero (i.e., false) otherwise."
But if you test it with true, true is converted to 1 and the test fails because for example, on my machine, it returns 8 for a space.
Two things to consider.
First I would use " else if(ispunct(words[i]) !=0 )", instead of comparing the return of the function against true. Thissince the functions return an integer. The value of the integer returned might not match the case of being equal to whatever true is defined in your platform or compiler.
My second suggestion is to check your locale. In unix you can use the "locale" command. In windows you can ask google how to check your locale, for instance for windows 7.
https://www.java.com/en/download/help/locale.xml
If your locale is a "wide character" locale, you might need to use iswpunct (wint_t wc) instead of ispunct(int c).
I hope this helps