Parsing and looping on user Yes/No console input in C# - c#

I'm writing a method that I will use in another code so I'm writing it separately first.
The code returns a boolean based on the first char of the input (yes, Yes, y, yeah, No, ...). But when the input is different from Y or N it starts acting up. In eg3 It stays in the loop until The Console.Read encounters a Y or an N or there are no chars left. In the last case, it will ask again for input.
Am I using the Console.Read wrong or is there another error in my code?
Thanks in advance
EDIT: Is the solution, in the end, an efficient one?
eg1:
Yes or No?
YeahIthinkso <--my input
True
eg2:
Yes or No?
Negative <--my input
False
eg3:
Yes or No?
Idontknow <--my input
You can only answer with Yes or No
Yes or No?
You can only answer with Yes or No
Yes or No?
You can only answer with Yes or No
Yes or No?
False
The Code:
static void Main(string[] args)
{
char YN = 'x';
bool ans = false;
while (YN == 'x')
{
Console.WriteLine("Yes or No?");
YN = char.ToUpper(Convert.ToChar(Console.Read()));
switch (YN)
{
case 'Y':
ans = true;
break;
case 'N':
ans = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
YN = 'x';
break;
}
}
Console.WriteLine(ans);
Alternate solution based on #StuartLC's answer:
bool ans = true, loop = true;
do
{
switch (Console.ReadLine().ToUpper().FirstOrDefault())
{
case 'Y':
ans = true;
loop = false;
break;
case 'N':
ans = false;
loop = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
break;
}
} while (loop==true);
Console.WriteLine(ans);

As per #Lasse's comment - if the user types in multiple characters, you'll loop on each character in the string typed in by the user, resulting in the printing a new line of output for each character the user typed. Instead, use of ReadLine will parse the input as a single string, and then the Linq extension FirstOrDefault() will safely obtain the first char in the string:
YN = char.ToUpper(Console.ReadLine().FirstOrDefault());
As an aside, instead of starting a while loop with a forced false condition, C# also supports a do-while loop syntax which fits your requirement better, i.e. at least one iteration through the loop, with a check at the end of the loop:
do
{
Console.WriteLine("Yes or No?");
YN = char.ToUpper(Console.ReadLine().FirstOrDefault());
switch (YN)
{
case 'Y':
ans = true;
break;
case 'N':
ans = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
YN = 'x';
break;
}
}
while (YN == 'x');
Re OP's follow up question
Can I now remove the 'YN' completely and put a switch (Console.ReadLine().FirstOrDefault().ToUpper()) in the beginning and a while (ans == true || ans ==false) in the end?
Not quite - since ans is a boolean, it can only have two states (true or false), and you need to model at least 3 states (true, false, and invalid). Although you could use a nullable boolean (bool?) to model the 3 states (where null = invalid), I personally dislike using null to indicate something isn't known, as this invariably leads to the NullReferenceException abyss, and C# hasn't (yet) opted for the "Option" type / Monad (like Java's Optional).
If you're OK with C#8 pattern matching and tuples, you could possibly make the code a bit more concise and refactored as follows, by splitting out the concerns of 'is the input valid' and 'what is the valid input value'?. You could also refactor the switch expression into it's own method to split out the concerns of 'UI' from the parsing logic, which is always a good idea.
static void Main(string[] args)
{
bool ans;
bool isValid;
do
{
Console.WriteLine("Yes or No?");
(ans, isValid) = ParseInput(Console.ReadLine());
if (!isValid)
{
Console.WriteLine("You can only answer with Yes or No");
}
}
while (!isValid);
Console.WriteLine(ans);
(bool ans, bool isValid) ParseInput(string input) =>
char.ToUpper(input.FirstOrDefault()) switch
{
'Y' => (true, true),
'N' => (false, true),
_ => (default, false)
};
}

Related

c# loop until Console.ReadLine = 'y' or 'n'

I'm fairly new to c#, and writing a simple console app as practice. I want the application to ask a question, and only progress to the next piece of code when the user input equals 'y' or 'n'. Here's what I have so far.
static void Main(string[] args)
{
string userInput;
do
{
Console.WriteLine("Type something: ");
userInput = Console.ReadLine();
} while (string.IsNullOrEmpty(userInput));
Console.WriteLine("You typed " + userInput);
Console.ReadLine();
string wantCount;
do
{
Console.WriteLine("Do you want me to count the characters present? Yes (y) or No (n): ");
wantCount = Console.ReadLine();
string wantCountLower = wantCount.ToLower();
} while ((wantCountLower != 'y') || (wantCountLower != 'n'));
}
I'm having trouble from string wantCount; onwards. What I want to do is ask the user if they want to count the characters in their string, and loop that question until either 'y' or 'n' (without quotes) is entered.
Note that I also want to cater for upper/lower case being entered, so I image I want to convert the wantCount string to lower - I know that how I currently have this will not work as I'm setting string wantCountLower inside the loop, so I cant then reference outside the loop in the while clause.
Can you help me understand how I can go about achieving this logic?
You could move the input check to inside the loop and utilise a break to exit. Note that the logic you've used will always evaluate to true so I've inverted the condition as well as changed your char comparison to a string.
string wantCount;
do
{
Console.WriteLine("Do you want me to count the characters present? Yes (y) or No (n): ");
wantCount = Console.ReadLine();
var wantCountLower = wantCount?.ToLower();
if ((wantCountLower == "y") || (wantCountLower == "n"))
break;
} while (true);
Also note the null-conditional operator (?.) before ToLower(). This will ensure that a NullReferenceException doesn't get thrown if nothing is entered.
If you want to compare a character, then their is not need for ReadLine you can use ReadKey for that, if your condition is this :while ((wantCountLower != 'y') || (wantCountLower != 'n')); your loop will be an infinite one, so you can use && instead for || here or it will be while(wantCount!= 'n') so that it will loops until you press n
char charYesOrNo;
do
{
charYesOrNo = Console.ReadKey().KeyChar;
// do your stuff here
}while(char.ToLower(charYesOrNo) != 'n');

Not all code paths return a value #2

I am looking to return the bool containsVowel into my main method, where if a word contains a vowel it will allow it to be stored into an array. When I use the code below an error comes up over the method name saying "not all code paths return a value". I've also tried not using the for loop, as well as the char array - it works fine then but if I enter a vowel into the form it still says no vowel has been found.
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
break;
default:
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
break;
}
}
}
Given your method has the return type bool instead of void, all code paths must return a value. The C# compiler tries to very naively evaluate all code paths to see if your method returns a value from all paths.
Your method looks like this to the compiler:
start
for
switch
case
return
default
return
end
The switch is covered alright thanks to the default, but the for is a conditional branch as well. If you pass an empty string, the for won't be entered (i = 0 and word.Length = 0, so i < word.Length is false from the beginning), so after the for, there needs to be a return statement as well.
You can massively improve the code by doing the following:
public bool ContainsVowel(string word)
{
if (string.IsNullOrEmpty(word))
{
return false;
}
var upperCaseCharacters = word.ToUpper().ToCharArray();
foreach (var character in upperCaseCharacters)
{
switch (character)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
}
}
return false;
}
This code checks whether the string being passed is null or empty, and then immediately returns false (fail fast). Then it converts the string to upper case once, and loops over the character array with a foreach(), which is the preferred way to iterate over a collection anyway. Then it doesn't use an intermediate variable to store the result, as you can return true as soon as you encounter a vowel.
Finally, when no vowel has been encountered after iterating over all characters, the method returns false.
Then at the call site, you can do this:
if (!ContainsVowel(word))
{
MessageBox.Show("Word must contain a vowel", "Error");
}
Because methods like this should not contain UI logic such as message boxes.
This code should do.First, check for a null word value.Then you must convert your word to upper in order to detect all vowels, being this upper or lower. Then, you must only check for a vowel, because if one is present the method should return true.If,after checking all the letters, the method has not detected a vowel,show error message and return false :
private static bool vowel(string word)
{
if (word == null) return false;
char[] wordChar = word.ToUpper().ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (word[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
break;
}
}
Console.WriteLine("Word must contain a vowel");
return false;
}
As you can see, this method always returns a value. In your code, if the word passed is empty,it would not enter the forloop, so it won't return anything.
Your code only checked the first character of the string, since it jumped out of the loop if it was vowel or not. This should fix it, but for a better approach, check #Sr82's answer
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
//if you find a vowel, you can return true straight away
}
}
//if you dont find one, let the for loop finish. If it reaches this code you know
//there was no vowel found and you can proceed with your error report
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
}
This simple method should work if you can use Linq. The Intersect method takes two char sequences and returns a sequence of char that exists in both of them. The Any method returns true if there is anything in the sequence.
using System.Linq;
void Main()
{
string s = "Word";
if (hasVowels(s))
{
Console.WriteLine("Word has vowels");
}
else
{
Console.WriteLine("Word does not have vowels");
}
}
//The `Intersect` method takes two `char` sequences and returns a sequence
//of `char` that exists in both of them. The `Any` method returns true if
//there is anything in the resulting sequence.
private bool hasVowels(string word)
{
string vowels = "AEIOUaeiou";
return word != null && word.Intersect(vowels).Any();
}
If you're trying to check whether a string contains a vowel and that's it, you can do something like this:
private bool vowel(string word)
{
return word.ToLower().Any(x => "aeiou".Contains(x));
}
This uses LINQ and specifically the Any method to determine if the string contains any vowels. It would be good to set the string to all lower case so you can check whether a vowel is present irrespective of case.

