Using Readline() and ReadKey() Simultaneously - c#

Is there any way to detect both Readline and ReadKey, so that in most cases it behaves as a readline, except for some special key inputs that should be detected?
I need some "parallel" implementation to introduce simultaneity.
The code below is synchronous and does not meet my need
while ((line = Console.ReadLine()) != "x")
{
if (line == "BLABLA")
{
//Stuff
}
else
{
//Stuff
}
ConsoleKeyInfo ki = Console.ReadKey(true);
if ((ki.Key == ConsoleKey.V) && (ki.Modifiers == ConsoleModifiers.Control))
{
//Stuff
}
}

Here's a function I just made to do this.
Right now it only handles Backspace, Enter and Esc, but it could easily be modified to handle other keys if you deem them necessary.
// returns null if user pressed Escape, or the contents of the line if they pressed Enter.
private static string ReadLineOrEsc()
{
string retString = "";
int curIndex = 0;
do
{
ConsoleKeyInfo readKeyResult = Console.ReadKey(true);
// handle Esc
if (readKeyResult.Key == ConsoleKey.Escape)
{
Console.WriteLine();
return null;
}
// handle Enter
if (readKeyResult.Key == ConsoleKey.Enter)
{
Console.WriteLine();
return retString;
}
// handle backspace
if (readKeyResult.Key == ConsoleKey.Backspace)
{
if (curIndex > 0)
{
retString = retString.Remove(retString.Length - 1);
Console.Write(readKeyResult.KeyChar);
Console.Write(' ');
Console.Write(readKeyResult.KeyChar);
curIndex--;
}
}
else
// handle all other keypresses
{
retString += readKeyResult.KeyChar;
Console.Write(readKeyResult.KeyChar);
curIndex++;
}
}
while (true);
}

No, not as such. Both methods block until the user enters something on the console. So even if you would find a way to have both run in parallel, it will not be deterministic which one gets the first shot.
There is a (not obvious) similar problem: how to make Console.ReadLine() abort/break after a certain amount of time without user input.
There have been multiple attempts for this problem here:
Console.ReadLine Break
How to add a Timeout to Console.ReadLine()?
Most are modelled around either creating your own version of a ReadLine function that adds a timeout (or in your case special handling for certain character (codes)) or the use some sort of threading.
Both ways are either non-trivial or have their own issues (make sure you review the comments, even for the accepted answers).
In short, I think you will need to roll your own version of ReadLine, based on Console.ReadKey with your special handling included and that much of the genuine Console.ReadLine behavior that you require. Note that this even include such basic things as RETURN, ARROW KEYS, BACKSPACE handling, etc.
Update: There is the getline.cs Code from the Mono project, which implements a line editing capability like it was provided by some venerable UNIX shells (EMACS mode, if you care). For that, I believe it will need to implement some sort of ReadLine replacement, although I haven't checked. Maybe you can use this as a starting point.

Here is a method that I created that works great. You do not have to press button twice in order for string to start appearing. Basically this replaces Console.ReadLine() but it also looks for Esc key pressed. Just look at return type of method if it is null then you know Esc was pressed.
private string ReadLineOrEscape()
{
ConsoleKeyInfo keyInfo = new ConsoleKeyInfo();
StringBuilder sb = new StringBuilder();
int index = 0;
while (keyInfo.Key != ConsoleKey.Enter)
{
keyInfo = Console.ReadKey(true);
if (keyInfo.Key == ConsoleKey.Escape)
{
return null;
}
if(keyInfo.Key == ConsoleKey.Backspace)
{
if (index > 0)
{
Console.CursorLeft = index - 1;
sb.Remove(index - 1, 1);
Console.Write(" \b");
index--;
}
}
if(keyInfo.KeyChar > 31 && keyInfo.KeyChar < 127)
{
index++;
Console.Write(keyInfo.KeyChar);
sb.Append(keyInfo.KeyChar);
}
}
return sb.ToString(); ;
}

