Key Listner in a console application using multithreading - c#

I have to create this console based application and I have this problem:
I have created something like a KeyListener using multithreading (can't do simple loop, because there is a second thread running simultaneously).
And the loop in a thread needs to check if pressed key is an integer.
What is that I don't understand?
The way I get this: there is an infinite loop within the thread that tries to capture the input and if the input == 1 it writes text in the console.
What am I missing?
static void KeyRead()
{
do
{
int i = (int) Console.ReadKey().KeyChar;
if (i == 1) {
Console.Out.Write("Key 1 pressed");
}
} while (true);
}
static void Main(string[] args)
{
Thread keyListner = new Thread(new ThreadStart(KeyRead));
keyListner.Start();
}

KeyChar returns value of type char and casting char to int returns unicode value representing that character. But character '1' have unicode value 49, not 1. So you have to modify condition to compare i to be eqaual to 49 instead of 1.
static void KeyRead()
{
do
{
int i = (int)Console.ReadKey().KeyChar;
if (i == 49)
{
Console.Out.Write("Key 1 pressed");
}
} while (true);
}
But it is even beter to avoid this integer conversion altogether and compare character directly:
static void KeyRead()
{
do
{
char c = Console.ReadKey().KeyChar;
if (c == '1')
{
Console.Out.Write("Key 1 pressed");
}
} while (true);
}

Related

Use variable instead of keycode unity

Okay so I'm trying to make a system that picks a random word then it turns that word into a char array. Then it will track if you type the characters. But the method that I'm trying to do hasn't been working. mainly because it won't let me use a variable name as a keycode. Is this a worthwhile problem, or should I abort mission and try something else.
string currentWord = wordArray[Random.Range(0, typingWords.Length)];
char[] wordAsArray = currentWord.ToCharArray();
Keycode currentLetter = wordAsArray[0];
if (Input.GetKey(currentLetter))
{
Debug.Log("Test");
}
most of this works fine but what doesn't work the problem is the if (Input.GetKey(currentLetter))
is there something that can turn the word into a KeycodeArray or something like that or turn the specific character into keycode.
Does anybody know if this problem is solvable or will I have to use another method.
is there something that can turn the word into a KeycodeArray or something like that or turn the specific character into keycode.
There is not a built-in function for that. You can create a keycode array with each keycodes in it and use that to determine current letter. Like:
KeyCode[] letterCodes = new KeyCode[] {
KeyCode.A, KeyCode.B, KeyCode.C, ... KeyCode.Z
};
KeyCode currentLetterCode = letterCodes[wordAsArray[0] - 'A'];
'A' is subtracted from it to get the index of the letter in the letterCodes array. This assumes that the word only contains uppercase letters.
Instead of that method i would use a different approach. I will write down a complete example of how i would do it with comments.
public class TypingGame : MonoBehaviour
{
public Text wordText;
private string currentWord;
private int currentIndex;
private void Start()
{
// Pick a random word to type
currentWord = GetRandomWord();
// Display the word on screen
wordText.text = currentWord;
}
private void Update()
{
// Check if the current letter has been typed
if (Input.anyKeyDown)
{
KeyCode keyPressed = GetKeyPressed();
if (keyPressed != KeyCode.None && keyPressed == GetNextKeyCode())
{
currentIndex++;
if (currentIndex >= currentWord.Length)
{
// The word has been completely typed
currentWord = GetRandomWord();
wordText.text = currentWord;
currentIndex = 0;
}
else
{
// Update the display to show the next letter
wordText.text = currentWord.Substring(currentIndex);
}
}
}
}
private string GetRandomWord()
{
// Replace this with your own word selection logic
string[] words = { "cat", "dog", "bird", "fish" };
return words[Random.Range(0, words.Length)];
}
private KeyCode GetNextKeyCode()
{
char nextChar = currentWord[currentIndex];
KeyCode keyCode = (KeyCode)System.Enum.Parse(typeof(KeyCode), nextChar.ToString().ToUpper());
return keyCode;
}
private KeyCode GetKeyPressed()
{
foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode)))
{
if (Input.GetKeyDown(keyCode))
{
return keyCode;
}
}
return KeyCode.None;
}
}
GetNextKeyCode method converts the next letter in the word to a KeyCode value by converting the letter to uppercase and using System.Enum.Parse to look up the corresponding KeyCode value.
GetKeyPressed method checks which key was pressed and returns the corresponding KeyCode value by looping over all possible KeyCode values and checking which one has been pressed using Input.GetKeyDown.

