为什么.NET Core在Raspbian上处理ReadKey的方式不同?

时间:2017-10-09 21:52:48

标签: c# raspberry-pi .net-core console-application

我正在编写.NET Core控制台应用程序。我想将控制台输入限制为每个输入的特定数量的最大字符。我有一些代码可以通过使用Console.ReadKey()而不是Console.ReadLine()构建一个字符串来实现这一点。一切都在Windows上完美地测试它。然后,当我部署到运行Raspbian的Raspberry Pi 3时,我很快遇到了各种各样的问题。我记得Linux处理行结尾的方式与Windows不同,似乎退格处理方式也不同。我改变了处理它们的方式,取消了ConsoleKey而不是字符,换行问题消失了,但退格只是有时会注册。此外,有时字符会输出到我输入框外的控制台,即使我将ReadKey设置为不自行输出到控制台。我错过了Linux处理控制台输入的方法吗?

//I replaced my calls to Console.ReadLine() with this. The limit is the
//max number of characters that can be entered in the console.
public static string ReadChars(int limit)
{
    string str = string.Empty; //all the input so far
    int left = Console.CursorLeft; //store cursor position for re-outputting
    int top = Console.CursorTop;
    while (true) //keep checking for key events
    {
        if (Console.KeyAvailable)
        {
            //true to intercept input and not output to console
            //normally. This sometimes fails and outputs anyway.
            ConsoleKeyInfo c = Console.ReadKey(true);
            if (c.Key == ConsoleKey.Enter) //stop input on Enter key
                break;
            if (c.Key == ConsoleKey.Backspace) //remove last char on Backspace
            {
                if (str != "")
                {
                     tr = str.Substring(0, str.Length - 1);
                }
            }
            else if (c.Key != ConsoleKey.Tab && str.Length < limit)
            {
                //don't allow tabs or exceeding the max size
                str += c.KeyChar;
            }
            else
            {
                //ignore tabs and when the limit is exceeded
                continue;
            }
            Console.SetCursorPosition(left, top);
            string padding = ""; //padding clears unused chars in field
            for (int i = 0; i < limit - str.Length; i++)
            {
                padding += " ";
            }
            //output this way instead
            Console.Write(str + padding);
        }
    }
    return str;
}

2 个答案:

答案 0 :(得分:3)

我测试过并发现Console.ReadKey(true)确实存在一些错误,当键入快速或重复键时,键实际上会回显到控制台。这是你不期望的,但为什么会发生我不知道。

如果您对调试它感兴趣,可以查看以下源代码

https://referencesource.microsoft.com/#mscorlib/system/console.cs,1476

我选择针对此问题制定解决方法。所以你的方法中几乎没有问题。应该处理Left ArrowRight Arrow密钥,或者不应该允许它们。我通过添加以下代码

选择了后者
if (c.Key == ConsoleKey.LeftArrow || c.Key == ConsoleKey.RightArrow) {
   continue;
}

使用下面的

键入字符时
Console.Write(str + padding);

你基本上也会打扰光标位置,这是不正确的。因此,您需要在此之后使用

设置光标位置
Console.CursorLeft = str.Length;

现在出现了处理漏洞密钥的部分,这可能是一个.NET错误,我在下面添加了代码

else
{
    //ignore tabs and when the ilimit is exceeded
    if (Console.CursorLeft > str.Length){

        var delta = Console.CursorLeft - str.Length;
        Console.CursorLeft = str.Length;
        Console.Write(new String(' ',delta));
        Console.CursorLeft = str.Length;
    }
    continue;
}

因此,我们检查任何看不见的原因,然后将其删除。然后进行压力测试

$ docker run -it console
Please enter some text:
tarun6686e
You entered: tarun6686e

以下是我用过的最终代码

using System;

namespace ConsoleTest
{
    public class Program {
        public static string tr="";
        //I replaced my calls to Console.ReadLine() with this. The limit is the
        //max number of characters that can be entered in the console.
        public static string ReadChars(int limit)
        {
            string str = string.Empty; //all the input so far
            int left = Console.CursorLeft; //store cursor position for re-outputting
            int top = Console.CursorTop;

            while (true) //keep checking for key events
            {
                if (Console.KeyAvailable)
                {
                    //true to intercept input and not output to console
                    //normally. This sometimes fails and outputs anyway.
                    ConsoleKeyInfo c = Console.ReadKey(true);
                    string name = Enum.GetName(typeof(ConsoleKey), c.Key);
                    var key = c.KeyChar;
                    // Console.WriteLine(String.Format("Name={0}, Key={1}, KeyAscii={2}", name, key,(int)key));
                    if (c.Key == ConsoleKey.Enter) //stop input on Enter key
                        {
                            Console.WriteLine();
                            break;
                        }
                    if (c.Key == ConsoleKey.LeftArrow || c.Key == ConsoleKey.RightArrow) {
                        continue;
                    }

                    if (c.Key == ConsoleKey.Backspace) //remove last char on Backspace
                    {
                        if (str != "")
                        {
                            str = str.Substring(0, str.Length - 1);
                        }
                    }
                    else if (c.Key != ConsoleKey.Tab && str.Length < limit)
                    {
                        //don't allow tabs or exceeding the max size
                        str += c.KeyChar;
                    }
                    else
                    {
                        //ignore tabs and when the ilimit is exceeded
                        if (Console.CursorLeft > str.Length){

                            var delta = Console.CursorLeft - str.Length;
                            Console.CursorLeft = str.Length;
                            Console.Write(new String(' ',delta));
                            Console.CursorLeft = str.Length;
                        }
                        continue;
                    }
                    Console.SetCursorPosition(left, top);
                    string padding = ""; //padding clears unused chars in field
                    for (int i = 0; i < limit - str.Length; i++)
                    {
                        padding += " ";
                    }
                    //output this way instead
                    Console.Write(str + padding);
                    Console.CursorLeft = str.Length;
                }
            }
            return str;
        }

        public static void Main(string[] args) {
            Console.WriteLine("Please enter some text: ");
            var text = ReadChars(10);

            Console.WriteLine("You entered: " + text);
        }
    }
}

答案 1 :(得分:3)

我认为Stephen Toub在this GitHub issue中的评论揭示了根本问题:

  

您可能会想到我们现在只在ReadKey(intercept:true)调用期间禁用echo,因此在用户输入和调用ReadKey(intercept:true)之间的竞争中,键可能是echo' d即使你希望它不会,但你也不会失去击键。

这是冷舒适,但准确。这是一场很难取胜的比赛。核心问题是Linux终端的工作方式与Windows控制台完全不同。它的运作更像是20世纪70年代的电传打字机。你敲打键盘,无论计算机是否注意你输入的内容,电传打字机都只是回应了你在纸上打出来的东西。在按Enter键之前,计算机开始启动文本。

与Windows控制台非常不同,它要求程序具有活动的读取调用以回显任何类型的文本。

所以这与控制台api非常不匹配。它需要Echo属性才能让您有正确的希望。因此,在开始接受输入并自己处理回声之前,可以将其设置为 false 。它仍然是一场比赛,但至少你有机会清除所有预先输入的文字。

在您开始执行程序之前,现在只有disable echo的唯一正常解决方法是{{3}}。要求您通过您的方法完成所有输入。