In response to #Overlord Zurd, I improved the code provided by the user.
public class ConsoleOutput
{
private ConsoleOutputType OutputType { get; set; }
private object MyObject { get; }
private static bool IsInserting { get; set; }
public string KeyName => IsKey() && (ConsoleKeyInfo)MyObject != null ? ((ConsoleKeyInfo)MyObject).Key.ToString() : "Null";
public string OutputString => !IsKey() && MyObject != null ? (string)MyObject : string.Empty;
public static event Action<string> ReadInput = delegate { };
public static event Action<ConsoleKeyInfo> ReadKey = delegate { };
private ConsoleOutput()
{
}
public ConsoleOutput(object obj)
{
MyObject = obj;
OutputType = obj is ConsoleKeyInfo ? ConsoleOutputType.Key : ConsoleOutputType.Value;
}
public bool IsKey()
{
return OutputType == ConsoleOutputType.Key;
}
public bool IsExitKey()
{
if (!IsKey())
return false;
var info = ((ConsoleKeyInfo)MyObject);
return (info.Modifiers & ConsoleModifiers.Control) != 0 && info.Key == ConsoleKey.B;
}
public string GetValue()
{
return (string)MyObject;
}
// returns null if user pressed Escape, or the contents of the line if they pressed Enter.
public static ConsoleOutput ReadLineOrKey()
{
string retString = "";
int curIndex = 0;
do
{
ConsoleKeyInfo readKeyResult = Console.ReadKey(true);
// handle Enter
if (readKeyResult.Key == ConsoleKey.Enter)
{
ReadInput?.Invoke(retString);
Console.WriteLine();
return new ConsoleOutput(retString);
}
// handle backspace
if (readKeyResult.Key == ConsoleKey.Backspace)
{
if (curIndex > 0)
{
retString = retString.Remove(retString.Length - 1);
Console.Write(readKeyResult.KeyChar);
Console.Write(' ');
Console.Write(readKeyResult.KeyChar);
--curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.Delete)
{
if (retString.Length - curIndex > 0)
{
// Store current position
int curLeftPos = Console.CursorLeft;
// Redraw string
for (int i = curIndex + 1; i < retString.Length; ++i)
Console.Write(retString[i]);
// Remove last repeated char
Console.Write(' ');
// Restore position
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Remove string
retString = retString.Remove(curIndex, 1);
}
}
else if (readKeyResult.Key == ConsoleKey.RightArrow)
{
if (curIndex < retString.Length)
{
++Console.CursorLeft;
++curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.LeftArrow)
{
if (curIndex > 0)
{
--Console.CursorLeft;
--curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.Insert)
{
IsInserting = !IsInserting;
}
#if DEBUG
else if (readKeyResult.Key == ConsoleKey.UpArrow)
{
if (Console.CursorTop > 0)
--Console.CursorTop;
}
else if (readKeyResult.Key == ConsoleKey.DownArrow)
{
if (Console.CursorTop < Console.BufferHeight - 1)
++Console.CursorTop;
}
#endif
else
// handle all other keypresses
{
if (IsInserting || curIndex == retString.Length)
{
retString += readKeyResult.KeyChar;
Console.Write(readKeyResult.KeyChar);
++curIndex;
}
else
{
// Store char
char c = readKeyResult.KeyChar;
// Write char at position
Console.Write(c);
// Store cursor position
int curLeftPos = Console.CursorLeft;
// Clear console from curIndex to end
for (int i = curIndex; i < retString.Length; ++i)
Console.Write(' ');
// Go back
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Write the chars from curIndex to end (with the new appended char)
for (int i = curIndex; i < retString.Length; ++i)
Console.Write(retString[i]);
// Restore again
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Store in the string
retString = retString.Insert(curIndex, new string(c, 1));
// Sum one to the cur index (we appended one char)
++curIndex;
}
}
if (char.IsControl(readKeyResult.KeyChar) &&
readKeyResult.Key != ConsoleKey.Enter &&
readKeyResult.Key != ConsoleKey.Backspace &&
readKeyResult.Key != ConsoleKey.Tab &&
readKeyResult.Key != ConsoleKey.Delete &&
readKeyResult.Key != ConsoleKey.RightArrow &&
readKeyResult.Key != ConsoleKey.LeftArrow &&
readKeyResult.Key != ConsoleKey.Insert)
{
#if DEBUG
if (readKeyResult.Key == ConsoleKey.UpArrow || readKeyResult.Key == ConsoleKey.DownArrow)
continue;
#endif
ReadKey?.Invoke(readKeyResult);
Console.WriteLine();
return new ConsoleOutput(readKeyResult);
}
}
while (true);
}
}
As you can see, I implemented Insert, Arrow control, Delete, etc etc... (Insert was an important thing because if you write any text with this code you will see behavior as Insert key provides).
And example of use:
internal class Program
{
private static void Main(string[] args)
{
Console.Write("Write test string: ");
var test = ConsoleOutput.ReadLineOrKey();
if (test.IsKey())
Console.WriteLine(test.KeyName);
else
Console.WriteLine($"Output string: {test.OutputString}");
Console.Read();
}
}
You can keep updated on this link (is a link to my lib I'm currently working at).

Related

Problem with palindrome checker for numbers

I'm having problem with my code and cannot find my error. Why only the first try is working and on every other tries it prints me false?
Even when I enter 323, which is true, for example, and prints "true" after that everything is false even empty scapes.
class Program
{
public static void Main()
{
string inputedString = Console.ReadLine();
string reversedString = string.Empty;
while (true)
{
if (inputedString == "END")
{
break;
}
for (int i = inputedString.Length - 1; i >= 0; i--)
{
reversedString += inputedString[i];
}
if (reversedString == inputedString)
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
}
}
You read the first string outside the loop and never reread the string inside the loop. You also don't clear the reversedString so each subsequent time in the loop its wrong.
public static void Main()
{
string inputedString;
string reversedString;
while (true)
{
inputedString = Console.ReadLine();
reversedString = string.Empty;
if (inputedString == "END")
{
break;
}
for (int i = inputedString.Length - 1; i >= 0; i--)
{
reversedString += inputedString[i];
}
if (reversedString == inputedString)
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
}
}
The part of the code below should be inside the 'while" loop
string inputedString = Console.ReadLine();
string reversedString = string.Empty;

How to set a password on c# console? [duplicate]

I tried the following code...
string pass = "";
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
// Backspace Should Not Work
if (key.Key != ConsoleKey.Backspace)
{
pass += key.KeyChar;
Console.Write("*");
}
else
{
Console.Write("\b");
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
But this way the backspace functionality doesn't work while typing the password.
Any suggestion?
Console.Write("\b \b"); will delete the asterisk character from the screen, but you do not have any code within your else block that removes the previously entered character from your pass string variable.
Here's the relevant working code that should do what you require:
var pass = string.Empty;
ConsoleKey key;
do
{
var keyInfo = Console.ReadKey(intercept: true);
key = keyInfo.Key;
if (key == ConsoleKey.Backspace && pass.Length > 0)
{
Console.Write("\b \b");
pass = pass[0..^1];
}
else if (!char.IsControl(keyInfo.KeyChar))
{
Console.Write("*");
pass += keyInfo.KeyChar;
}
} while (key != ConsoleKey.Enter);
For this you should use the System.Security.SecureString
public SecureString GetPassword()
{
var pwd = new SecureString();
while (true)
{
ConsoleKeyInfo i = Console.ReadKey(true);
if (i.Key == ConsoleKey.Enter)
{
break;
}
else if (i.Key == ConsoleKey.Backspace)
{
if (pwd.Length > 0)
{
pwd.RemoveAt(pwd.Length - 1);
Console.Write("\b \b");
}
}
else if (i.KeyChar != '\u0000' ) // KeyChar == '\u0000' if the key pressed does not correspond to a printable character, e.g. F1, Pause-Break, etc
{
pwd.AppendChar(i.KeyChar);
Console.Write("*");
}
}
return pwd;
}
Complete solution, vanilla C# .net 3.5+
Cut & Paste
:)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleReadPasswords
{
class Program
{
static void Main(string[] args)
{
Console.Write("Password:");
string password = Orb.App.Console.ReadPassword();
Console.WriteLine("Sorry - I just can't keep a secret!");
Console.WriteLine("Your password was:\n<Password>{0}</Password>", password);
Console.ReadLine();
}
}
}
namespace Orb.App
{
/// <summary>
/// Adds some nice help to the console. Static extension methods don't exist (probably for a good reason) so the next best thing is congruent naming.
/// </summary>
static public class Console
{
/// <summary>
/// Like System.Console.ReadLine(), only with a mask.
/// </summary>
/// <param name="mask">a <c>char</c> representing your choice of console mask</param>
/// <returns>the string the user typed in </returns>
public static string ReadPassword(char mask)
{
const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127;
int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const
var pass = new Stack<char>();
char chr = (char)0;
while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER)
{
if (chr == BACKSP)
{
if (pass.Count > 0)
{
System.Console.Write("\b \b");
pass.Pop();
}
}
else if (chr == CTRLBACKSP)
{
while (pass.Count > 0)
{
System.Console.Write("\b \b");
pass.Pop();
}
}
else if (FILTERED.Count(x => chr == x) > 0) { }
else
{
pass.Push((char)chr);
System.Console.Write(mask);
}
}
System.Console.WriteLine();
return new string(pass.Reverse().ToArray());
}
/// <summary>
/// Like System.Console.ReadLine(), only with a mask.
/// </summary>
/// <returns>the string the user typed in </returns>
public static string ReadPassword()
{
return Orb.App.Console.ReadPassword('*');
}
}
}
Taking the top answer, as well as the suggestions from its comments, and modifying it to use SecureString instead of String, test for all control keys, and not error or write an extra "*" to the screen when the password length is 0, my solution is:
public static SecureString getPasswordFromConsole(String displayMessage) {
SecureString pass = new SecureString();
Console.Write(displayMessage);
ConsoleKeyInfo key;
do {
key = Console.ReadKey(true);
// Backspace Should Not Work
if (!char.IsControl(key.KeyChar)) {
pass.AppendChar(key.KeyChar);
Console.Write("*");
} else {
if (key.Key == ConsoleKey.Backspace && pass.Length > 0) {
pass.RemoveAt(pass.Length - 1);
Console.Write("\b \b");
}
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
return pass;
}
Mine ignores control characters and handles line wrapping:
public static string ReadLineMasked(char mask = '*')
{
var sb = new StringBuilder();
ConsoleKeyInfo keyInfo;
while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Enter)
{
if (!char.IsControl(keyInfo.KeyChar))
{
sb.Append(keyInfo.KeyChar);
Console.Write(mask);
}
else if (keyInfo.Key == ConsoleKey.Backspace && sb.Length > 0)
{
sb.Remove(sb.Length - 1, 1);
if (Console.CursorLeft == 0)
{
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
Console.Write(' ');
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
}
else Console.Write("\b \b");
}
}
Console.WriteLine();
return sb.ToString();
}
This masks the password with a red square, then reverts back to the original colours once the password has been entered.
It doesn't stop the user from using copy/paste to get the password, but if it's more just about stopping someone looking over your shoulder, this is a good quick solution.
Console.Write("Password ");
ConsoleColor origBG = Console.BackgroundColor; // Store original values
ConsoleColor origFG = Console.ForegroundColor;
Console.BackgroundColor = ConsoleColor.Red; // Set the block colour (could be anything)
Console.ForegroundColor = ConsoleColor.Red;
string Password = Console.ReadLine(); // read the password
Console.BackgroundColor= origBG; // revert back to original
Console.ForegroundColor= origFG;
Reading console input is hard, you need to handle special keys like Ctrl, Alt, also cursor keys and Backspace/Delete. On some keyboard layouts, like Swedish Ctrl is even needed to enter keys that exist directly on US keyboard. I believe that trying to handle this using the "low-level" Console.ReadKey(true) is just very hard, so the easiest and most robust way is to just to disable "console input echo" during entering password using a bit of WINAPI.
The sample below is based on answer to Read a password from std::cin question.
private enum StdHandle
{
Input = -10,
Output = -11,
Error = -12,
}
private enum ConsoleMode
{
ENABLE_ECHO_INPUT = 4
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(StdHandle nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, int dwMode);
public static string ReadPassword()
{
IntPtr stdInputHandle = GetStdHandle(StdHandle.Input);
if (stdInputHandle == IntPtr.Zero)
{
throw new InvalidOperationException("No console input");
}
int previousConsoleMode;
if (!GetConsoleMode(stdInputHandle , out previousConsoleMode))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not get console mode.");
}
// disable console input echo
if (!SetConsoleMode(stdInputHandle , previousConsoleMode & ~(int)ConsoleMode.ENABLE_ECHO_INPUT))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not disable console input echo.");
}
// just read the password using standard Console.ReadLine()
string password = Console.ReadLine();
// reset console mode to previous
if (!SetConsoleMode(stdInputHandle , previousConsoleMode))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not reset console mode.");
}
return password;
}
I found a bug in shermy's vanilla C# 3.5 .NET solution which otherwise works a charm. I have also incorporated Damian LeszczyƄski - Vash's SecureString idea here but you can use an ordinary string if you prefer.
THE BUG: If you press backspace during the password prompt and the current length of the password is 0 then an asterisk is incorrectly inserted in the password mask. To fix this bug modify the following method.
public static string ReadPassword(char mask)
{
const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127;
int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const
SecureString securePass = new SecureString();
char chr = (char)0;
while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER)
{
if (((chr == BACKSP) || (chr == CTRLBACKSP))
&& (securePass.Length > 0))
{
System.Console.Write("\b \b");
securePass.RemoveAt(securePass.Length - 1);
}
// Don't append * when length is 0 and backspace is selected
else if (((chr == BACKSP) || (chr == CTRLBACKSP)) && (securePass.Length == 0))
{
}
// Don't append when a filtered char is detected
else if (FILTERED.Count(x => chr == x) > 0)
{
}
// Append and write * mask
else
{
securePass.AppendChar(chr);
System.Console.Write(mask);
}
}
System.Console.WriteLine();
IntPtr ptr = new IntPtr();
ptr = Marshal.SecureStringToBSTR(securePass);
string plainPass = Marshal.PtrToStringBSTR(ptr);
Marshal.ZeroFreeBSTR(ptr);
return plainPass;
}
(My) nuget package to do this, based on the top answer:
install-package PanoramicData.ConsoleExtensions
Usage:
using PanoramicData.ConsoleExtensions;
...
Console.Write("Password: ");
var password = ConsolePlus.ReadPassword();
Console.WriteLine();
Project URL: https://github.com/panoramicdata/PanoramicData.ConsoleExtensions
Pull requests welcome.
Here's a version that adds support for the Escape key (which returns a null string)
public static string ReadPassword()
{
string password = "";
while (true)
{
ConsoleKeyInfo key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.Escape:
return null;
case ConsoleKey.Enter:
return password;
case ConsoleKey.Backspace:
if (password.Length > 0)
{
password = password.Substring(0, (password.Length - 1));
Console.Write("\b \b");
}
break;
default:
password += key.KeyChar;
Console.Write("*");
break;
}
}
}
Jeez guys
static string ReadPasswordLine()
{
string pass = "";
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Enter)
{
if (!(key.KeyChar < ' '))
{
pass += key.KeyChar;
Console.Write("*");
}
else if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
{
Console.Write(Convert.ToChar(ConsoleKey.Backspace));
pass = pass.Remove(pass.Length - 1);
Console.Write(" ");
Console.Write(Convert.ToChar(ConsoleKey.Backspace));
}
}
} while (key.Key != ConsoleKey.Enter);
return pass;
}
You could append your keys to an accumulating linked list.
When a backspace key is received, remove the last key from the list.
When you receive the enter key, collapse your list into a string and do the rest of your work.
I made some changes for backspace
string pass = "";
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
// Backspace Should Not Work
if (key.Key != ConsoleKey.Backspace)
{
pass += key.KeyChar;
Console.Write("*");
}
else
{
pass = pass.Remove(pass.Length - 1);
Console.Write("\b \b");
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
I have updated Ronnie's version after spending way too much time trying to enter a password only to find out that I had my CAPS LOCK on!
With this version what ever the message is in _CapsLockMessage will "float" at the end of the typing area and will be displayed in red.
This version takes a bit more code and does require a polling loop. On my computer CPU usage about 3% to 4%, but one could always add a small Sleep() value to decrease CPU usage if needed.
private const string _CapsLockMessage = " CAPS LOCK";
/// <summary>
/// Like System.Console.ReadLine(), only with a mask.
/// </summary>
/// <param name="mask">a <c>char</c> representing your choice of console mask</param>
/// <returns>the string the user typed in</returns>
public static string ReadLineMasked(char mask = '*')
{
// Taken from http://stackoverflow.com/a/19770778/486660
var consoleLine = new StringBuilder();
ConsoleKeyInfo keyInfo;
bool isDone;
bool isAlreadyLocked;
bool isCapsLockOn;
int cursorLeft;
int cursorTop;
ConsoleColor originalForegroundColor;
isDone = false;
isAlreadyLocked = Console.CapsLock;
while (isDone == false)
{
isCapsLockOn = Console.CapsLock;
if (isCapsLockOn != isAlreadyLocked)
{
if (isCapsLockOn)
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
originalForegroundColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0}", _CapsLockMessage);
Console.SetCursorPosition(cursorLeft, cursorTop);
Console.ForegroundColor = originalForegroundColor;
}
else
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
Console.Write("{0}", string.Empty.PadRight(_CapsLockMessage.Length));
Console.SetCursorPosition(cursorLeft, cursorTop);
}
isAlreadyLocked = isCapsLockOn;
}
if (Console.KeyAvailable)
{
keyInfo = Console.ReadKey(intercept: true);
if (keyInfo.Key == ConsoleKey.Enter)
{
isDone = true;
continue;
}
if (!char.IsControl(keyInfo.KeyChar))
{
consoleLine.Append(keyInfo.KeyChar);
Console.Write(mask);
}
else if (keyInfo.Key == ConsoleKey.Backspace && consoleLine.Length > 0)
{
consoleLine.Remove(consoleLine.Length - 1, 1);
if (Console.CursorLeft == 0)
{
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
Console.Write(' ');
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
}
else
{
Console.Write("\b \b");
}
}
if (isCapsLockOn)
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
originalForegroundColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0}", _CapsLockMessage);
Console.CursorLeft = cursorLeft;
Console.CursorTop = cursorTop;
Console.ForegroundColor = originalForegroundColor;
}
}
}
Console.WriteLine();
return consoleLine.ToString();
}
Here is my simple version.
Every time you hit a key, delete all from console and draw as many '*' as the length of password string is.
int chr = 0;
string pass = "";
const int ENTER = 13;
const int BS = 8;
do
{
chr = Console.ReadKey().KeyChar;
Console.Clear(); //imediately clear the char you printed
//if the char is not 'return' or 'backspace' add it to pass string
if (chr != ENTER && chr != BS) pass += (char)chr;
//if you hit backspace remove last char from pass string
if (chr == BS) pass = pass.Remove(pass.Length-1, 1);
for (int i = 0; i < pass.Length; i++)
{
Console.Write('*');
}
}
while (chr != ENTER);
Console.Write("\n");
Console.Write(pass);
Console.Read(); //just to see the pass
If I understand this correctly, you're trying to make backspace delete both the visible * character on screen and the cached character in your pass variable?
If so, then just change your else block to this:
else
{
Console.Write("\b");
pass = pass.Remove(pass.Length -1);
}
I just improve code from ask I simple and just work
string pass = ""; //create empty password string
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
int passLen = 0; // base password length
do
{
key = Console.ReadKey(true); //reading keyboard key
if (key.Key == ConsoleKey.Escape) Environment.Exit(0); // If key is escape console will close (optional)
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter) // on key with is not bacspase and enter
{
pass += key.KeyChar; //password string add key value
Console.Write("*"); // and print star as masked char
passLen++; // upgrading password length
}
else if (passLen > 0 && key.Key == ConsoleKey.Backspace) //if password have a any symbol and u press Backspace
{
Console.Write("\b \b"); //Backspace delete star symbol and coursor back in line
passLen--; // password length is one less
pass = pass[0..^1]; // new string passowrd is string without last charter
}
}
while (key.Key != ConsoleKey.Enter); // if you press enter this stop execute with your password
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
string pass = "";
Console.WriteLine("Enter your password: ");
ConsoleKeyInfo key;
do {
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace) {
pass += key.KeyChar;
Console.Write("*");
} else {
Console.Write("\b \b");
char[] pas = pass.ToCharArray();
string temp = "";
for (int i = 0; i < pass.Length - 1; i++) {
temp += pas[i];
}
pass = temp;
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);

