【问题标题】:How can I allow user input from the beginning of the line in C# console app?如何在 C# 控制台应用程序的行首允许用户输入?
【发布时间】:2017-04-18 17:55:35
【问题描述】:

我知道我可以使用 Console.ReadLine() 来允许用户输入,但它只允许用户从光标所在的位置输入,我想要一种能够允许用户从行首输入的方法(超过整行),而不仅仅是光标所在的位置。例如:

for (int i = 1;i <= 5;i++) Console.Write(i+" ");
string x = Console.ReadLine();

这将给出以下输出:

1 2 3 4 5 _

其中'_'指的是光标,这样,用户将只能从光标位置开始书写——他也不能使用退格键来删除前面的字符。那么如何在光标不在行首时允许用户输入整行呢?

【问题讨论】:

  • C# shell 并不是真正为开箱即用而设计的。可以肯定的是,您只需在控制台输入中一次读取一个键,并在用户自己按下箭头键、退格键等时处理移动光标。你可能最好简单地调整你的 UI;要么使用更适合控制台应用程序的 UI,要么使用真正为更复杂的用户交互而设计的非控制台 UI。
  • @Servy 一次读取一个键当然是最接近的解决方案,但是将这些值存储在数组或列表中会更复杂,因为这不会像简单的 do-it-all Console.ReadLine() 在用户完成输入后开始处理值,因此它可以看到整行而不是当前光标位置的一个字符。
  • 我的意思是,如果你要这样做,你会想要编写自己的方法来完成所有需要的工作并在完成后返回一个字符串,以便调用者可以调用该方法并获取一个字符串。编写这样的方法确实需要大量的工作。这就是为什么我说你应该避免需要解决这个问题。

标签: c# input user-input


【解决方案1】:

这是准备好使用自定义输入处理的示例。 当然,这需要对性能进行一些调整,但它应该让您知道从哪里开始。

    static void Main(string[] args)
    {
        List<char> outputString = new List<char>();
        outputString = "test".ToCharArray().ToList();
        //If there is something in outputString write it to buffer and set cursor position
        if (outputString.Count > 0)
        {
            Console.Write(outputString.Select(x => x.ToString()).Aggregate((x, y) => x + y));
        }

        while (true)
        {
            //This will read user input but it will not print it to the console window 
            var key = Console.ReadKey(true);
            //Allow user maniputate cursor to the right
            //Here you can add another condition to prevent line overflow or some constrains
            //if(Console.CursorLeft < 30)
            if (key.Key == ConsoleKey.RightArrow && Console.CursorLeft < outputString.Count)
            {
                Console.SetCursorPosition(Console.CursorLeft + 1, Console.CursorTop);
            }
            else if (key.Key == ConsoleKey.LeftArrow && Console.CursorLeft > 0)
            {
                Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
            }
            //Here you should add all keys that your input should take, special characters are ok above ASCII number 32 which is space
            else if (key.KeyChar >= 48 && key.KeyChar <= 57 || //Read numbers
                key.KeyChar >= 64 && key.KeyChar <= 90 || //Read capital letters
                key.KeyChar >= 97 && key.KeyChar <= 122 || //Read lower letters
                key.Key == ConsoleKey.Spacebar)
            {
                Console.Write(key.KeyChar);
                if(Console.CursorLeft > outputString.Count)
                {
                    outputString.Add(' ');
                }
                outputString[Console.CursorLeft - 1] = key.KeyChar;
            }
            //This will remove character
            else if (key.Key == ConsoleKey.Backspace && Console.CursorLeft > 1)
            {
                ClearCurrentConsoleLine();
                outputString.RemoveAt(Console.CursorLeft - 1);
                var currentLeft = Console.CursorLeft;
                Console.CursorLeft = 0;
                Console.Write(outputString.Select(x => x.ToString()).Aggregate((x,y) => x + y));
                Console.CursorLeft = currentLeft - 1;
            }
            //This ends input loop
            //You can add more keys to break current loop with OR condition
            else if (key.Key == ConsoleKey.Enter)
            {
                break;
            }
        }
        //Display result and wait for any key
        Console.WriteLine(outputString.Select(x => x.ToString()).Aggregate((x, y) => x + y));
        Console.ReadKey();
    }

    //Clears current console line and sets cursor at where it was before
    public static void ClearCurrentConsoleLine()
    {
        int currentLineCursorTop = Console.CursorTop;
        int currentLineCursorLeft = Console.CursorLeft;
        Console.SetCursorPosition(0, Console.CursorTop);
        Console.Write(new string(' ', Console.WindowWidth));
        Console.SetCursorPosition(currentLineCursorLeft, currentLineCursorTop);
    }

【讨论】:

  • 太棒了!在这种情况下完美地处理了用户输入,但是在按下 Enter 之后,有没有办法读取整行以开始处理用户输入的内容,甚至在输入过程开始时?就像 Console.ReadLine() 所做的那样,读取在将其存储在字符串中之后,整行开始在下一个操作中处理它。
  • 整个数据在 outputString 变量中,它是字符列表。要将其转换为纯字符串,您需要像我在每个 writeline 方法之前一样聚合一个数组。
  • 不支持的一些内容:其他语言中的字符(特别注意的是代理对)、home/end、从剪贴板粘贴、标点符号以及到达控制台水平缓冲区的末尾.我敢肯定还有更多,但这只是我的想法。
  • 这只是一个开始,一个想法,需要进一步改进,您应该添加所有这些功能,但它们很容易添加。另外,如果我没记错的话,C# Console 无论如何都只支持 ASCII。 @Servy 欢迎来到 StackOverflow,我们不为他人编写代码,而是为他们指明正确的方向 :)
  • @TomaszJuszczak 实际上,其中许多功能很难添加。至于什么是 SO 作为一个站点,它是一个我们为质量问题提供高质量答案的站点。这不是人们提供并不能真正解决问题的一半答案的地方。该网站的设计是问题的范围仅限于可以快速简洁地回答的问题,而无需涉及从头开始编写相关功能的答案。
【解决方案2】:

您可以为此使用Console.CursorLeft = 0;。与Console.SetCursorPosition 相比的优势在于,您不必计算 y 坐标,因此光标停留在当前行。

我想为此做一个例子(在这两种情况下,我们都希望光标移动到行首):

Console.SetCursorPosition(0, Console.CursorTop); //With SetCursorPosition
Console.CursorLeft = 0; //With CursorLeft

但是,我认为没有一种优雅的方式可以让用户删除程序编写的字符。您仍然可以不断检查用户是否按下 Backshift,然后删除最后一个字符并将光标向后移动,而您必须异步检查哪些不会产生好的解决方案。

【讨论】:

  • 但是 OP 不想 想要移动光标。他们希望光标停留在原处,但只允许用户自己将其移动到开始位置的左侧。
  • @Servy 保持冷静,我实际上正在编辑这个答案。
  • 如果你还没有写完你的答案,那么你不应该发布它。仅在您认为已准备好进行评估时才发布您的答案。也就是说,您的编辑并没有解决它的问题;答案绝不回答问题。
  • 实际上,在对问题进行编辑之前,我很确定我的答案适合该问题。
  • @MetaColon 感谢您的回答,但正如 Servy 所说,我不想移动光标,我希望它保持原位,但允许用户从该点开始输入。
猜你喜欢
  • 1970-01-01
  • 2012-10-17
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多