如何在C#中等待大约15秒钟的控制台输入?

8

我需要在控制台中请求输入,并等待几秒钟,然后默认设置为某个值。如何在C#中基本计时控制台输入,并使用默认设置?如果您有示例代码,那就太好了。


你真不应该用“等等”这样的标签来表示事物。它毫无意义,所以我用C#代替了它。 - Mark
“wait” 也不是很好。我添加了更合适的标签。 - Jason Bunting
1
可能是如何为Console.ReadLine()添加超时?的重复问题。 - Steve Weet
4个回答

3
你可以使用 System.Timers.Timer 中的计时器对象,并将其设置为 60 秒,启用它。如果有人在控制台中输入了任何内容,请禁用它,否则就在 Timer.Elapsed 事件中处理发生的情况。
static void Main(string[] args)
        {
            System.Timers.Timer timer = new System.Timers.Timer(60000);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(T_Elapsed);
            timer.Start();
            var i = Console.ReadLine();
            if (string.IsNullOrEmpty(i)) 
            {
                timer.Stop();
                Console.WriteLine("{0}", i);
            }
        }

        static void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Console.WriteLine("Defult Values Used");
            var T = (Timer)sender;
            T.Stop;

        }

不确定这是否是最好的方法。我已经测试过了,但正如我所说,这可能不是最好的方式。


3
这个模型的问题在于,ReadLine是一个阻塞调用,直到输入完一行才会返回结果。所以,尽管默认值可能在计时器到期时被发送,但程序在文本输入完成之前不会继续执行。 - ICR
嗯,我真的没有考虑到那个。糟糕。 - Nathan W

3

您可能希望考虑添加一个命令行参数,以充当自动模式和完全用户输入模式之间的开关。

如果您使用特定的解释方式,则变得非常简单。在此模型中,提示用户进行输入。如果超时时间内未输入任何内容,则使用默认值。如果他们开始输入,则不使用超时时间。这也解决了可用性问题,即当他们花费很长时间输入内容时,放弃并使用默认值。

    static void Main(string[] args)
    {
        Console.WriteLine("Please enter your name ");
        string input;

        if (TryReadLine(out input, 10000, true))
        {
            Console.WriteLine(input);
        }
        else
        {
            Console.WriteLine("[DEFAULT]");
        }

        Console.ReadKey(true);
    }

    const string timerString = "[{0} seconds until default value is used]";

    public static bool TryReadLine(out string s, double timeout, bool showTimer)
    {
        DateTime timeoutDateTime = DateTime.Now.AddMilliseconds(10000);
        DateTime nextTimer = DateTime.Now;
        while (DateTime.Now < timeoutDateTime)
        {
            if (Console.KeyAvailable)
            {
                ClearTimer(timeoutDateTime);
                s = Console.ReadLine();
                return true;
            }

            if (showTimer && DateTime.Now > nextTimer)
            {
                WriteTimer(string.Format(timerString, (timeoutDateTime - DateTime.Now).Seconds));
                nextTimer = DateTime.Now.AddSeconds(1);
            }
        }

        ClearTimer(timeoutDateTime);
        s = null;
        return false;
    }

    private static void ClearTimer(DateTime timeoutDateTime)
    {
        WriteTimer(new string(' ', string.Format(timerString, (timeoutDateTime - DateTime.Now).Seconds).Length));
    }

    private static void WriteTimer(string s)
    {
        int cursorLeft = Console.CursorLeft;
        Console.CursorLeft = 0;
        Console.CursorTop += 1;
        Console.Write(s);
        Console.CursorLeft = cursorLeft;
        Console.CursorTop -= 1;
    }
}