C# Terminating the program with a key combo without pressing enter while in read

Now here is the situation we are in:
getinput:
//things happen here
string typed = Console.ReadLine();
try
if (typed == "com")
{
//things happen here
}
else if (Console.ReadKey(true).Key == (ConsoleKey.F1) + (ConsoleModifiers.Alt))
{
System.Environment.Exit(0);
}
//other else if's happen here
else
{
Console.WriteLine("\n" + typed + " isn't an option.");
goto getInput;
}
}
catch (Exception)
{
}
goto getInput;
what i want to do with this is when i press alt+f1 the program will terminate itself however because the program waits for some input from me to write even with the working version (without the alt part) it wants me to type the things then press enter, which i dont want. how does one handlde this??
static void Main(string[] args)
{
Console.TreatControlCAsInput = true;
var typed = ReadLine();
if (typed == "com")
{
Console.WriteLine("com");
//things happen here
}
//other else if's happen here
else
{
Console.WriteLine("\n" + typed + " isn't an option.");
}
}
public static string ReadLine() {
StringBuilder sb = new StringBuilder();
do
{
ConsoleKeyInfo key = Console.ReadKey();
if ((key.Modifiers & ConsoleModifiers.Alt) != 0)
{
if (key.Key == ConsoleKey.K)
{
Console.WriteLine("killing console");
System.Environment.Exit(0);
}
}
else
{
sb.Append(key.KeyChar);
if (key.KeyChar == '\n'||key.Key==ConsoleKey.Enter)
{
return sb.ToString();
}
}
} while (true);
}
that code will help you with your problem,
just be aware that when you reading a line by char's you will need to handle things like backspace
First of all, please consider using loops instead of goto, as gotos are dangerous. Why? Have a look here: 'Goto' is this bad?
To solve your problem you can use the ConsoleKeyInfo class in combination with the Console.ReadKey() method to get information about single key presses. With this you can check for any key-combination right before adding up the single characters to a string. A working example could look like:
namespace Stackoverflow
{
using System;
class Program
{
public static void Main(string[] args)
{
ConsoleKeyInfo keyInfo = default(ConsoleKeyInfo);
string input = string.Empty;
// loop while condition is true
while (true)
{
// read input character-wise as long as user presses 'Enter' or 'Alt+F1'
while (true)
{
// read a single character and print it to console
keyInfo = Console.ReadKey(false);
// check for close-combination
if (keyInfo.Key == ConsoleKey.F1 && (keyInfo.Modifiers & ConsoleModifiers.Alt) != 0)
{
// program terminates
Environment.Exit(0);
}
// check for enter-press
if (keyInfo.Key == ConsoleKey.Enter)
{
// break out of the loop without adding '\r' to the input string
break;
}
// add up input-string
input += keyInfo.KeyChar;
}
// optional: enter was pressed - add a new line
Console.WriteLine();
// user pressed enter, do something with the input
try
{
if (input == "com")
{
// right option - do something
}
else
{
// wrong option - reset ConsoleKeyInfo + input
Console.WriteLine("\n" + input + " isn't an option.");
keyInfo = default(ConsoleKeyInfo);
input = string.Empty;
continue;
}
}
catch (Exception)
{
// handle exceptions
}
}
}
}
}

