C# How to make repeated input overlap? - c#

When the input's type is not an integer, the program should fail. However, there are two problems:
After a letter is typed and I get the "not valid" response, then if the next input is a number, it won't accept it and says "not valid".
How can I make it so when a wrong number is inputted, it stays on the same row and just clears the previous input from the screen (and allows for a retry from the same position)?
static void Main(string[] args)
{
int firstNum;
int Operation = 1;
switch (Operation)
{
case 1:
Console.SetCursorPosition(0, 0);
Console.Write("Write a number: ");
firstNum = ReadInteger("");
Console.ReadKey();
break;
}
}
private static int ReadInteger(string title)
{
while (true)
{
if (!string.IsNullOrWhiteSpace(title))
Console.WriteLine(title);
string input = Console.ReadLine();
if (int.TryParse(input, out int result))
return result;
Console.WriteLine("Sorry, not a valid integer value; please, try again.");
Console.ReadKey();
}
}

Ad 1)
Because you have Console.ReadKey at the end of ReadInteger which will be executed as well. So if you hit Console.ReadLine at the line string input = Console.ReadLine(); and enter something that is not a number, int.TryParse will return false. This leads to the error message and the execution of Console.ReadKey. So you should first of all get rid of that Console.ReadKey there.
Ad 2)
You are setting the cursor position before the call of ReadInteger but not within ReadInteger. So if someone enters a text, the end of the input is typically done by pressing enter. You then write a line (with Console.WriteLine). So if you want to have the cursor at the same position, you will have to reset it's position within the loop which is within the ReadInteger method.

How about something like this:
public static int ReadInt(string prompt)
{
Console.Clear();
var length = prompt.Length + 2; //2 is for a colon and a space
var position = length;
Console.Write($"{prompt}: ");
string buffer = String.Empty;
int returnNum = 0;
while (true)
{
Console.SetCursorPosition(position, 0);
var charRead = Console.ReadKey();
if(charRead.KeyChar == '\r')
{
return returnNum;
}
if (!int.TryParse(buffer + charRead.KeyChar, out returnNum))
{
Console.SetCursorPosition(position, 0);
Console.WriteLine(" "); //overwrite
Console.SetCursorPosition(0, 1);
Console.Write("Error: enter only digits");
continue;
}
else
{
buffer += charRead.KeyChar;
++position;
//overwrite any error
Console.SetCursorPosition(0, 1);
Console.Write(" ");
}
}
}
It's not perfect. It doesn't handle typing in too many digits. It doesn't handle backspace. It clears the console to establish position (it doesn't look like you can read the console position, and I'm too lazy to keep track of things).
If you do handle backspace, make sure you don't let users backup too much. It's been years (um, no, decades) since I've done low level console management, but I can remember doing this in CP/M in the early 80s - if you backed up to far, the OS died (which would be a very result in Windows :-) ).

Related

Calculating product of numbers using do while

