Im working with ConsoleKeyInfo in C# but i have problems with Console.ReadKey when I try to write numbers greater than 9 in the console, for example
ConsoleKeyInfo number;
Console.Write("Write a number: ");
number = Console.ReadKey();
If i want to write 10 or 11... the console only reads the "1"
I dont wanna use Console.ReadLine because I don want to press "Enter" for each number.
Is there another way to use Console.ReadKey to wait maybe 1 second before continue?
Thanks
As the comments on the question say, Console.ReadKey only reads a single key by definition. You will need to use a different function if you want to get more input from the console. Try something like this, for instance:
Console.Write("Write a number: ");
string line = Console.ReadLine();
int num = 0;
if (line != null)
num = int.Parse(line);
That's a start, with minimal error checking. See what you can get from there.
The best you can do is use Console.ReadLine(). There's no way the program will know you have finished the number.
UPDATE
If you have a fixed length number (i.e. a 13-digit ISBN), you can use ReadKey, but like this:
string isbn = "";
while (isbn.Length < 13)
{
isbn += Console.ReadKey().KeyChar;
}
The idea is that you have to call cki = Console.ReadKey(true) multiple times.
ConsoleKeyInfo cki;
string userNumber = "";
while(true)
{
System.Console.WriteLine("Press more than one key:");
cki = Console.ReadKey(true);
if(cki.Key == ConsoleKey.Escape)
{
System.Console.WriteLine("GOOD BYE!");
Environment.Exit(0);
}
else
{
if(char.IsNumber(cki.KeyChar) || cki.Key == ConsoleKey.Enter)
{
while(char.IsNumber(cki.KeyChar))
{
Console.Write(cki.KeyChar);
userNumber += (cki.KeyChar).ToString();
cki = Console.ReadKey(true); // !!! Main idea
}
System.Console.WriteLine();
System.Console.WriteLine($"Your number is: {userNumber}");
Environment.Exit(0);
}
else
{
System.Console.WriteLine("Wrong symbol! Input a number!");
}
}
}
Related
I'm a complete newbie when it comes to C#, and I'm sure this has a very simple answer:
I've written a super simple piece of code to enable a user to pop in a number and receive the square of that number back out. I've also enabled the user to decide whether they want to play or not, with Yes, No, or 'else' triggering different outcomes.
The 'Yes' input works just fine, with the program working as expected. However, the 'No' or 'else' response seems to require the user to enter a response a second time before triggering the outcome properly.
I can't see why it would be looking for a response twice in a row, so if you can offer any help I'd really appreciate it.
Here's the code I'm using:
namespace Playground
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("If you enter a number, I'll report back the number squared. Would you like to play? Enter Yes or No");
int x = 100;
while (x == 100)
{
if (Console.ReadLine() == "Yes")
{
Console.WriteLine("Great! Enter a number");
int initial = Convert.ToInt32(Console.ReadLine());
Console.WriteLine(initial * initial);
Console.WriteLine("Would you like to try again?");
}
else if(Console.ReadLine() == "No")
{
Console.WriteLine("Ok, goodbye! Press any key to Exit");
Console.ReadLine();
Environment.Exit(0);
}
else
{
Console.WriteLine("Sorry, I didn't understand that. Please enter Yes or No");
}
}
}
}
}
When running into the first if the condition run the function Console.ReadLine() so you have to write the answer. This is going to happen also in the else if (Console.ReadLine() == "No").
What you have to do is save the user input only once and decide what to do from that answer.
The code is:
int x = 100;
while (x == 100)
{
string answer = Console.ReadLine();
if (answer == "Yes")
{
Console.WriteLine("Great! Enter a number");
int initial = Convert.ToInt32(Console.ReadLine());
Console.WriteLine(initial * initial);
Console.WriteLine("Would you like to try again?");
}
else if(answer == "No")
{
Console.WriteLine("Ok, goodbye! Press any key to Exit");
Console.ReadLine();
Environment.Exit(0);
}
else
{
Console.WriteLine("Sorry, I didn't understand that. Please enter Yes or No");
}
}
You can also use the switch statement:
int x = 100;
while (x == 100){
string answer = Console.ReadLine();
switch (answer){
case "Yes":
Console.WriteLine("Great! Enter a number");
int initial = Convert.ToInt32(Console.ReadLine());
Console.WriteLine(initial * initial);
Console.WriteLine("Would you like to try again?");
break;
case "No":
Console.WriteLine("Ok, goodbye! Press any key to Exit");
Console.ReadLine();
Environment.Exit(0);
break;
default:
Console.WriteLine("Sorry, I didn't understand that. Please enter Yes or No");
break;
}
}
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.
If i use readline() i could not use readkey(); to avoid read twice
Console.Write("Your selection is: ");
bSelAns = int.TryParse(Console.ReadLine(), out iSelection);
string ans =iSelection.ToString();
i do readline here so i try to avoid readkey();
ConsoleKey keychar ;
keychar = ConsoleKey.Q;
string y = "q";
I need to bool exit when i enter q;
if (bSelAns ==false)
{
if (ans == y)
{
break;
}
Console.WriteLine("You have entered an invalid strinng, please enter a number or string from the list.\n");
}
Sorry, I think I misunderstood what you were looking for.
So I guess what you want is to be able to use ReadKey and Readline together?
that is impossible as ReadKey end after the first char, and readLine will end when Enter is pressed.. however, you could make ReadKey act as a ReadLine, and then you can check if Q is pressed for each keypress.
to do that you need a buffer, that saves all keypresses, and each time 'Enter' is pressed, it empties the Buffer to the screen.
Edit: Logic for 'Enter' changed to reset if pressed, even if the buffer is empty
Edit2: Added Comments
using System;
class Program
{
public static void Main(string[] args){
var buffer = "";
while(true){
//Only show if buffer is empty
if(buffer.Length == 0){
Console.WriteLine("enter key:");
}
var key = Console.ReadKey(false);
//Only Exit if buffer is empty and Q is pressed (if Q is the first Key pressed)
if(buffer.Length == 0 && key.Key == ConsoleKey.Q){
Console.WriteLine();
Console.WriteLine("the key was 'Q' goodbye");
return;
}
//Every time Enter is pressed, Use and Flush the buffer
else if(key.Key == ConsoleKey.Enter)
{
if(buffer.Length > 0){
Console.WriteLine();
Console.WriteLine("buffer have {0}", buffer);
if(int.TryParse(buffer, out int number)){
Console.WriteLine("{0} is a number!", number);
}
}
buffer = "";
}
//else will capture everything else pressed, and add it to buffer
else{
buffer += key.KeyChar;
}
}
}
}
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 :-) ).
I'm having trouble with looping my program so that it breaks out of the loop if user enters "quit" or "exit".
When I type any string, my program crashes as it tries to parse the string input to an int. Any advice?
namespace DiceRolling
{
/* We’re going to write a program that makes life easier for the player of a game like this. Start the
program off by asking the player to type in a number of dice to roll.Create a new Random
object and roll that number of dice.Add the total up and print the result to the user. (You should
only need one Random object for this.) For bonus points, put the whole program in a loop and allow them to keep typing in numbers
until they type “quit” or “exit”. */
class DiceRolling
{
static void RollDice(int numTries)
{
Random random = new Random();
//Console.Write("Please enter the number of dice you want to roll: ");
//int numTries = Convert.ToInt32(Console.ReadLine());
int sum = 0;
int dieRoll;
for (int i = 1; i <= numTries; i++)
{
dieRoll = random.Next(6) + 1;
Console.WriteLine(dieRoll);
sum += dieRoll;
}
Console.WriteLine("The sum of your rolls equals: " + sum);
}
static void Main(string[] args)
{
while (true)
{
Console.Write("Please enter the number of dice you want to roll: ");
string input = Console.ReadLine();
int numTries = Convert.ToInt32(input);
//bool isValid = int.TryParse(input, out numTries);
RollDice(numTries);
Console.ReadKey();
if (input == "quit" || input == "exit")
break;
}
}
}
}
Tigrams is pretty close, this is slightly better
Console.Write("Please enter the number of dices you want to roll: ");
string input = Console.ReadLine();
while (input != "quit" && input != "exit")
{
int numTries = 0;
if (int.tryParse(input, out numTries)
{
RollDice(numTries);
}
Console.Write("Please enter the number of dices you want to roll: ");
input = Console.ReadLine();
}
tried to make it as similar as possible to what you have
while (true)
{
Console.Write("Please enter the number of dices you want to roll: ");
string input = Console.ReadLine();
int numTries;
if (int.TryParse(input, out numTries))
{
RollDice(numTries);
}
else if(input == "quit" || input == "exit")
{
break;
}
}
while (true)
{
Console.Write("Please enter the number of dices you want to roll: ");
string input = Console.ReadLine();
if (input == "quit" || input == "exit") //check it immediately here
break;
int numTries = 0;
if(!int.TryParse(input, out numTries)) //handle not valid number
{
Console.WriteLine("Not a valid number");
continue;
}
RollDice(numTries);
Console.ReadKey();
}
Try int numTries =int.Parse(input != null ? input: "0");