C# Controlling balanced parenthesis to recursive

I am trying to write a function to control if the parenthesis included in a stringare balanced or not.
I have written the following function:
public bool IsBalanced(string input)
{
//last condition, gets out
if (string.IsNullOrEmpty(input)) return true;
int numOpen = 0;
bool opened = false;
foreach (char c in input)
{
if (char.ToUpperInvariant(c) =='(')
{
opened = true;
numOpen+=1;
}
if (char.ToUpperInvariant(c) == ')')
{
if (opened)
{
numOpen-=1;
opened = numOpen > 0;
}
else
return false; //incorrect position parentheses, closes but not opened
}
}
return numOpen == 0;
}
I wanted to change it to a recursive function, but have not been able to do so. Could anyone give a hint how to do it?
Well, your algorithm is not pretty. Here is a better one
int check = 0;
foreach (var c in input)
{
if (c == '(') {
check++;
} else if (c == ')') {
check--;
}
if (check < 0) {
return false; // error, closing bracket without opening brackets first
}
}
return check == 0; // > 0 error, missing some closing brackets
To make it (algorithm of checking for balanced brackets) recursive you can go with following
bool IsBalanced(string input)
{
var first = input.IndexOf('('); // first opening bracket position
var last = input.LastIndexOf(')'); // last closing bracket position
if (first == -1 && last == -1)
return true; // no more brackets - balanced
if (first == -1 && last != -1 || first != -1 && last == -1)
return false; // error - one of brackets is missing
if (first > last)
return false; // error - closing bracket is BEFORE opening
return IsBalanced(input.Substring(first, last - first)); // not sure, might need to tweak it, parameter should be without first and last bracket (what is inside)
}
This simply remove first opening brackets and last closing bracket and pass what is left as parameter (recursively) until one of the end conditions is met.
The basic idea is to take a variant (numOpen in this case) as an argument.
Here is my code:
public bool IsBalancedRec(string input, int numOpen = 0)
{
if (numOpen < 0)
return false;
if (string.IsNullOrEmpty(input))
return numOpen == 0;
char c = input[0];
string rest = input.Substring(1);
if (c == '(')
return IsBalancedRec(rest, numOpen + 1);
else if (c == ')')
return IsBalancedRec(rest, numOpen - 1);
else
return IsBalancedRec(rest, numOpen);
}
And call this like IsBalancedRec("so(m(eth)ing)").
Implement with stack:
Stack myStak = new Stack();
public bool IsBalanced(string input)
{
if (input.ToArray().Count() != 0)
{
if(input.ToArray()[0] == '(')
{
myStak.Push('(');
}
else if(input.ToArray()[0] == ')')
{
if (myStak.Count != 0)
myStak.Pop();
else
{
//not balanced
return false;
}
}
return IsBalanced(input.Substring(1));
}
else
{
if (myStak.Count == 0)
{
//balanced
return true;
}
else
{
//not balanced
return false;
}
}
}
public static bool IsBalanced(string input)
{
int numOpen = 0;
while(input != "")
{
char c = input[0];
input = input.Substring(1);
numOpen = c == '(' ? (numOpen + 1) : (c == ')' ? (numOpen - 1) : numOpen);
}
return numOpen == 0;
}
// count no of left and right bracket or parenthesis and check counts
var InputStr= str.ToCharArray();
int left = 0;
int right = 0;
foreach (char item in InputStr)
{
if(item == '(')
{
left ++;
} else if(item == ')')
{
right ++;
}
}
if(right == l)
{
return "1";
}
return "0";
}

