【问题标题】:Console.ReadKey Unexpected Behavior with Pasted Text - Multi-Keys Entered at OnceConsole.ReadKey 粘贴文本的意外行为 - 一次输入多个键
【发布时间】:2013-07-22 08:26:20
【问题描述】:

调用Console.ReadKey() 并在提示符处对长度> 1 的任何剪贴板文本进行粘贴(编辑->粘贴)(比如说“Hello World!”)。

我刚刚意外地发现,在随后调用 Console.ReadKey() 时,向用户显示提示,却没有给出提示!而是读取粘贴文本中的后续字符......没有提示!

底线:要使用 ReadKey,我需要确保(非常确定)当我下次调用 ReadKey 时,它实际上会给用户一个提示。目前,这使得 ReadKey 无法用于此类关键场景。

似乎没有任何方法可以禁用此功能,或者只是清除输入但尚未读出的值。

在我的例子中,这给简单的控制台应用程序带来了严重错误,我在其中循环查找单词“R”代表“RUN”,或任何“E”到“END”。由于接近这个时间,我提示输入文件路径,因此对于已经使用它的用户来说,在这一点上意外粘贴文件路径是相当容易的。问题是 (!!!),如果该文件路径包含一个 'r',它会立即运行。

using System;

namespace Practise1
{
    class Program
    {
        static void Main()
        {
            RunDialogue2();
        }

        public static void RunDialogue()
        {
            while (true) {
                Console.WriteLine("Type 'R' to RUN, 'E' to END");
                ConsoleKey key = Console.ReadKey().Key;
                Console.WriteLine("\r\n");

                switch (key) {
                    case ConsoleKey.R:
                        Console.WriteLine("RUN! ENGINES BLAST OFF!");
                        BlastOff();
                        break;
                    case ConsoleKey.E:
                        Console.WriteLine("FINISHED");
                        return;
                    default:
                        Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                        // we *thought* this would pick up bad input, but not pasted text!
                        break;
                }
            }

        }

        public static void RunDialogue2()
        {
            while (true) {
                Console.WriteLine("Type 'R' to RUN, 'E' to END");
                ConsoleKey key = Console.ReadKey().Key;
                Console.WriteLine("\r\n");

                switch (key) {
                    case ConsoleKey.R:
                        Console.WriteLine("RUN! ENGINES BLAST OFF!");
                        Console.WriteLine("We blasted off at: " + DateTime.UtcNow);
                        //BlastOff();
                        break;
                    case ConsoleKey.E:
                        Console.WriteLine("FINISHED");
                        return;
                    default:
                        Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                        // we *thought* this would pick up bad input, but not pasted text!
                        break;
                }
            }
        }
    }
}

如果你有“查理的!”在剪贴板中并粘贴它,这是输出。请注意如何获得多个 RUN!都是无意的。这太可怕了,这对我来说意味着我们永远不能依赖 ReadKey 作为一种可靠的输入法,尤其是当我们认为我们正在验证输入时:

Type 'R' to RUN, 'E' to END
C

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
h

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
a

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
r

RUN! ENGINES BLAST OFF!
We blasted off at: 7/21/2013 11:24:36 PM
Type 'R' to RUN, 'E' to END
l

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
e

FINISHED

我知道一个解决方案是改用 Console.ReadLine,但是通过单次按键立即进行交互的能力要快乐得多。有什么办法可以解决这种奇怪的行为吗?

【问题讨论】:

  • 我不会说用户没有得到提示。打印提示,然后从输入缓冲区完成读取。通常这是控制台应用程序所需的行为(它允许您一次粘贴对多个问题的响应),但在您的情况下,如 tinstaafl 所示刷新缓冲区。虽然我会在打印提示之前刷新缓冲区。
  • 那么谁否决了我的问题?绝对卑鄙。
  • 本是对的,因为就像我在我的回答中评论说的那样,我测试了,一切都是正确的
  • @BenVoigt '我不会说用户没有得到提示。' - 就个人而言,我更希望 ReadKey 只接受输入中的一个键,而丢弃其余的。但是,嘿,我不会因为这种意见分歧而投票给人们。我不在乎它的措辞,只要你没有因为一个小的措辞语义问题而否决这个问题。
  • 我也没有投反对票。

标签: c# .net console


【解决方案1】:

