我正在编写.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;
}
答案 0 :(得分:3)
我测试过并发现Console.ReadKey(true)
确实存在一些错误,当键入快速或重复键时,键实际上会回显到控制台。这是你不期望的,但为什么会发生我不知道。
如果您对调试它感兴趣,可以查看以下源代码
https://referencesource.microsoft.com/#mscorlib/system/console.cs,1476
我选择针对此问题制定解决方法。所以你的方法中几乎没有问题。应该处理Left Arrow
和Right 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}}。要求您通过您的方法完成所有输入。