Try {} Catch {} Not working as it should in C#

I have a try {} catch {} for each line my user inputs something, this is to ensure it's in the right format/range/acceptable. However it doesn't seem to... well work! Here is one of my examples.
string userAnswer;
bool errorHandling = true;
while (errorHandling){
try{
userAnswer = Console.ReadLine();
if (userAnswer == "1") {
singleGrade.grapher(acount, bcount, ccount, dcount, ecount, fcount);
}else{
if (userAnswer == "2"){
readIn.Classes(SingleGrade[1]);
}else{
if (userAnswer == "3"){
countAll.allGrades(multiGrade);
} else{
errorHandling = false;
}
}
}
}
catch (FormatException a){
Console.WriteLine(a.Message);
//Console.WriteLine("Error - please enter a number between 1 & 6.");
}
} // end of While loop
If someone could please explain to me why no error is caught when an invalid number is placed/not in the correct format.
There is no FormatException being thrown because valid strings are being entered. If you were to convert the user input to an integer, for example, that would throw a FormatException. But since you are leaving it as a string, no exception is being thrown.
However, since you are really only trying to restrict user input, and nothing truly exceptional is happening here, you should probably just handle it through you application logic.
What you are really looking for is probably something like this:
bool errorHandling = true;
while (errorHandling)
{
string userAnswer = Console.ReadLine();
switch (userAnswer)
{
case "1":
singleGrade.grapher(acount, bcount, ccount, dcount, ecount, fcount);
errorHandling = false;
break;
case "2":
readIn.Classes(SingleGrade[1]);
errorHandling = false;
break;
case "3":
countAll.allGrades(multiGrade);
errorHandling = false;
break;
// cases for 4, 5, and 6...
default:
Console.WriteLine("Error - please enter a number between 1 & 6.");
break;
}
} // end of While loop
You're not throwing and exception when the number is invalid, you're just handling a bunch of if statements - thus, since there is no throw statement, nothing can hit the catch statement, and insteadyou're seeing what we colloquially call a "Fall through error" - logic is proceeding past where it should.
If you want an exception when input is invalid, just add a throw new FormatException to the end of the if statements, so you get a "if we make it here there's a problem" behaviour.
The reason is that you are grabbing a string, and not trying to convert it to anything else, so there is no FormatException.
I would get away from using the try / catch for input validation, and instead use the TryParse method of the Int type.
What I usually do is write a separate function to get validated input from the user, then call that from my main code. In this case, since you have upper and lower bound requirements, you might consider writing something like this:
public static int GetBoundedIntFromUser(int minVal, int maxVal)
{
if (minVal >= maxVal)
{
throw new ArgumentException("minVal must be less than maxVal.");
}
int input;
while (true)
{
Console.Write("Please enter a number from {0} to {1}: ", minVal, maxVal);
if (int.TryParse(Console.ReadLine(), out input)
&& input >= minVal && input <= maxVal)
{
break;
}
Console.WriteLine("That input is not valid. Please try again.");
}
return input;
}
Then you can just call it from your main code (without any try catch), like:
switch (GetBoundedIntFromUser(1, 3))
{
case 1:
singleGrade.grapher(acount, bcount, ccount, dcount, ecount, fcount);
break;
case 2:
readIn.Classes(SingleGrade[1]);
break;
case 3:
countAll.allGrades(multiGrade);
break;
default:
break;
}