How can I validate console input as integers?

I have written my codes and i want to validate it in such a way thet it will only allow intergers to be inputed and not alphabets. Here is the code, please I will love you to help me. Thanks.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace minimum
{
class Program
{
static void Main(string[] args)
{
int a = Convert.ToInt32(Console.ReadLine());
int b = Convert.ToInt32(Console.ReadLine());
int c = Convert.ToInt32(Console.ReadLine());
if (a < b)
{
if (a < c)
{
Console.WriteLine(a + "is the minimum number");
}
}
if (b < a)
{
if (b < c)
{
Console.WriteLine(b + "is the minimum number");
}
}
if (c < a)
{
if (c < b)
{
Console.WriteLine(c + "is the minimum number");
}
}
Console.ReadLine();
}
}
}
You should test if it's an int instead of converting in right away.
Try something like :
string line = Console.ReadLine();
int value;
if (int.TryParse(line, out value))
{
// this is an int
// do you minimum number check here
}
else
{
// this is not an int
}
Simply call Readline() and loop with Int.TryParse until the user inputs a valid number :)
int X;
String Result = Console.ReadLine();
while(!Int32.TryParse(Result, out X))
{
Console.WriteLine("Not a valid number, try again.");
Result = Console.ReadLine();
}
Hope that helps
To get the console to filter out alphabetical keystrokes you have to take over input parsing. The Console.ReadKey() method is fundamental to this, it lets you sniff the pressed key. Here's a sample implementation:
static string ReadNumber() {
var buf = new StringBuilder();
for (; ; ) {
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter && buf.Length > 0) {
return buf.ToString() ;
}
else if (key.Key == ConsoleKey.Backspace && buf.Length > 0) {
buf.Remove(buf.Length-1, 1);
Console.Write("\b \b");
}
else if ("0123456789.-".Contains(key.KeyChar)) {
buf.Append(key.KeyChar);
Console.Write(key.KeyChar);
}
else {
Console.Beep();
}
}
}
You could add, say, Decimal.TryParse() in the if() statement that detects the Enter key to verify that the entered string is still a valid number. That way you can reject input like "1-2".
Do not Convert the input from the user immediately. Put it in a string and use Int32.TryParse(...) to find out whether or not a number was entered. Like this:
int i;
string input = Console.ReadLine();
if(Int32.TryParse(input, out i))
{
// it is a number and it is stored in i
}
else
{
// it is not a number
}
Note that
if (a < b) {
if (a < c) {
is equivalent to
if (a < b && a < c) {
and that this latter form introduces less nesting and is more readable, particularly if your code grows more complex. Also, you should probably never use Convert.ToInt32 - it has a particularly ill-conceived and surprising corner case; and it's also less type-safe than int.Parse which is the superior choice where possible - or int.TryParse when you're unsure whether the string is valid. Basically, avoid Convert.... wherever possible.
string Temp;
int tempInt,a;
bool result=false;
while ( result == false )
{
Console.Write ("\n Enter A Number : ");
Temp = Console.ReadLine ();
result = int.TryParse (Temp, out tempInt);
if ( result == false )
{
Console.Write ("\n Please Enter Numbers Only.");
}
else
{
a=tempInt;
break;
}
}
My preferred solution would be:
static void Main()
{
Console.WriteLine(
(
from line in Generate(()=>Console.ReadLine()).Take(3)
let val = ParseAsInt(line)
where val.HasValue
select val.Value
).Min()
);
}
static IEnumerable<T> Generate<T>(Func<T> generator) {
while(true) yield return generator();
}
static int? ParseAsInt(string str) {
int retval;
return int.TryParse(str,out retval) ? retval : default(int?);
}
Of course, depending on the specification (should invalid number be retried?), it may need to be tweaked.
Double/Float:
I'm just extending #Hans Passant answer (Taking care of DecimalSeparator and "-"):
static double ReadNumber()
{
var buf = new StringBuilder();
for (; ; )
{
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter && buf.Length > 0)
{
Console.WriteLine();
return Convert.ToDouble(buf.ToString());
}
else if (key.Key == ConsoleKey.Backspace && buf.Length > 0)
{
buf.Remove(buf.Length - 1, 1);
Console.Write("\b \b");
}
else if (System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator.Contains(key.KeyChar) && buf.ToString().IndexOf(System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) == -1)
{
buf.Append(key.KeyChar);
Console.Write(key.KeyChar);
}
else if ("-".Contains(key.KeyChar) && buf.ToString().IndexOf("-") == -1 && buf.ToString() == "")
{
buf.Append(key.KeyChar);
Console.Write(key.KeyChar);
}
else if ("0123456789".Contains(key.KeyChar))
{
buf.Append(key.KeyChar);
Console.Write(key.KeyChar);
}
else
{
Console.Beep();
}
}
}
var getInput=Console.ReadLine();
int option;
//validating input
while(!int.TryParse(getInput, out option))
{
Console.WriteLine("Incorrect input type. Please try again");
getInput=Console.ReadLine();
}
Try This Simple
try
{
string x= "aaa";
Convert.ToInt16(x);
//if success is integer not go to catch
}
catch
{
//if not integer
return;
}

Categories