如何在.Net控制台应用程序中设置默认输入值?

20

如何在 .net 控制台应用程序中设置默认输入值?

这是一些虚构的代码:

Console.Write("Enter weekly cost: ");
string input = Console.ReadLine("135"); // 135 is the default. The user can change or press enter to accept
decimal weeklyCost = decimal.Parse(input);

当然,我并不指望它会这么简单。我打赌会涉及到一些低级别的、非托管的事情;我只是不知道如何实现。

编辑

我知道我可以用默认值替换没有输入的情况。这不是我的问题所在。我试图学习所需实现的行为:给用户提供可编辑的默认值。我也不担心输入验证;我的问题与此无关。


你可以按照答案建议的编写代码 - 用户不会关心编码技巧。对于理论问题,如果有一种使用readline的方法来解决它 - 可能没有(至少没有记录)。 - Dani
但是,我明白你的意图,我们正在寻找一种解决方案,使用户能够更改默认文本。 - Dani
我知道不能用.ReadLine()来完成这个任务。但是,我知道有一种方法可以做到这一点。 - Ronnie Overby
8个回答

11

我认为您需要手动监听每个按键来完成此操作:

以下是快速拼凑的示例:

   // write the initial buffer
   char[] buffer = "Initial text".ToCharArray();
   Console.WriteLine(buffer);

   // ensure the cursor starts off on the line of the text by moving it up one line
   Console.SetCursorPosition(Console.CursorLeft + buffer.Length, Console.CursorTop - 1);

   // process the key presses in a loop until the user presses enter
   // (this might need to be a bit more sophisticated - what about escape?)
   ConsoleKeyInfo keyInfo = Console.ReadKey(true);
   while (keyInfo.Key != ConsoleKey.Enter)
   {

       switch (keyInfo.Key)
       {
            case ConsoleKey.LeftArrow:
                    ...
              // process the left key by moving the cursor position
              // need to keep track of the position in the buffer

         // if the user presses another key then update the text in our buffer
         // and draw the character on the screen

         // there are lots of cases that would need to be processed (backspace, delete etc)
       }
       keyInfo = Console.ReadKey(true);
   }

这相当复杂 - 您需要确保光标不超出范围并手动更新缓冲区。


我认为这不是问题的意思。 - driis
4
实际上,这无疑是目前为止最好的回答。 - Ronnie Overby
将此代码放入扩展方法中,这样您就可以调用Console.ReadLine("135"); 扩展方法可以重载现有方法吗?如果不能,请为其命名。 - CoderDennis
1
@Dennis - 你不能将扩展方法添加到静态类中,因为你无法创建它们的实例。也许可以使用SuperConsole.ReadLine("default")?有很多情况需要处理,但编写代码并将其打包成DLL应该是值得的。 - Ronnie Overby
当我尝试为控制台创建扩展方法时,出现了“静态类型不能用作参数”的错误。因此,这可能行不通,也许需要在 Stack Overflow 上提一个新问题。 - CoderDennis

9
这里有一个简单的解决方案:
public static string ConsoleReadLineWithDefault(string defaultValue)
{
    System.Windows.Forms.SendKeys.SendWait(defaultValue);
    return Console.ReadLine();
}

然而,它还不完整。在SendWait输入字符串中的一些字符具有特殊含义,因此您必须对它们进行转义(例如+、(、)等)。 请参阅:http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx 以获得完整描述。


7