I'm new to coding and trying my best but I got stuck. Again.
So. I need to calculate the product of some random numbers using do while.
You type the numbers and when you type x, the loop needs to close showing the result. If you ONLY type x, it needs to show "1".
I can't manage to only show "1" when you type "x".
I have this:
int product = 1;
Console.WriteLine();
String input = Console.ReadLine();
do
{
int n = Convert.ToInt32(input);
product = product * n;
input = Console.ReadLine();
} while (!input.ToLower().Equals("x"));
Console.WriteLine(product);
Console.ReadLine();
for and while breaks in the start of the loops and do while break at the end, sometimes you have to break it the middle (here after the input was entered, if it's x) and before the accumulation.
The best way to manage this is an infinite loop for(;;) or while(true) and uses of break:
var product = 1;
for(;;)
{
var input = Console.ReadLine();
if (input is null || input.Equals("x", StringComparison.OrdinalIgnoreCase))
break;
product *= Convert.ToInt32(input);
}
Console.WriteLine(product);
Console.ReadLine();
Or you can try to make it fit (mostly double calls to ReadLine).
A for version that looks ugly:
var product = 1;
for (var input = Console.ReadLine(); input != "x"; input = Console.ReadLine())
{
product *= int.Parse(input);
}
Console.WriteLine(product);
Console.ReadLine();
A while version:
var product = 1;
var input = Console.ReadLine();
while (!input.ToLower().Equals("x"))
{
product *= Convert.ToInt32(input);
input = Console.ReadLine();
}
Console.WriteLine(product);
Console.ReadLine();
Another while version that avoid ReadLine at multiple places:
var product = 1;
string input;
// an affectation actually evaluate to the value affected
// avoid this since it's not easily readable (= and == mismatch)
while ((input = Console.ReadLine()) != "x")
{
product *= Convert.ToInt32(input);
}
Console.WriteLine(product);
Console.ReadLine();
A do while version:
I add it because it's in the question title, otherwise I didn't consider it a good solution.
Based on yassinMi answer.
var product = 1;
var input = "1";
do
{
product *= int.Parse(input);
input = Console.ReadLine();
} while (input != "x");
Console.WriteLine(product);
Console.ReadLine();
A Linq version:
var product = Enumerable.Range(0, int.MaxValue) // many elements
.Select(_ => Console.ReadLine()) // discard them and take the console input
.TakeWhile(s => s != "x") // stop on 'x'
.Select(int.Parse) // parse to int
.Aggregate(1, (a, b) => a * b); // accumulate from 1 and by making the product
Console.WriteLine(product);
Console.ReadLine();
a simple fix: change the line
String input = Console.ReadLine();
to
String input = "1";
otherwise you would use the while loop, or add extra if statement..
Welcome to C#!
Your issue is most likely due to an error while converting a value. You are trying to convert "x" to int, but "x" is a letter, which cannot be converted to a number.
Cause of the issue
The problem of this program is that you are trying to convert a letter to a number. This is not possible in real life, and less possible in programming! A computer is not able to convert a number to a letter, unless you are explaining to it how, or if your handle the error properly.
The following line:
int n = Convert.ToInt32(input);
...Does not check for what has been entered. Convert.ToInt32() will throw an exception if the string? (From Console.ReadLine()) could not be converted.
The fix
To fix this issue, you need to handle the input of the user. Literally anything is accepted by Console.ReadLine() but you want a value that can be converted to a int.
The int type has a built-in function to convert a value without throwing an exception if it couldn't be converted, instead, it returns a bool (Either true or false).
So the following changes need to be brought to your program:
int product = 1;
string ? input;
do {
input = Console.ReadLine();
// Try to parse to integer, if it couldn't be parsed to integer, continue.
if (!int.TryParse(input, out int n)) {
continue;
}
product = product * n;
} while (!input.Equals("x", StringComparison.InvariantCultureIgnoreCase));
Console.WriteLine(product);
// End of the program
Console.WriteLine("Press any key to quit the program.");
Console.ReadKey();
Trying to parse a string to an integer
The method int.TryParse(string? input, out int output); allows you to try to convert a string to a int.
If the method fails, instead of throwing an Exception, it will returns false.
Getting the output value
The output value can be obtained from out int output, out allows you to output a value from a function.
The logic would be that if int.TryParse(...) returns true, the value can be used, otherwise you can continue.
The continue statement tells your loop that the current loop should be skipped and goes to the next one (= At the beginning of your loop).
Other changes
I've brought to your program some changes to make it more readable and easier to edit.
Firstly, you'd want to use Equals(input, StringComparison.InvariantCultureIgnoreCase) instead of ToLowerCase(), the result is the same and it is cleaner! 🙂
Also, you can directly ask the input within your while loop since this will be repeated each time, you just need to position it correctly so the value can be verified.
Final program
ConsoleKeyInfo continueProgram = new ConsoleKeyInfo();
ConsoleKeyInfo continueInput = new ConsoleKeyInfo();
List<int> products = new List<int>();
do
{
Console.Clear();
do
{
Console.Write("\nPlease enter new number . . . ");
if (int.TryParse(Console.ReadLine(), out int result))
{
products.Add(result);
Console.WriteLine("\nNew Product Added!");
}
else
{
Console.WriteLine("\nYou have entered a value that is not an integer!\n");
}
Console.Write("\nDo you want to enter an other value (press Y) or any key to exit . . . ");
continueInput = Console.ReadKey();
Console.WriteLine();
}
while (continueInput.Key == ConsoleKey.Y);
Console.WriteLine();
foreach (var item in products)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
Console.Write("\n\nPress 'Y' to continue or any other key to exit! . . . ");
continueProgram = Console.ReadKey();
} while (continueProgram.Key == ConsoleKey.Y);
The problem is that Console.ReadLine() gets called twice before checking for x. The first input gets eaten up.
What you need to do is inside the loop ask and process the input, and exit the loop when the condition is met.
One way of doing this is by keeping a boolean value called done indicating when to exit the loop'
static void Main(string[] args)
{
int product = 1;
bool done = false;
do
{
Console.WriteLine("Enter number or 'x' to calculate result.");
string input = Console.ReadLine();
done = input.ToLower() == "x";
if (int.TryParse(input, out int value))
{
product *= value;
}
else
{
Console.WriteLine("Input ignored.");
}
} while (!done);
Console.WriteLine($"The product is {product}");
}
Another more succinct way is to use the break; statement
static void Main(string[] args)
{
int product = 1;
do
{
Console.WriteLine("Enter number or 'x' to calculate result.");
string input = Console.ReadLine();
if (input.ToLower() == "x")
{
// this exits the loop
break;
}
if (int.TryParse(input, out int value))
{
product *= value;
}
else
{
Console.WriteLine("Input ignored.");
}
} while (true);
Console.WriteLine($"The product is {product}");
}
Also, it is recommended to use int.TryParse(string, out integer) for better handling weird user inputs instead of Convert.ToInt32(). See the pattern above on how it is used.

How to ignore a line/block of code if the condition is met?

Is there a way to ignore a line/block of code if the condition is met?
I'm doing a C# .NET tutorial, and the application is a number guessing game.
I added a hint option if the user enters a wrong number (else if part):
// While guess is not correct
while (guess != correctNumber)
{
//Get users input
string input = Console.ReadLine();
// Make sure it's a number
if (!int.TryParse(input, out guess))
{
// Print error message
PrintColorMessage(ConsoleColor.Red, "Please use an actual number");
// Keep going
continue;
}
// Cast to int and put in guess
guess = Int32.Parse(input);
// Check if guess is close to correct number
if(guess == correctNumber + 2 || guess == correctNumber - 2)
{
// Tell the user that he is close
PrintColorMessage(ConsoleColor.DarkCyan, "You are close!!");
}
// Match guess to correct number
else if (guess != correctNumber)
{
// Print error message
PrintColorMessage(ConsoleColor.Red, "Wrong number, please try again");
AskForAHint(correctNumber);
}
}
// Print success message
PrintColorMessage(ConsoleColor.Yellow, "You are CORRECT!");
Basically I am asking a user if he wants a hint, and if he writes Y, the hint will be displayed. However, is there an option to display this question only once since this if statement is included in a while loop?
It would be annoying if "Do you want a hint?" question keeps displaying even if the user says Y.
My AskForAHint function:
static void AskForAHint(int num)
{
// Ask user if he wants a hint
Console.WriteLine("Do you want a hint? [Y/N]");
// Take his answer
string ans = Console.ReadLine().ToUpper();
// If the user wants a hint
if (ans == "Y")
{
// First hint number
int beginning = (num - num % 10);
// Second hint number
int finish = beginning + 10;
// Give user a hint
Console.WriteLine("The correct number is somewhere betweer {0} and {1}", beginning, finish);
}
else if (ans == "N")
{
return;
}
}
Thanks
Another way to do it would be to make the number of hints configurable (allowing the caller to specify how many hints they want to let the user ask for), and then keep track of the number of hints given in the method itself.
This would require a slight change to the AskForAHint method, however, since we don't know if the user answered "Y" or "N" to the hint question. Since AskForHint has no return value, we could have it return a bool that indicates how the user responded to the question:
static bool AskForAHint(int num)
{
var answer = GetUserInput("Do you want a hint? [Y/N]: ", ConsoleColor.Yellow);
if (!answer.StartsWith("Y", StringComparison.OrdinalIgnoreCase))
{
return false;
}
var beginning = num - num % 10;
var finish = beginning + 10;
Console.WriteLine($"The correct number is somewhere between {beginning} and {finish}");
return true;
}
Now we can keep track of how many hints the user has received by incrementing a counter in our "Game" method:
// Only ask for a hint if they have any hints (and guesses) remaining
if (hintCount < maxHints && guessCount < maxGuesses)
{
// If they asked for a hint, increase the hint count
if (AskForAHint(correctNumber)) hintCount++;
// If they didn't want a hint, max out hint count so we don't ask again
else hintCount = maxHints;
}
To test out the sample code above, I used this method below, which also allows us to configure how many total guesses the user has, what the min and max values of the range should be, and if they should be given a "directional hint", like "too high!" or "too low!":
private static readonly Random Random = new Random();
private static void PlayGuessingGame(int maxHints = 1, int maxGuesses = 10,
int rangeMin = 1, int rangeMax = 100, bool giveDirectionalHint = true)
{
if (rangeMax < rangeMin) rangeMax = rangeMin;
var correctNumber = Random.Next(rangeMin, rangeMax + 1);
var guessCount = 0;
var hintCount = 0;
WriteMessage("Welcome to the guessing game!", ConsoleColor.White);
WriteMessage("-----------------------------\n", ConsoleColor.White);
WriteMessage($"I'm thinking of a number from {rangeMin} to {rangeMax}. ", ConsoleColor.Green);
WriteMessage("Let's see how many guesses it takes you to guess it!\n", ConsoleColor.Green);
do
{
WriteMessage($"(You have {maxGuesses - guessCount} guesses left)");
var input = GetUserInput("Enter the number I'm thinking of: ", ConsoleColor.White);
int guess;
if (!int.TryParse(input, out guess))
{
WriteMessage("Please enter a whole number", ConsoleColor.Red);
continue;
}
// Only increment guesses if they entered an actual number
guessCount++;
if (guess == correctNumber) break;
if (Math.Abs(guess - correctNumber) == 2)
{
WriteMessage("You are close!!", ConsoleColor.DarkCyan);
}
if (giveDirectionalHint)
{
WriteMessage("Wrong number - too " + (guess < correctNumber ? "low!" : "high!"),
ConsoleColor.Red);
}
else
{
WriteMessage("Wrong number, please try again", ConsoleColor.Red);
}
// Only ask for a hint if they have any hints (and guesses) remaining
if (hintCount < maxHints && guessCount < maxGuesses)
{
// If they asked for a hint, increase the hint count
if (AskForAHint(correctNumber)) hintCount++;
// If they didn't want a hint, max out hint count so we don't ask again
else hintCount = maxHints;
}
} while (guessCount < maxGuesses);
WriteMessage("You are CORRECT!", ConsoleColor.Yellow);
GetKeyFromUser("\nDone! Press any key to exit...");
}
This uses the helper functions:
public static void WriteMessage(string message, ConsoleColor color = ConsoleColor.Gray)
{
Console.ForegroundColor = color;
Console.WriteLine(message);
Console.ResetColor();
}
private static string GetUserInput(string prompt, ConsoleColor color = ConsoleColor.Gray)
{
Console.ForegroundColor = color;
Console.Write(prompt);
Console.ResetColor();
return Console.ReadLine();
}
Output
You can see in the output below, I was only given a single hint. However that, combined with the directional hints, made the game easy to win:
I think you can do an "if" with a counter.
Try It
Int cont = 0; //global
// While guess is not correct
while (guess != correctNumber)
{
//Get users input
string input = Console.ReadLine();
// Make sure it's a number
if (!int.TryParse(input, out guess))
{
// Print error message
PrintColorMessage(ConsoleColor.Red, "Please use an actual number");
// Keep going
continue;
}
// Cast to int and put in guess
guess = Int32.Parse(input);
// Check if guess is close to correct number
if(guess == correctNumber + 2 || guess == correctNumber - 2)
{
// Tell the user that he is close
PrintColorMessage(ConsoleColor.DarkCyan, "You are close!!");
}
// Match guess to correct number
else if (guess != correctNumber)
{
// Print error message
PrintColorMessage(ConsoleColor.Red, "Wrong number, please try again");
if(cont == 0){
AskForAHint(correctNumber);
}
}
}
// Print success message
PrintColorMessage(ConsoleColor.Yellow, "You are CORRECT!");
And in the function add
static void AskForAHint(int num)
{
// Ask user if he wants a hint
Console.WriteLine("Do you want a hint? [Y/N]");
// Take his answer
string ans = Console.ReadLine().ToUpper();
// If the user wants a hint
if (ans == "Y")
{
cont = 1;
// First hint number
int beginning = (num - num % 10);
// Second hint number
int finish = beginning + 10;
// Give user a hint
Console.WriteLine("The correct number is somewhere betweer {0} and {1}", beginning, finish);
}
else if (ans == "N")
{
return;
}
}
Use a Member Variable Boolean, its similar to how you can avoid recursive calls.
private bool alreadyHinted = false;
static void AskForAHint(int num)
{
if (alreadyHinted) return;
alreadyHinted = true;
At some point you will need to set alreadyHinted back to false;

How do I allow the user more attempts as opposed to just restarting the game after catching a format exception?

So, I'm building this little guessing game and I recently added a try/catch to stop the console from crashing if the user inputted a wrong character (besides numbers of course). Right now it works well but it restarts the entire game, I've tried to just have the catch re-run the script starting at the "for" loop, but it breaks and the random number originally generated doesn't match.
public static void StartGame()
{
Console.Clear();
Random ran = new Random();
decimal returnValue = (ran.Next(1, 100));
Console.WriteLine("Welcome to my guessing game. I'm thinking of a number between 1 and 100, can you guess it?");
Console.WriteLine("Hint: 'Way too off' means you're greater than 25 from the answer!");
try
{
for (int guessnumber = 1; guessnumber < 6; guessnumber++)
{
var Guess = Convert.ToInt32(Console.ReadLine());
//The Guess variable is simply whatever the user inputs, which is read and used by the for loop each time it is inputed.
if (Guess == returnValue)
{
Console.WriteLine("Well done!");
Console.ReadKey();
Game.NewGame();
}
else if (Guess < returnValue)
{
Console.WriteLine("Guess higher!");
}
else if (Guess > returnValue)
{
Console.WriteLine("Guess lower!");
}
if (guessnumber == 5)
{
Console.WriteLine($"The correct answer was {returnValue}");
Console.ReadKey();
Game.NewGame();
//This if statement activates when the "guessnumber" counter has reached 5, in which case it will tell the user the correct answer, and end with a return statement.
}
//result is the absolute value of
decimal result = Math.Abs(returnValue - Guess);
if (result > 25)
Console.WriteLine("Way too off!");
if (result < 5)
{
Console.WriteLine("Very close!!");
}
if (result < 2)
{
Console.WriteLine("RED HOT!!!");
}
}
}
catch (System.FormatException)
{
Console.WriteLine("Incorrect character inputted, restarting game. . .");
System.Threading.Thread.Sleep(2000);
StartGame();
}
}
public static void NewGame()
{
Console.Clear();
string input = " ";
Console.WriteLine("Game over, would you like to play again? Y/N");
input = Console.ReadLine();
input = input.ToUpper();
if (input == "Y")
{
Console.WriteLine("Ok! Re-Initializing game...");
StartGame();
}
else
{
System.Environment.Exit(0);
}
}
}
}
As another answer stated, you can just move your try/catch inside the for loop, so the loop continues.
But you really shouldn't be relying on exception handing for something that isn't exceptional, like parsing user input.
Instead, we can use the int.TryParse method to try to parse a string to an int. The nice thing about this method is that it returns true if the parse was successful, and it sets an out parameter to the parsed value.
We can also put it in it's own method that takes in a string to be used to prompt the user, which makes the code more reusable:
public static int GetIntFromUser(string prompt)
{
int result;
bool askedOnce = false;
do
{
if (askedOnce) Console.WriteLine("Invalid input, please try again.");
Console.Write(prompt);
askedOnce = true;
} while (!int.TryParse(Console.ReadLine(), out result));
return result;
}
Now our main code is greatly simplified (also notice that the Random instance should be a class field, not created on every call to StartGame, and notice that the upper bound specified in the call to ran.Next is exclusive, so if you want to include 100 as a possible answer, make the max value 101):
private static Random ran = new Random();
public static void StartGame()
{
Console.Clear();
int randomNumber = ran.Next(1, 101);
Console.WriteLine("Welcome to my guessing game");
Console.WriteLine("I'm thinking of a number between 1 and 100, can you guess it?");
for (int guessCount = 1; guessCount < 6; guessCount++)
{
var guess = GetIntFromUser($"Guess a number (try #{guessCount}): ");
if (guess == randomNumber)
{
Console.WriteLine("Well done!");
Console.ReadKey();
Game.NewGame();
}
else if (guessCount == 5)
{
Console.WriteLine("That was your last guess.");
Console.WriteLine($"The correct answer was: {randomNumber}.");
Console.ReadKey();
Game.NewGame();
}
else
{
decimal result = Math.Abs(randomNumber - guess);
if (result > 25) Console.Write("You're way off! ");
else if (result < 5) Console.Write("Very close!! ");
else if (result < 2) Console.WriteLine("You're RED HOT!!! ");
Console.WriteLine(guess < randomNumber ? "Guess higher!" : "Guess lower!");
}
}
}
Move the Try Catch handling inside for loop. What this does is, that exceptions are handled during each guess iteration.
What you had originally was exception handling for all iterations.
Also, Dont call the StartGame Function again in your catch block. You want to allow 6 (per the for loop) Guesses and not Infinite.
Ex:
for (int guessnumber = 1; guessnumber < 6; guessnumber++)
{
try
{
}
catch (System.FormatException)
{
}
}
The simple answer is as follows:
You don't control application flow with exception handling. Catching exceptions are for exceptional circumstances, a user getting input wrong isn't exceptional... it's guaranteed.
Never ever ever ever ever (+1000) use Convert.... or int.Parse to take user input... Users make mistakes all the time, use the TryParse type methods instead, they return a bool to say if the data was successfully parsed so you can then deal with it.
Eg
var success = int.TryParse(Console.ReadLine(), out var guess);
However, you can take it one step further, and add a loop (add pepper and salt to taste)
int guess = 0;
while(!int.TryParse(Console.ReadLine(), out guess))
Console.WriteLine("Omg you had one job, just enter a number");
Additional Resources
Int32.TryParse Method
Converts the string representation of a number to its 32-bit signed
integer equivalent. A return value indicates whether the operation
succeeded.