C# read value from user console.read [duplicate]

What I am looking for is how to read an integer that was given by the user from the command line (console project). I primarily know C++ and have started down the C# path. I know that Console.ReadLine(); only takes a char/string. So in short I am looking for the integer version of this.
Just to give you an idea of what I'm doing exactly:
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
Console.ReadLine(); // Needs to take in int rather than string or char.
I have been looking for quite a while for this. I have found a lot on C but not C#. I did find however a thread, on another site, that suggested to convert from char to int. I'm sure there has to be a more direct way than converting.
You can convert the string to integer using Convert.ToInt32() function
int intTemp = Convert.ToInt32(Console.ReadLine());
I would suggest you use TryParse:
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
string input = Console.ReadLine();
int number;
Int32.TryParse(input, out number);
This way, your application does not throw an exception, if you try to parse something like "1q" or "23e", because somebody made a faulty input.
Int32.TryParse returns a boolean value, so you can use it in an if statement, to see whether or not you need to branch of your code:
int number;
if(!Int32.TryParse(input, out number))
{
//no, not able to parse, repeat, throw exception, use fallback value?
}
To your question: You will not find a solution to read an integer because ReadLine() reads the whole command line, threfor returns a string. What you can do is, try to convert this input into and int16/32/64 variable.
There are several methods for this:
Int.Parse()
Convert.ToInt()
Int.TryParse()
If you are in doubt about the input, which is to be converted, always go for the TryParse methods, no matter if you try to parse strings, int variable or what not.
Update
In C# 7.0 out variables can be declared directly where they are passed in as an argument, so the above code could be condensed into this:
if(Int32.TryParse(input, out int number))
{
/* Yes input could be parsed and we can now use number in this code block
scope */
}
else
{
/* No, input could not be parsed to an integer */
}
A complete example would look like this:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var foo = Console.ReadLine();
if (int.TryParse(foo, out int number1)) {
Console.WriteLine($"{number1} is a number");
}
else
{
Console.WriteLine($"{foo} is not a number");
}
Console.WriteLine($"The value of the variable {nameof(number1)} is {number1}");
Console.ReadLine();
}
}
Here you can see, that the variable number1 does get initialized even if the input is not a number and has the value 0 regardless, so it is valid even outside the declaring if block
You need to typecast the input. try using the following
int input = Convert.ToInt32(Console.ReadLine());
It will throw exception if the value is non-numeric.
Edit
I understand that the above is a quick one. I would like to improve my answer:
String input = Console.ReadLine();
int selectedOption;
if(int.TryParse(input, out selectedOption))
{
switch(selectedOption)
{
case 1:
//your code here.
break;
case 2:
//another one.
break;
//. and so on, default..
}
}
else
{
//print error indicating non-numeric input is unsupported or something more meaningful.
}
int op = 0;
string in = string.Empty;
do
{
Console.WriteLine("enter choice");
in = Console.ReadLine();
} while (!int.TryParse(in, out op));
Use this simple line:
int x = int.Parse(Console.ReadLine());
I didn't see a good and complete answer to your question, so I will show a more complete example. There are some methods posted showing how to get integer input from the user, but whenever you do this you usually also need to
validate the input
display an error message if invalid input
is given, and
loop through until a valid input is given.
This example shows how to get an integer value from the user that is equal to or greater than 1. If invalid input is given, it will catch the error, display an error message, and request the user to try again for a correct input.
static void Main(string[] args)
{
int intUserInput = 0;
bool validUserInput = false;
while (validUserInput == false)
{
try
{
Console.Write("Please enter an integer value greater than or equal to 1: ");
intUserInput = int.Parse(Console.ReadLine()); //try to parse the user input to an int variable
}
catch (Exception e) //catch exception for invalid input, such as a letter
{
Console.WriteLine(e.Message);
}
if (intUserInput >= 1) { validUserInput = true; }
else { Console.WriteLine(intUserInput + " is not a valid input, please enter an integer greater than 0."); }
} //end while
Console.WriteLine("You entered " + intUserInput);
Console.WriteLine("Press any key to exit ");
Console.ReadKey();
} //end main
In your question it looks like you wanted to use this for menu options. So if you wanted to get int input for choosing a menu option you could change the if statement to
if ( (intUserInput >= 1) && (intUserInput <= 4) )
This would work if you needed the user to pick an option of 1, 2, 3, or 4.
I used int intTemp = Convert.ToInt32(Console.ReadLine()); and it worked well, here's my example:
int balance = 10000;
int retrieve = 0;
Console.Write("Hello, write the amount you want to retrieve: ");
retrieve = Convert.ToInt32(Console.ReadLine());
Better way is to use TryParse:
Int32 _userInput;
if(Int32.TryParse (Console.Readline(), out _userInput) {// do the stuff on userInput}
Try this it will not throw exception and user can try again:
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
int choice = 0;
while (!Int32.TryParse(Console.ReadLine(), out choice))
{
Console.WriteLine("Wrong input! Enter choice number again:");
}
static void Main(string[] args)
{
Console.WriteLine("Please enter a number from 1 to 10");
int counter = Convert.ToInt32(Console.ReadLine());
//Here is your variable
Console.WriteLine("The numbers start from");
do
{
counter++;
Console.Write(counter + ", ");
} while (counter < 100);
Console.ReadKey();
}
You could create your own ReadInt function, that only allows numbers
(this function is probably not the best way to go about this, but does the job)
public static int ReadInt()
{
string allowedChars = "0123456789";
ConsoleKeyInfo read = new ConsoleKeyInfo();
List<char> outInt = new List<char>();
while(!(read.Key == ConsoleKey.Enter && outInt.Count > 0))
{
read = Console.ReadKey(true);
if (allowedChars.Contains(read.KeyChar.ToString()))
{
outInt.Add(read.KeyChar);
Console.Write(read.KeyChar.ToString());
}
if(read.Key == ConsoleKey.Backspace)
{
if(outInt.Count > 0)
{
outInt.RemoveAt(outInt.Count - 1);
Console.CursorLeft--;
Console.Write(" ");
Console.CursorLeft--;
}
}
}
Console.SetCursorPosition(0, Console.CursorTop + 1);
return int.Parse(new string(outInt.ToArray()));
}
Declare a variable that will contain the value of the user input :
Ex :
int userInput = Convert.ToInt32(Console.ReadLine());
I know this question is old, but with some newer C# features like lambda expressions, here's what I actually implemented for my project today:
private static async Task Main()
{
// -- More of my code here
Console.WriteLine("1. Add account.");
Console.WriteLine("2. View accounts.");
int choice = ReadInt("Please enter your choice: ");
// -- Code that uses the choice variable
}
// I have this as a public function in a utility class,
// but you could use it directly in Program.cs
private static int ReadInt(string prompt)
{
string? text;
do
{
Console.Write(prompt);
text = Console.ReadLine();
} while (text == null || !text.Where(c => char.IsNumber(c)).Any());
return int.Parse(new string(text.Where(c => char.IsNumber(c)).ToArray()));
}
The difference here is that if you accidentally type a number and any other text along with that number, only the number is parsed.
You could just go ahead and try :
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
int choice=int.Parse(Console.ReadLine());
That should work for the case statement.
It works with the switch statement and doesn't throw an exception.

In C#, how to take null input from keyboard into nullable type boolean variable?

I want to do something like this -
using System;
class MainClass
{
public static void Main ()
{
bool? input;
Console.WriteLine ("Are you Major?");
input = bool.Parse (Console.ReadLine ());
IsMajor (input);
}
public static void IsMajor (bool? Answer)
{
if (Answer == true) {
Console.WriteLine ("You are a major");
} else if (Answer == false) {
Console.WriteLine ("You are not a major");
} else {
Console.WriteLine ("No answer given");
}
}
}
Here if user gives no answer and simply presses enter, the variable input must store the value null and output must be No answer given.
In my code, input of true and false is working fine.
But if no input is given and enter is pressed the compiler throws the exception
System.FormatExeption has been thrown
String was not recognized as a valid Boolean
So how to get null value stored in variable input so that output is No answer given
Here,
the question String was not recognized as a valid boolean C#
is obviosly not a duplicate as it does not want to take null input directly from keyboard. And if such input can't be taken, what is the utility of nullable type, as there would be work around as well?
bool input;
Console.WriteLine("Are you Major?");
if (!bool.TryParse(Console.ReadLine(), out input))
{
Console.WriteLine("No answer given");
}
else
{
//....
}
Or using C# 7:
if (!bool.TryParse(Console.ReadLine(), out bool input))
{
Console.WriteLine("No answer given");
}
else
{
// Use "input" variable
}
// You can use "input" variable here too
bool? finalResult = null;
bool input = false;
Console.WriteLine("Are you Major?");
if (bool.TryParse(Console.ReadLine(), out input))
finalResult = input;
}
Using the above technique finalResult will be null if the input cannot be parsed as either true or false.
You could surround your parse with a try-catch, and on catch (so if something other than true or false is given by the user) set input to null.

Reading an integer from user input

What I am looking for is how to read an integer that was given by the user from the command line (console project). I primarily know C++ and have started down the C# path. I know that Console.ReadLine(); only takes a char/string. So in short I am looking for the integer version of this.
Just to give you an idea of what I'm doing exactly:
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
Console.ReadLine(); // Needs to take in int rather than string or char.
I have been looking for quite a while for this. I have found a lot on C but not C#. I did find however a thread, on another site, that suggested to convert from char to int. I'm sure there has to be a more direct way than converting.
You can convert the string to integer using Convert.ToInt32() function
int intTemp = Convert.ToInt32(Console.ReadLine());
I would suggest you use TryParse:
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
string input = Console.ReadLine();
int number;
Int32.TryParse(input, out number);
This way, your application does not throw an exception, if you try to parse something like "1q" or "23e", because somebody made a faulty input.
Int32.TryParse returns a boolean value, so you can use it in an if statement, to see whether or not you need to branch of your code:
int number;
if(!Int32.TryParse(input, out number))
{
//no, not able to parse, repeat, throw exception, use fallback value?
}
To your question: You will not find a solution to read an integer because ReadLine() reads the whole command line, threfor returns a string. What you can do is, try to convert this input into and int16/32/64 variable.
There are several methods for this:
Int.Parse()
Convert.ToInt()
Int.TryParse()
If you are in doubt about the input, which is to be converted, always go for the TryParse methods, no matter if you try to parse strings, int variable or what not.
Update
In C# 7.0 out variables can be declared directly where they are passed in as an argument, so the above code could be condensed into this:
if(Int32.TryParse(input, out int number))
{
/* Yes input could be parsed and we can now use number in this code block
scope */
}
else
{
/* No, input could not be parsed to an integer */
}
A complete example would look like this:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var foo = Console.ReadLine();
if (int.TryParse(foo, out int number1)) {
Console.WriteLine($"{number1} is a number");
}
else
{
Console.WriteLine($"{foo} is not a number");
}
Console.WriteLine($"The value of the variable {nameof(number1)} is {number1}");
Console.ReadLine();
}
}
Here you can see, that the variable number1 does get initialized even if the input is not a number and has the value 0 regardless, so it is valid even outside the declaring if block
You need to typecast the input. try using the following
int input = Convert.ToInt32(Console.ReadLine());
It will throw exception if the value is non-numeric.
Edit
I understand that the above is a quick one. I would like to improve my answer:
String input = Console.ReadLine();
int selectedOption;
if(int.TryParse(input, out selectedOption))
{
switch(selectedOption)
{
case 1:
//your code here.
break;
case 2:
//another one.
break;
//. and so on, default..
}
}
else
{
//print error indicating non-numeric input is unsupported or something more meaningful.
}
int op = 0;
string in = string.Empty;
do
{
Console.WriteLine("enter choice");
in = Console.ReadLine();
} while (!int.TryParse(in, out op));
Use this simple line:
int x = int.Parse(Console.ReadLine());
I didn't see a good and complete answer to your question, so I will show a more complete example. There are some methods posted showing how to get integer input from the user, but whenever you do this you usually also need to
validate the input
display an error message if invalid input
is given, and
loop through until a valid input is given.
This example shows how to get an integer value from the user that is equal to or greater than 1. If invalid input is given, it will catch the error, display an error message, and request the user to try again for a correct input.
static void Main(string[] args)
{
int intUserInput = 0;
bool validUserInput = false;
while (validUserInput == false)
{
try
{
Console.Write("Please enter an integer value greater than or equal to 1: ");
intUserInput = int.Parse(Console.ReadLine()); //try to parse the user input to an int variable
}
catch (Exception e) //catch exception for invalid input, such as a letter
{
Console.WriteLine(e.Message);
}
if (intUserInput >= 1) { validUserInput = true; }
else { Console.WriteLine(intUserInput + " is not a valid input, please enter an integer greater than 0."); }
} //end while
Console.WriteLine("You entered " + intUserInput);
Console.WriteLine("Press any key to exit ");
Console.ReadKey();
} //end main
In your question it looks like you wanted to use this for menu options. So if you wanted to get int input for choosing a menu option you could change the if statement to
if ( (intUserInput >= 1) && (intUserInput <= 4) )
This would work if you needed the user to pick an option of 1, 2, 3, or 4.
I used int intTemp = Convert.ToInt32(Console.ReadLine()); and it worked well, here's my example:
int balance = 10000;
int retrieve = 0;
Console.Write("Hello, write the amount you want to retrieve: ");
retrieve = Convert.ToInt32(Console.ReadLine());
Better way is to use TryParse:
Int32 _userInput;
if(Int32.TryParse (Console.Readline(), out _userInput) {// do the stuff on userInput}
Try this it will not throw exception and user can try again:
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
int choice = 0;
while (!Int32.TryParse(Console.ReadLine(), out choice))
{
Console.WriteLine("Wrong input! Enter choice number again:");
}
static void Main(string[] args)
{
Console.WriteLine("Please enter a number from 1 to 10");
int counter = Convert.ToInt32(Console.ReadLine());
//Here is your variable
Console.WriteLine("The numbers start from");
do
{
counter++;
Console.Write(counter + ", ");
} while (counter < 100);
Console.ReadKey();
}
You could create your own ReadInt function, that only allows numbers
(this function is probably not the best way to go about this, but does the job)
public static int ReadInt()
{
string allowedChars = "0123456789";
ConsoleKeyInfo read = new ConsoleKeyInfo();
List<char> outInt = new List<char>();
while(!(read.Key == ConsoleKey.Enter && outInt.Count > 0))
{
read = Console.ReadKey(true);
if (allowedChars.Contains(read.KeyChar.ToString()))
{
outInt.Add(read.KeyChar);
Console.Write(read.KeyChar.ToString());
}
if(read.Key == ConsoleKey.Backspace)
{
if(outInt.Count > 0)
{
outInt.RemoveAt(outInt.Count - 1);
Console.CursorLeft--;
Console.Write(" ");
Console.CursorLeft--;
}
}
}
Console.SetCursorPosition(0, Console.CursorTop + 1);
return int.Parse(new string(outInt.ToArray()));
}
Declare a variable that will contain the value of the user input :
Ex :
int userInput = Convert.ToInt32(Console.ReadLine());
I know this question is old, but with some newer C# features like lambda expressions, here's what I actually implemented for my project today:
private static async Task Main()
{
// -- More of my code here
Console.WriteLine("1. Add account.");
Console.WriteLine("2. View accounts.");
int choice = ReadInt("Please enter your choice: ");
// -- Code that uses the choice variable
}
// I have this as a public function in a utility class,
// but you could use it directly in Program.cs
private static int ReadInt(string prompt)
{
string? text;
do
{
Console.Write(prompt);
text = Console.ReadLine();
} while (text == null || !text.Where(c => char.IsNumber(c)).Any());
return int.Parse(new string(text.Where(c => char.IsNumber(c)).ToArray()));
}
The difference here is that if you accidentally type a number and any other text along with that number, only the number is parsed.
You could just go ahead and try :
Console.WriteLine("1. Add account.");
Console.WriteLine("Enter choice: ");
int choice=int.Parse(Console.ReadLine());
That should work for the case statement.
It works with the switch statement and doesn't throw an exception.

Is there a good way to use Console.ReadKey for choosing between values without doing a lot of conversion between types?

Im using Console.ReadKey() to choose from a number of options that varies from time to time.
Before this initial code snippet there is a for loop that counts occurances into counter variable of type int.
The point is to use Console.ReadKey() to get an int.
int choice = ReadKey();
Console.WriteLine("");
if (choice < counter)
{
mail.to = result[counter-1].email;
}
By using the following methods
static int ReadKey()
{
ConsoleKeyInfo choice = Console.ReadKey();
char convertedchoice = choice.KeyChar;
string convertedchoice2 = convertedchoice.ToString();
int result = TryInt(convertedchoice2);
return result;
}
static int TryInt(string totry)
{
while (true)
{
int result;
if (int.TryParse(totry, out result))
{
return result;
}
Console.WriteLine("Sorry, you need to enter a number. Try again.");
}
}
I tried using ToString() but this was the way that it would let me do it in the end.
So this looks kind of inneffective to me and hence I would really appreciate some guidance as what to do differently?
Edit:
I ended up with a combination of all the good answers below. Thanks alot guys.
static int ReadKey()
{
while (true)
{
ConsoleKeyInfo choice = Console.ReadKey();
if (char.IsDigit(choice.KeyChar))
{
int answer = Convert.ToInt32(choice.KeyChar);
return answer - 48; //-48 because 0 is represented in unicode by 48 and 1 by 49 etc etc
}
Console.WriteLine("\nSorry, you need to input a number");
}
}
For a menu system with choices 0..9 this is reasonably OK. Not for reading larger numbers though.
Your whole checking logic can be made a lot easier with char.IsDigit() :
if char.IsDigit(convertedchoice)
{
int result = convertedchoice - '0'; // char1 - char2 = int, in this case in 0..9
return result;
}
else ...
You can just call Convert.ToInt32(choice.KeyChar); directly.
That would simplify it a bit.
There are lots of ways to simplify your code, but for a start try to avoid putting everything into a variable. In general, things like:
(a + b + c) / 2
are much easier to read than things like:
int A_plus_B = a + b
int A_plus_B_plus_C = A_plus_B + c
int answer = A_plus_B_plus_C / 2
With this in mind, you could write:
static int ReadKey()
{
while (true)
{
char ch = Console.ReadKey().KeyChar;
int result;
if (int.TryParse(ch.toString(), out result))
{
return result;
}
}
}

Categories