我已经按照Matt的实现方法完成了这个任务:

    public static string ReadInputWithDefault(string defaultValue, string caret = "> ")
    {
        Console.WriteLine(); // make sure we're on a fresh line

        List<char> buffer = defaultValue.ToCharArray().Take(Console.WindowWidth - caret.Length - 1).ToList();
        Console.Write(caret); 
        Console.Write(buffer.ToArray());
        Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop);

        ConsoleKeyInfo keyInfo = Console.ReadKey(true);
        while (keyInfo.Key != ConsoleKey.Enter)
        {
            switch (keyInfo.Key)
            {
                case ConsoleKey.LeftArrow:
                    Console.SetCursorPosition(Math.Max(Console.CursorLeft - 1, caret.Length), Console.CursorTop);
                    break;
                case ConsoleKey.RightArrow:
                    Console.SetCursorPosition(Math.Min(Console.CursorLeft + 1, caret.Length + buffer.Count), Console.CursorTop);
                    break;
                case ConsoleKey.Home:
                    Console.SetCursorPosition(caret.Length, Console.CursorTop);
                    break;
                case ConsoleKey.End:
                    Console.SetCursorPosition(caret.Length + buffer.Count, Console.CursorTop);
                    break;
                case ConsoleKey.Backspace:
                    if (Console.CursorLeft <= caret.Length)
                    {
                        break;
                    }
                    var cursorColumnAfterBackspace = Math.Max(Console.CursorLeft - 1, caret.Length);
                    buffer.RemoveAt(Console.CursorLeft - caret.Length - 1);
                    RewriteLine(caret, buffer);
                    Console.SetCursorPosition(cursorColumnAfterBackspace, Console.CursorTop);
                    break;
                case ConsoleKey.Delete:
                    if (Console.CursorLeft >= caret.Length + buffer.Count)
                    {
                        break;
                    }
                    var cursorColumnAfterDelete = Console.CursorLeft;
                    buffer.RemoveAt(Console.CursorLeft - caret.Length);
                    RewriteLine(caret, buffer);
                    Console.SetCursorPosition(cursorColumnAfterDelete, Console.CursorTop);
                    break;
                default:
                    var character = keyInfo.KeyChar;
                    if (character < 32) // not a printable chars
                        break;
                    var cursorAfterNewChar = Console.CursorLeft + 1;
                    if (cursorAfterNewChar > Console.WindowWidth || caret.Length + buffer.Count >= Console.WindowWidth - 1)
                    {
                        break; // currently only one line of input is supported
                    }
                    buffer.Insert(Console.CursorLeft - caret.Length, character);
                    RewriteLine(caret, buffer);
                    Console.SetCursorPosition(cursorAfterNewChar, Console.CursorTop);
                    break;
            }
            keyInfo = Console.ReadKey(true);
        }
        Console.Write(Environment.NewLine);

        return new string(buffer.ToArray());
    }

    private static void RewriteLine(string caret, List<char> buffer)
    {
        Console.SetCursorPosition(0, Console.CursorTop);
        Console.Write(new string(' ', Console.WindowWidth - 1));
        Console.SetCursorPosition(0, Console.CursorTop);
        Console.Write(caret);
        Console.Write(buffer.ToArray());
    }

提示:

  • 仅适用于单行输入
  • 你可以定义可编辑文本区域前的内容(caret参数)
  • 使用需谨慎,可能仍存在一些越界问题。 ;)

5

或者...只需测试输入的值,如果为空,则将默认值放入输入框。


4

现在有更好的方法来解决这个问题,可以在nuget上查看Readlinehttps://www.nuget.org/packages/ReadLine

  1. install-package Readline
  2. var input = ReadLine.Read("输入每周成本: ", "135");

我喜欢使用控制台编写交互式测试,并且默认值可以帮助完成测试。


4
这里有些微小的区别:@default 只是一个默认值,而不是用户可以删除的自动插入的值。 - Peder Rice
一个好的框架可供使用...但只支持 .NET Standard 2.0...不适用于 .NET Framework...使用 .NET Framework 安装该包时会出现错误。 - Marcus.D
抱歉,我的说法是错误的。我发现我需要至少 .NET Framework 4.6.1,但在我的项目中我使用了 4.5.2。这就是为什么我无法使用那个库/nuget包的原因。 - Marcus.D

3
  1. 在您的项目中添加对程序集库"System.Windows.Forms"的引用
  2. 在您的Console.WriteLine命令之后,Console.ReadLine命令之前立即添加SendKeys.SendWait("DefaultText")
string _weeklycost = "";
Console.WriteLine("Enter weekly cost: ");
System.Windows.Forms.SendKeys.SendWait("135");
_weeklycost = Console.ReadLine();

0

简单的解决方案,如果用户没有输入任何内容,则分配默认值:

Console.Write("Enter weekly cost: ");
string input = Console.ReadLine();
decimal weeklyCost = String.IsNullOrEmpty(input) ? 135 : decimal.Parse(input);

在处理用户输入时,您应该预期它可能包含错误。因此,您可以使用TryParse来避免异常,如果用户没有输入数字:

Console.Write("Enter weekly cost: ");
string input = Console.ReadLine(); 
decimal weeklyCost;
if ( !Decimal.TryParse(input, out weeklyCost) ) 
    weeklyCost = 135;

这被认为是处理用户输入的最佳实践。如果您需要解析多个用户输入,请使用一个辅助函数来进行解析。一种方法是使用可空值的方法,并在解析失败时返回 null。然后,可以使用 null coalescing operator 来轻松分配默认值:

public static class SafeConvert
{
    public static decimal? ToDecimal(string value)
    {
        decimal d;
        if (!Decimal.TryParse(value, out d))
            return null;
        return d;
    }
}

那么,读取输入并分配默认值就像这样简单:

decimal d = SafeConvert.ToDecimal(Console.ReadLine()) ?? 135;

你把他虚构的参数留在了 ReadLine 的位置。 - Adam Robinson

0
你可以像这样使用辅助方法:
public static string ReadWithDefaults(string defaultValue)
{
    string str = Console.ReadLine();
    return String.IsNullOrEmpty(str) ? defaultValue : str;
}

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