这样的事情将摆脱 readkey 的持续读取:

    public static void RunDialogue()
    {
        while (true)
        {
            Console.WriteLine("Type 'R' to RUN, 'E' to END");
            ConsoleKey key = Console.ReadKey().Key;
            Console.WriteLine("\r\n");
            switch (key)
            {
                case ConsoleKey.R:
                    Console.WriteLine("RUN! ENGINES BLAST OFF!");
                    BlastOff();
                    break;
                case ConsoleKey.E:
                    Console.WriteLine("FINISHED");
                    return;
                default:
                    Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                    // we *thought* this would pick up bad input, but not pasted text!
                    break;
            }
            Console.In.ReadToEnd();
        }

    }

虽然这将消除原始帖子中的直接问题,但更完整的解决方案是读取并忽略输入流的其余部分而不将其回显到屏幕上,这样的事情会起作用:

    public static void RunDialogue()
    {
        while (true)
        {
            Console.WriteLine("Type 'R' to RUN, 'E' to END");
            ConsoleKey key = Console.ReadKey().Key;
            Console.WriteLine("\r\n");
            switch (key)
            {
                case ConsoleKey.R:
                    Console.WriteLine("RUN! ENGINES BLAST OFF!");
                    BlastOff();
                    break;
                case ConsoleKey.E:
                    Console.WriteLine("FINISHED");
                    return;
                default:
                    Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                    // we *thought* this would pick up bad input, but not pasted text!
                    break;
            }
            while (Console.KeyAvailable)
            {
                Console.ReadKey(true);
            }
        }

    }

【讨论】:

  • 实际上操作员说他不希望 readkey 读取输入缓冲区的其余部分并将其分配给key,您似乎在答案中错过了这一点。我在这个答案中的两个解决方案都解决了这个问题,但就像我说的第二个是一个更完整的解决方案。
  • 老兄,加油。我以为我什么都试过了,但我错过了“Console.KeyAvailable”。太时髦了。现在我仍然对 Console.ReadKey 允许输入多个键这一事实感到失望,因为它仍然意味着(在我的示例中)如果它以“R”或“E”开头,则这些被视为实际命令,而多个字符应该被拒绝。实际上,这让我知道谁来解决这个问题,请参见下文。
  • 您看到的行为实际上是控制台的默认行为,并且可以按原样适用于许多应用程序。正如您所见,通常有一些简单的方法可以修改此行为。
  • 通过这次经历,我确实了解到这是(出乎意料的!)行为,至少对我来说是出乎意料的,尽管我认为许多其他人不会知道这一点。但这不是 StackOverflow 的重点吗? IE。如果您对此投票否决了我的问题,那么如果您不能提问,那么 SO 的意义何在?
  • 我没有否决你的问题,也不知道是谁投了反对票。需要考虑的一件事是,在提出问题时可能会隐晦地违反预期,这当然总是可以接受个人解释。这是此类网站的缺点之一。出于任何原因,甚至个人的愤怒,都可能发生反对票。就个人而言,除非这个人足够成熟和专业,可以指出为什么会投反对票,否则他们不值得考虑甚至考虑。
【解决方案2】:

感谢@tinstaafl 的回答,他通过查看Console.KeyAvailable 揭示了关键解决方案。虽然我绝对公平地将这个答案作为解决方案,但以下是最终解决方案,涵盖了您必须完全验证输入是否真的只有一个字符的场景。

        public static ConsoleKey ReadKeySingle(bool intercept = false, ConsoleKey errorKey = ConsoleKey.Clear)
        {
            ConsoleKey key = Console.ReadKey(intercept).Key;
            while (Console.KeyAvailable) {
                Console.ReadKey(true);
                key = errorKey;
            }
            return key;
        }

然后这样使用(单行改):

        public static void RunDialogue()
        {
            while (true) {
                Console.WriteLine("Type 'R' to RUN, 'E' to END");

                // KEY! Call ReadKeySingle instead
                ConsoleKey key = ReadKeySingle(); // Console.ReadKey().Key; //!!!

                Console.WriteLine("\r\n");

                switch (key) {
                    case ConsoleKey.R:
                        Console.WriteLine("RUN! ENGINES BLAST OFF!");
                        Console.WriteLine("We blasted off at: " + DateTime.UtcNow);

                        // BlastOff();
                        break;
                    case ConsoleKey.E:
                        Console.WriteLine("FINISHED");
                        return;
                    default:
                        Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                        // we *thought* this would pick up bad input, but not pasted text!
                        break;
                }
            }
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    • 2012-03-21
    • 2019-11-06
    • 1970-01-01
    • 2016-02-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多