how do I write in the same line that the user wrote?

The user enters a numbers and the program should make a sideways graph by writing "-" * the amount of digits in the number, but it writes the "-" a line under the user input
Current output:
Expected output:
static void Main(string[] args)
{
int num1, num2;
Console.WriteLine("how many numbers will you want to enter?");
num1 = int.Parse(Console.ReadLine());
Console.WriteLine("Enter " + num1 + " numbers");
for(; num1 > 0; num1--)
{
num2 = int.Parse(Console.ReadLine());
Hi(num2);
}
}
static void Hi(int num)
{
while(num != 0)
{
num /= 10;
Console.Write("-");
}
Console.WriteLine()
}
You can get and set the cursor position in the console, so if you remember which line it is on before the user presses enter for the number entry, you can put the cursor back on that line.
Also, to print a number of dashes of the length of the input, it is not necessary for the input to be digits (or you would have checked for that).
Something like this should be suitable:
static void Main(string[] args)
{
Console.Write("How many numbers will you want to enter? ");
int num1 = int.Parse(Console.ReadLine());
Console.WriteLine("Enter " + num1 + " numbers");
for (; num1 > 0; num1--)
{
int currentLine = Console.CursorTop;
string num2 = Console.ReadLine();
Console.SetCursorPosition(20, currentLine);
Console.WriteLine(new string('-', num2.Length));
}
Console.WriteLine("\r\n(Press enter to leave program.)");
Console.ReadLine();
}
Sample output:
How many numbers will you want to enter? 4
Enter 4 numbers
1 -
435 ---
What happens long wi-----------------------
(Press enter to leave program.)
Use a method like the following:
public string getKeyBuffer()
{
string buffer = "";
do
{
var charIn = Console.ReadKey(true);
if (charIn.Key == ConsoleKey.Enter) break;
buffer += charIn.KeyChar;
Console.Write(charIn.KeyChar);
} while (true);
return buffer;
}
This will echo each key pressed and then return all the keys pressed once the user presses the enter key without echoing the enter key.
The best solution would be to write to something other than the console, where you would have absolute control over what is displayed and where.
Another solution would be to format a string in your code, then clear the console and write the entire thing each time.
Another solution would be to keep track of where you are and move the console cursor using Console.SetCursorPosition. However, this is rarely a satisfying solution given the existence of nicer output alternatives.
You can move the cursor up one line with Console.CursorTop--;, avoiding the necessity of keeping track of which line you are on.