c# if statement inside while loop how to break out if user inputs valid data

I would like to know what to put in the if statement brackets to tell the program, if x or y equals a double, it can break out and continue carrying out the rest of my code.
Any suggestions?
while (true)
{
Console.Write("I need to pour this much from this one: ");
string thisOne = Console.ReadLine();
Double.TryParse(thisOne, out x);
if ( /* Here I want to put "x is a number/double*/ )
{
break;
}
}
while (true)
{
Console.Write("I need to pour this much from that one: ");
string thatOne = Console.ReadLine();
Double.TryParse(thatOne, out y);
if (/* Here I want to put "y is a number/double*/)
{
break;
}
}
TryParse returns a boolean to say whether the parse was successful
if (Double.TryParse(thatOne, out y))
{
break;
}
From documentation
A return value indicates whether the conversion succeeded or failed.
Double.TryParse returns a boolean, perfect fit for your if statement
if (Double.TryParse(thatOne, out y)) {
break;
}
You have a misconception about TryParse(). You want to check if x is a double. somewhere above in your code, you did not post it here there is probably a line like double x = 0;.
You defined x and y already as double. You want to check if your input which is string can be parsed to double:
The shorthand version is this:
if (Double.TryParse(thatOne, out x))
{
break;
}
This can also be written as:
bool isThisOneDouble = Double.TryParse(thisOne, out x);
if (isThisOneDouble)
{
break;
}
If you really want to check if a variable is of certain type without trying to parse it, try it like this:
double x = 3;
bool isXdouble = x.GetType() == typeof(double);
or
double x = 3;
if(x.GetType() == typeof(double))
{
// do something
}
According to the documentation, TryParse returns true if parsing succeeded so just put your tryparse into your if statement.
Control your loop with a bool, set the bool false when your condition is met...
bool running = true;
while (running)
{
Console.Write("I need to pour this much from this one: ");
string thisOne = Console.ReadLine();
if (Double.TryParse(thisOne, out y))
{
running = false
}
}

