问题
I want to read user inputs from a C# console application targeting .NET Core 3.1. In some situations, the user shall have the choice to cancel the input and the related operation by pressing the Escape key at any point of time when the user is prompted for input.
The following script works for empty strings (user just hit Enter and nothing else), and normal user input (user enters characters and confirms with Enter).
But there is a issue: When the user cancels the input (hits Escape), the first character of the following Console.WriteLine
is suppressed, and Console.Write
outputs nothing. Observations shows, that an line break must be outputted to restore Console.WriteLine
and Console.Write
expected behaviour.
Another issue directly related to this is that when I inject an additional line break, in the case of a normal input, I receive 2 line breaks, and in case of a canceled input, I receive 1 line break.
using System;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApp1
{
internal class Program
{
internal const ConsoleColor DefaultConsoleForegroundColor = ConsoleColor.Gray;
internal const ConsoleColor DefaultConsoleInputForegroundColor = ConsoleColor.White;
internal static readonly Regex WhitespaceRegex = new Regex(@"\s{2,}", RegexOptions.Compiled | RegexOptions.CultureInvariant);
internal static readonly Encoding UTF8NoBomEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false);
private static void Main()
{
Console.InputEncoding = UTF8NoBomEncoding;
Console.OutputEncoding = UTF8NoBomEncoding;
Console.Title = @"ConsoleApp1";
Console.TreatControlCAsInput = true;
Console.Clear();
Console.ResetColor();
Console.ForegroundColor = DefaultConsoleForegroundColor;
string firstName = ConsoleReadLine(message: @"Enter your first name: ", treatEmptyOrWhitespaceAsNull: true) ?? @"World";
Console.WriteLine(@"Hello, {0}!", firstName);
ConsoleReadKey(message: @"Press any key to exit...", hideInput: true, messageColor: DefaultConsoleInputForegroundColor);
}
internal static string ConsoleReadLine(string message = null, bool hideInput = false, bool treatEscapeAsCancel = true,
bool trimInput = true, bool normalizeWhitespace = true,
bool treatEmptyOrWhitespaceAsNull = false, bool addLineBreak = true,
ConsoleColor messageColor = DefaultConsoleForegroundColor,
ConsoleColor inputColor = DefaultConsoleInputForegroundColor)
{
string inputString;
// Print optional message before the prompt (same line).
Console.ForegroundColor = messageColor;
if (!string.IsNullOrWhiteSpace(message))
Console.Write(message);
// Get input from user.
Console.ForegroundColor = inputColor;
StringBuilder inputBuilder = new StringBuilder();
while (true)
{
ConsoleKeyInfo inputKey = Console.ReadKey(hideInput);
if (inputKey.Key == ConsoleKey.Enter)
{
inputString = inputBuilder.ToString();
break;
}
else if (treatEscapeAsCancel && inputKey.Key == ConsoleKey.Escape)
{
inputString = null;
break;
}
else
inputBuilder.Append(inputKey.KeyChar);
}
// Optional input modifications.
if (inputString != null)
{
if (trimInput)
inputString = inputString.Trim();
if (normalizeWhitespace)
WhitespaceRegex.Replace(inputString, @" ");
if (treatEmptyOrWhitespaceAsNull && string.IsNullOrWhiteSpace(inputString))
inputString = null;
}
// Reset foreground color.
Console.ForegroundColor = DefaultConsoleForegroundColor;
if (addLineBreak)
Console.WriteLine();
return inputString;
}
internal static ConsoleKeyInfo ConsoleReadKey(string message = null, bool hideInput = false, bool addLineBreak = true,
ConsoleColor messageColor = DefaultConsoleForegroundColor,
ConsoleColor inputColor = DefaultConsoleInputForegroundColor)
{
ConsoleKeyInfo key;
// Print optional message before the prompt (same line).
Console.ForegroundColor = messageColor;
if (!string.IsNullOrWhiteSpace(message))
Console.Write(message);
// Get input from user.
Console.ForegroundColor = inputColor;
key = Console.ReadKey(hideInput);
// Reset foreground color.
Console.ForegroundColor = DefaultConsoleForegroundColor;
if (addLineBreak)
Console.WriteLine();
return key;
}
}
}
Back a couple of ages ("the old days"), I had to double/triple read the keyboard input due to the control character was also submitted. However, there is nothing in the input buffer after the Escape key was registered.
How to fix the input cancellation issues related to pressing the Escape key?
Example 1 (correct)
Enter your first name: Test<Enter>
Hello, Test!
Press any key to exit...
Example 2 (wrong)
Enter your first name: Test<Escape>
ello, World!
Press any key to exit...
Example 3 (correct)
Enter your first name: <Enter>
Hello, World!
Press any key to exit...
Example 4 (wrong)
Enter your first name: <Escape>
ello, World!
Press any key to exit...
回答1:
Thanks to @jscarle, the solution is to not allow Console.ReadKey to print characters, but do it manually.
Here is the code, that fixes the ConsoleReadLine
function.
while (true)
{
ConsoleKeyInfo inputKey = Console.ReadKey(true);
if (inputKey.Key == ConsoleKey.Enter)
{
inputString = inputBuilder.ToString();
break;
}
else if (treatEscapeAsCancel && inputKey.Key == ConsoleKey.Escape)
{
inputString = null;
break;
}
else
inputBuilder.Append(inputKey.KeyChar);
if (!hideInput)
Console.Write(inputKey.KeyChar);
}
来源:https://stackoverflow.com/questions/61973054/how-to-fix-console-output-when-console-readkey-receives-an-escape-key-in-c