因为在我意识到有更好的方法之前,我花了很长时间,所以这里是一些我刚刚编写的代码,用于从控制台读取字符串,并具有超时选项。它还可以选择将剩余时间打印到控制台。它没有经过非常彻底的测试,因此可能存在许多错误。回调功能使用.NET 3.0 Action,但如果这是针对C# 2.0,则可以将其转换为委托。

    static void Main(string[] args)
    {
        string input;
        Console.Write("Please enter your name (");
        int timerPromptStart = Console.CursorLeft;
        Console.Write("    seconds left): ");
        if (TryReadLine(out input, 10000, delegate(TimeSpan timeSpan)
                                          {
                                              int inputPos = Console.CursorLeft;
                                              Console.CursorLeft = timerPromptStart;
                                              Console.Write(timeSpan.Seconds.ToString("000"));
                                              Console.CursorLeft = inputPos;
                                          },
                                          1000))
        {
            Console.WriteLine(input);
        }
        else
        {
            Console.WriteLine("DEFAULT");
        }
        while (true) { }
    }

    /// <summary>
    /// Tries to read a line of input from the Console.
    /// </summary>
    /// <param name="s">The string to put the input into.</param>
    /// <param name="timeout">The time in milliseconds before the attempt fails.</param>
    /// <returns>Whether the user inputted a line before the timeout.</returns>
    public static bool TryReadLine(out string s, double timeout)
    {
        return TryReadLine(out s, timeout, null, 0);
    }

    /// <summary>
    /// Tries to read a line of input from the Console.
    /// </summary>
    /// <param name="s">The string to put the input into.</param>
    /// <param name="timeout">The time in milliseconds before the attempt fails.</param>
    /// <param name="timerCallback">A function to call every callbackInterval.</param>
    /// <param name="callbackInterval">The length of time between calls to timerCallback.</param>
    /// <returns>Whether the user inputted a line before the timeout.</returns>
    public static bool TryReadLine(out string s, double timeout, Action<TimeSpan> timerCallback, double callbackInterval)
    {
        const int tabLength = 6;

        StringBuilder inputBuilder = new StringBuilder();
        int readStart = Console.CursorLeft;
        int lastLength = 0;
        bool isInserting = true;
        DateTime endTime = DateTime.Now.AddMilliseconds(timeout);
        DateTime nextCallback = DateTime.Now;
        while (DateTime.Now < endTime)
        {
            if (timerCallback != null && DateTime.Now > nextCallback)
            {
                nextCallback = DateTime.Now.AddMilliseconds(callbackInterval);
                timerCallback((endTime - DateTime.Now));
            }

            if (Console.KeyAvailable)
            {
                ConsoleKeyInfo key = Console.ReadKey(true);
                switch (key.Key)
                {
                    case ConsoleKey.Enter:
                        Console.WriteLine();
                        s = inputBuilder.ToString();
                        return true;

                    case ConsoleKey.Backspace:
                        if (Console.CursorLeft > readStart)
                        {
                            Console.CursorLeft -= 1;
                            inputBuilder.Remove(Console.CursorLeft - readStart, 1);
                        }
                        break;

                    case ConsoleKey.Delete:
                        if (Console.CursorLeft < readStart + inputBuilder.Length)
                        {
                            inputBuilder.Remove(Console.CursorLeft - readStart, 1);
                        }
                        break;

                    case ConsoleKey.Tab:
                        // Tabs are very difficult to handle properly, so we'll simply replace it with spaces.
                        AddOrInsert(inputBuilder, new String(' ', tabLength), isInserting, readStart);
                        Console.CursorLeft += tabLength;
                        break;

                    case ConsoleKey.Escape:
                        Console.CursorLeft = readStart;
                        inputBuilder = new StringBuilder();
                        break;

                    case ConsoleKey.Insert:
                        isInserting = !isInserting;
                        // This may be dependant on a variable somewhere.
                        if (isInserting)
                        {
                            Console.CursorSize = 25;
                        }
                        else
                        {
                            Console.CursorSize = 50;
                        }
                        break;

                    case ConsoleKey.Home:
                        Console.CursorLeft = readStart;
                        break;

                    case ConsoleKey.End:
                        Console.CursorLeft = readStart + inputBuilder.Length;
                        break;

                    case ConsoleKey.LeftArrow:
                        if (Console.CursorLeft > readStart)
                        {
                            Console.CursorLeft -= 1;
                        }
                        break;

                    case ConsoleKey.RightArrow:
                        if (Console.CursorLeft < readStart + inputBuilder.Length)
                        {
                            Console.CursorLeft += 1;
                        }
                        break;

                    case ConsoleKey.UpArrow:
                        // N.B. We can't handle Up like we normally would as we don't know the last console input.
                        //      You might want to handle this so it works appropriately within your own application.
                        break;

                    case ConsoleKey.PageUp:
                    case ConsoleKey.PageDown:
                    case ConsoleKey.PrintScreen:
                    case ConsoleKey.LeftWindows:
                    case ConsoleKey.RightWindows:
                    case ConsoleKey.Sleep:
                    case ConsoleKey.F1:
                    case ConsoleKey.F2:
                    case ConsoleKey.F3:
                    case ConsoleKey.F4:
                    case ConsoleKey.F5:
                    case ConsoleKey.F6:
                    case ConsoleKey.F7:
                    case ConsoleKey.F8:
                    case ConsoleKey.F9:
                    case ConsoleKey.F10:
                    case ConsoleKey.F11:
                    case ConsoleKey.F12:
                    case ConsoleKey.F13:
                    case ConsoleKey.F14:
                    case ConsoleKey.F15:
                    case ConsoleKey.F16:
                    case ConsoleKey.F17:
                    case ConsoleKey.F18:
                    case ConsoleKey.F19:
                    case ConsoleKey.F20:
                    case ConsoleKey.F21:
                    case ConsoleKey.F22:
                    case ConsoleKey.F23:
                    case ConsoleKey.F24:
                    case ConsoleKey.BrowserBack:
                    case ConsoleKey.BrowserForward:
                    case ConsoleKey.BrowserStop:
                    case ConsoleKey.BrowserRefresh:
                    case ConsoleKey.BrowserSearch:
                    case ConsoleKey.BrowserFavorites:
                    case ConsoleKey.BrowserHome:
                    case ConsoleKey.VolumeMute:
                    case ConsoleKey.VolumeUp:
                    case ConsoleKey.VolumeDown:
                    case ConsoleKey.MediaNext:
                    case ConsoleKey.MediaPrevious:
                    case ConsoleKey.MediaStop:
                    case ConsoleKey.MediaPlay:
                    case ConsoleKey.LaunchMail:
                    case ConsoleKey.LaunchMediaSelect:
                    case ConsoleKey.LaunchApp1:
                    case ConsoleKey.LaunchApp2:
                    case ConsoleKey.Play:
                    case ConsoleKey.Zoom:
                    case ConsoleKey.NoName:
                    case ConsoleKey.Pa1:
                        // These keys shouldn't do anything.
                        break;

                    case ConsoleKey.Clear:
                    case ConsoleKey.Pause:
                    case ConsoleKey.Select:
                    case ConsoleKey.Print:
                    case ConsoleKey.Execute:
                    case ConsoleKey.Process:
                    case ConsoleKey.Help:
                    case ConsoleKey.Applications:
                    case ConsoleKey.Packet:
                    case ConsoleKey.Attention:
                    case ConsoleKey.CrSel:
                    case ConsoleKey.ExSel:
                    case ConsoleKey.EraseEndOfFile:
                    case ConsoleKey.OemClear:
                        // I'm not sure what these do.
                        break;

                    default:
                        Console.Write(key.KeyChar);
                        AddOrInsert(inputBuilder, key.KeyChar.ToString(), isInserting, readStart);
                        break;
                }

                // Write what has current been typed in back out to the Console.
                // We write out everything after the cursor to handle cases where the current input string is shorter than before
                // (i.e. the user deleted stuff).
                // There is probably a more efficient way to do this.
                int oldCursorPos = Console.CursorLeft;
                Console.CursorLeft = readStart;
                Console.Write(inputBuilder.ToString());
                if (lastLength > inputBuilder.Length)
                {
                    Console.Write(new String(' ', lastLength - inputBuilder.Length));
                }
                lastLength = inputBuilder.Length;
                Console.CursorLeft = oldCursorPos;
            }
        }

        // The timeout period was reached.
        Console.WriteLine();
        s = null;
        return false;
    }

    // This is a rather ugly helper method to add text to the inputBuilder, either inserting or appending as appropriate.
    private static void AddOrInsert(StringBuilder inputBuilder, string s, bool insert, int readStart)
    {
        if (Console.CursorLeft < readStart + inputBuilder.Length + (insert ? -1 : 1))
        {
            if (!insert)
            {
                inputBuilder.Remove(Console.CursorLeft - 1 - readStart, 1);
            }
            inputBuilder.Insert(Console.CursorLeft - 1 - readStart, s);
        }
        else
        {
            inputBuilder.Append(s);
        }
    }
}

2

请参考这里的内容,它使用了一种简单有效的轮询技术来处理控制台输入,虽然有些粗糙,但非常实用。


0
一种方法是循环调用Console.In.Peek(),等待输入或足够的时间过去。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接