Rewrite IsHexString method with RegEx

I got a method that checks if a string is a valid hex string:
public bool IsHex(string value)
{
if (string.IsNullOrEmpty(value) || value.Length % 2 != 0)
return false;
return
value.Substring(0, 2) == "0x" &&
value.Substring(2)
.All(c => (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
}
The rules are:
The expression must be composed of an even number of hexadecimal digits (0-9, A-F, a-f). The characters 0x must be the first two characters in the expression.
I'm sure it can be rewriten in regex in a much cleaner and more efficient way.
Could you help me out with that?
After you updated your question, the new regex that works for you should be:
^0x(?:[0-9A-Fa-f]{2})+$
Where I use (?: for non-capturing grouping for efficiency. The {2} means that you want two of the previous expression (i.e., two hex chars), the + means you want one or more hex characters. Note that this disallows 0x as a valid value.
Efficiency
"Oded" mentioned something about efficiency. I don't know your requirements, so I consider this more an exercise for the mind than anything else. A regex will make leaps as long as the smallest matching regex. For instance, trying my own regex on 10,000 variable input strings of size 50-5000 characters, all correct, it runs in 1.1 seconds.
When I try the following regex:
^0x(?:[0-9A-Fa-f]{32})+(?:[0-9A-Fa-f]{2})+$
it runs about 40% faster, in 0.67 seconds. But be careful. Knowing your input is knowing how to write efficient regexes. For instance, if the regex fails, it will do a lot of back-tracking. If half of my input strings has the incorrect length, the running time explodes to approx 34 seconds, or 3000% (!), for the same input.
It becomes even trickier if most input strings are large. If 99% of your input is of valid length, all are > 4130 chars and only a few are not, writing
^0x(?:[0-9A-Fa-f]{4096})+^0x(?:[0-9A-Fa-f]{32})+(?:[0-9A-Fa-f]{2})+$
is efficient and boosts time even more. However, if many have incorrect length % 2 = 0, this is counter-efficient because of back-tracking.
Finally, if most your strings satisfy the even-number-rule, and only some or many strings contain a wrong character, the speed goes up: the more input that contains a wrong character, the better the performance. That is, because when it finds an invalid character it can immediately break out.
Conclusion: if your input is mixed small, large, wrong character, wrong count your fastest approach would be to use a combination of checking the length of the string (instantaneous in .NET) and use an efficient regex.
So, basically you want to check whether the number starts with 0x and continues with a (non-empty) sequence of 0-9 and/or A-F. That can be specified as a regular expression easily:
return RegEx.IsMatch(value, "^0x[0-9A-Fa-f]+$")
I'm not sure why you do the value.Length % 2 != 0 check... isn't "0x1" a valid hexadecimal number? In addition, my function returns false on "0x", whereas yours would return true. If you want to change that, replace + (= one or many) with * (= zero or many) in the regular expression.
EDIT: Now that you've justified your "even number" requirement, I suggest you use Abel's RegEx. If you do that, I suggest that you call your method IsMsSqlHex or something like this to document that it does not follow the "usual" hex rules.
Diatribe: If you are at all concerned about speed forget about Regex. Regex is a NFA and is as such, in most cases, slower than a DFA or hand-written parser.
Ignoring that you asked for Regex here is something that would likely be more efficient (even though your implementation is probably fine - it does allocate strings):
static bool IsHex(string value)
{
if (string.IsNullOrEmpty(value) || value.Length < 3)
return false;
const byte State_Zero = 0;
const byte State_X = 1;
const byte State_Value = 2;
var state = State_Zero;
for (var i = 0; i < value.Length; i++)
{
switch (value[i])
{
case '0':
{
// Can be used in either Value or Zero.
switch (state)
{
case State_Zero: state = State_X; break;
case State_X: return false;
case State_Value: break;
}
}
break;
case 'X': case 'x':
{
// Only valid in X.
switch (state)
{
case State_Zero: return false;
case State_X: state = State_Value; break;
case State_Value: return false;
}
}
break;
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
{
// Only valid in Value.
switch (state)
{
case State_Zero: return false;
case State_X: return false;
case State_Value: break;
}
}
break;
default: return false;
}
}
return state == State_Value;
}
If I can garner correctly as to what you are trying to achieve maybe this function will suite your needs better:
static bool ParseNumber(string value, out int result)
{
if (string.IsNullOrEmpty(value))
{
result = 0;
return false;
}
if (value.StartsWith("0x"))
return int.TryParse(value.Substring(2), NumberStyles.AllowHexSpecifier, null, out result);
else
return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
}
Just for kicks I went and profiled:
Abel's Regex without static readonly caching as I described on Heinzi's answer.
Abel's Regex with static readonly caching.
My implementation.
Results on my laptop (Release/no debugger):
Regex with no compiled/cached Regex took 8137ms (2x cached, 20x hand-written)
Regex with compiled/cached Regex took 3463ms (8x hand-written)
Hand-written took 397ms (1x)

Categories