当Console.ReadKey收到C#中的Escape键时,如何修复Console输出?

我想从面向.NET Core 3.1的C#控制台应用程序中读取用户输入。 在某些情况下,用户应可以选择在提示用户进行输入的任何时间点按Escape键来取消输入和相关操作。

以下脚本适用于空字符串(用户只需按Enter键,而没有其他操作),以及普通用户输入(用户输入字符并按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.

与此直接相关的另一个问题是,当我注入一个额外的换行符时,在正常输入的情况下,我会收到2个换行符,而在输入被取消的情况下,我将收到1个换行符。

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;
        }
    }
}

回到很早以前(“旧时代”),由于必须同时提交控制字符,因此我不得不翻倍/三倍地读取键盘输入。但是,在注册了Escape键之后,输入缓冲区中什么也没有。

如何解决与按Escape键相关的输入取消问题?

示例1(正确)

Enter your first name: Test<Enter>
Hello, Test!
Press any key to exit...

示例2(错误)

Enter your first name: Test<Escape>
ello, World!
Press any key to exit...

示例3(正确)

Enter your first name: <Enter>
Hello, World!
Press any key to exit...

示例4(错误)

Enter your first name: <Escape>
ello, World!
Press any key to exit...
评论