C# Console Clear Input Line

I have a C# console program that asks the user for a certain word by showing a input prompt. The input is then processed using an switch statement. If the word is correct, the program continues. But if the input doesn't match, the program says "Error: invalid input" and then goes back to the input prompt. The tricky part is that I want the program to clear the input that the user just typed before pressing Enter and prompt the user again, without making another separate prompt below the first one.
Is there a library of some kind in C# that does something like that, or do I have to make a library for it?
One way to do this is to use a combination of Console.SetCursorPosition and Console.Write to set the cursor to the beginning of their response, write enough whitespace to "erase" their reaponse, and then set the cursor back to the beginning again.
For example:
static string GetUserInput(string prompt, List<string> validResponses)
{
Console.Write(prompt);
// Capture the cursor position just after the prompt
var inputCursorLeft = Console.CursorLeft;
var inputCursorTop = Console.CursorTop;
// Now get user input
string input = Console.ReadLine();
while (validResponses != null &&
validResponses.Any() &&
!validResponses.Contains(input, StringComparer.OrdinalIgnoreCase))
{
Console.ForegroundColor = ConsoleColor.Red;
// PadRight ensures that this line extends the width
// of the console window so it erases itself each time
Console.Write($"Error! '{input}' is not a valid response".PadRight(Console.WindowWidth));
Console.ResetColor();
// Set cursor position to just after the promt again, write
// a blank line, and reset the cursor one more time
Console.SetCursorPosition(inputCursorLeft, inputCursorTop);
Console.Write(new string(' ', input.Length));
Console.SetCursorPosition(inputCursorLeft, inputCursorTop);
input = Console.ReadLine();
}
// Erase the last error message (if there was one)
Console.Write(new string(' ', Console.WindowWidth));
return input;
}
In use this might look like:
static void Main(string[] args)
{
var validResponses = new List<string> {"Yes", "No"};
var userInput = GetUserInput("Do you like me? ", validResponses);
if (userInput.Equals("Yes", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("I like you too!");
}
else
{
Console.WriteLine("And all along I thought you had good taste.");
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Here's a sample run of the program. I had to include several screenshots since the response (and then the error message) are cleared on each iteration:
Try this ...
static void Main(string[] args)
{
CheckWord();
Console.ReadKey();
}
private static void CheckWord()
{
while (true)
{
string errorMessage = "Error: invalid input ... enter a valid entry";
string word = Console.ReadLine();
if (word != "word")
{
Console.SetCursorPosition(word.Length +1 , (Console.CursorTop) - 1);
Console.Write(errorMessage);
Console.ReadKey();
Console.SetCursorPosition(0, Console.CursorTop);
for (int i = 0; i <= word.Length + errorMessage.Length +1 ; i++)
{
Console.Write(" ");
}
Console.SetCursorPosition(0, Console.CursorTop);
}
else
{
break;
}
}
}

Categories