【问题标题】:Using Readline() and ReadKey() Simultaneously同时使用 Readline() 和 ReadKey()
【发布时间】:2012-01-09 14:06:42
【问题描述】:

有没有什么方法可以同时检测 Readline 和 ReadKey,以便在大多数情况下它表现为 readline,除了一些应该检测的特殊键输入?

我需要一些“并行”实现来引入同时性。 下面的代码是同步的,不符合我的需要

while ((line = Console.ReadLine()) != "x")
{    
    if (line == "BLABLA")
    {
        //Stuff
    }

    else
    {
        //Stuff
    }

    ConsoleKeyInfo ki = Console.ReadKey(true);
    if ((ki.Key == ConsoleKey.V) && (ki.Modifiers == ConsoleModifiers.Control))
    {
        //Stuff
    }
}

【问题讨论】:

标签: c# console-application


【解决方案1】:

这是我刚刚创建的一个函数。

目前它只处理 BackspaceEnterEsc,但如果您认为有必要,可以轻松修改它以处理其他键.

    // returns null if user pressed Escape, or the contents of the line if they pressed Enter.
    private static string ReadLineOrEsc()
    {
        string retString = "";

        int curIndex = 0;
        do
        {
            ConsoleKeyInfo readKeyResult = Console.ReadKey(true);

            // handle Esc
            if (readKeyResult.Key == ConsoleKey.Escape)
            {
                Console.WriteLine();
                return null;
            }

            // handle Enter
            if (readKeyResult.Key == ConsoleKey.Enter)
            {
                Console.WriteLine();
                return retString;
            }

            // handle backspace
            if (readKeyResult.Key == ConsoleKey.Backspace)
            {
                if (curIndex > 0)
                {
                    retString = retString.Remove(retString.Length - 1);
                    Console.Write(readKeyResult.KeyChar);
                    Console.Write(' ');
                    Console.Write(readKeyResult.KeyChar);
                    curIndex--;
                }
            }
            else
            // handle all other keypresses
            {
                retString += readKeyResult.KeyChar;
                Console.Write(readKeyResult.KeyChar);
                curIndex++;
            }
        }
        while (true);
    }

【讨论】:

  • 基于密钥的四个备选行动方案,我会选择 switch 声明而不是三个 ifelse
【解决方案2】:

不,不是这样。这两种方法都会阻塞,直到用户在控制台上输入内容。因此,即使您能找到一种方法让两者并行运行,也无法确定哪一个获得第一枪。

有一个(不明显)类似的问题:如何在没有用户输入的情况下在一定时间后使Console.ReadLine() 中止/中断。

这里已经多次尝试解决这个问题:

大多数都是围绕创建您自己的 ReadLine 函数版本(添加超时(或在您的情况下对某些字符(代码)进行特殊处理)或使用某种线程进行建模。

这两种方法都不是微不足道的,或者有自己的问题(确保您查看了 cmets,即使是接受的答案也是如此)。

简而言之,我认为您需要推出自己的 ReadLine 版本,基于 Console.ReadKey,包括您的特殊处理以及您需要的大部分真正的 Console.ReadLine 行为。请注意,这甚至包括诸如返回、箭头键、退格处理等基本内容。

更新:有来自 Mono projectgetline.cs 代码,它实现了行编辑功能,就像一些古老的 UNIX shell 提供的一样(EMACS 模式,如果你关心的话)。为此,我相信它需要实现某种 ReadLine 替换,尽管我还没有检查过。也许你可以以此为起点。

【讨论】:

  • 绝对不值得去实现一个完整的读行逻辑。这样一个“小”功能太麻烦了。简直不敢相信这是不可能的,因为这是人们真正期望从外壳中获得的功能。此外,我真的没有明白这一点:一定时间后中止readline与我的帖子有什么关系?
  • @MikaJacobi 它还要求您以一种最初没有考虑过的方式定制 ReadLine。特别是,至少某些实现还需要重写,您可以使用您的功能进行扩展。
  • @MehdiLAMRANI,我知道这个问题已经有 8 年历史了,但似乎 Overlord Zurg 的解决方案可以满足您的需求以及 Christian.K 所描述的问题。
【解决方案3】:

这是我创建的一种非常有效的方法。您不必按两次按钮即可开始出现字符串。基本上这取代了Console.ReadLine(),但它也寻找 Esc 键被按下。看看方法的返回类型,如果是null,你就知道Esc被按下了。

private string ReadLineOrEscape()
{
    ConsoleKeyInfo keyInfo = new ConsoleKeyInfo();
    StringBuilder sb = new StringBuilder();
    int index = 0;

    while (keyInfo.Key != ConsoleKey.Enter)
    {
        keyInfo = Console.ReadKey(true);

        if (keyInfo.Key == ConsoleKey.Escape)
        {
            return null;
        }

        if(keyInfo.Key == ConsoleKey.Backspace)
        {
            if (index > 0)
            {
                Console.CursorLeft = index - 1;

                sb.Remove(index - 1, 1);

                Console.Write(" \b");

                index--;
            }
            
        }

        if(keyInfo.KeyChar > 31 && keyInfo.KeyChar < 127)
        {
            index++;
            Console.Write(keyInfo.KeyChar);
            sb.Append(keyInfo.KeyChar);

        }

        
    }
    return sb.ToString(); ;
}

【讨论】:

    【解决方案4】:

    针对@Overlord Zurd,我改进了用户提供的代码。

    public class ConsoleOutput
    {
        private ConsoleOutputType OutputType { get; set; }
        private object MyObject { get; }
    
        private static bool IsInserting { get; set; }
        public string KeyName => IsKey() && (ConsoleKeyInfo)MyObject != null ? ((ConsoleKeyInfo)MyObject).Key.ToString() : "Null";
        public string OutputString => !IsKey() && MyObject != null ? (string)MyObject : string.Empty;
    
        public static event Action<string> ReadInput = delegate { };
    
        public static event Action<ConsoleKeyInfo> ReadKey = delegate { };
    
        private ConsoleOutput()
        {
        }
    
        public ConsoleOutput(object obj)
        {
            MyObject = obj;
    
            OutputType = obj is ConsoleKeyInfo ? ConsoleOutputType.Key : ConsoleOutputType.Value;
        }
    
        public bool IsKey()
        {
            return OutputType == ConsoleOutputType.Key;
        }
    
        public bool IsExitKey()
        {
            if (!IsKey())
                return false;
    
            var info = ((ConsoleKeyInfo)MyObject);
            return (info.Modifiers & ConsoleModifiers.Control) != 0 && info.Key == ConsoleKey.B;
        }
    
        public string GetValue()
        {
            return (string)MyObject;
        }
    
        // returns null if user pressed Escape, or the contents of the line if they pressed Enter.
        public static ConsoleOutput ReadLineOrKey()
        {
            string retString = "";
    
            int curIndex = 0;
            do
            {
                ConsoleKeyInfo readKeyResult = Console.ReadKey(true);
    
                // handle Enter
                if (readKeyResult.Key == ConsoleKey.Enter)
                {
                    ReadInput?.Invoke(retString);
    
                    Console.WriteLine();
                    return new ConsoleOutput(retString);
                }
    
                // handle backspace
                if (readKeyResult.Key == ConsoleKey.Backspace)
                {
                    if (curIndex > 0)
                    {
                        retString = retString.Remove(retString.Length - 1);
    
                        Console.Write(readKeyResult.KeyChar);
                        Console.Write(' ');
                        Console.Write(readKeyResult.KeyChar);
    
                        --curIndex;
                    }
                }
                else if (readKeyResult.Key == ConsoleKey.Delete)
                {
                    if (retString.Length - curIndex > 0)
                    {
                        // Store current position
                        int curLeftPos = Console.CursorLeft;
    
                        // Redraw string
                        for (int i = curIndex + 1; i < retString.Length; ++i)
                            Console.Write(retString[i]);
    
                        // Remove last repeated char
                        Console.Write(' ');
    
                        // Restore position
                        Console.SetCursorPosition(curLeftPos, Console.CursorTop);
    
                        // Remove string
                        retString = retString.Remove(curIndex, 1);
                    }
                }
                else if (readKeyResult.Key == ConsoleKey.RightArrow)
                {
                    if (curIndex < retString.Length)
                    {
                        ++Console.CursorLeft;
                        ++curIndex;
                    }
                }
                else if (readKeyResult.Key == ConsoleKey.LeftArrow)
                {
                    if (curIndex > 0)
                    {
                        --Console.CursorLeft;
                        --curIndex;
                    }
                }
                else if (readKeyResult.Key == ConsoleKey.Insert)
                {
                    IsInserting = !IsInserting;
                }
    #if DEBUG
                else if (readKeyResult.Key == ConsoleKey.UpArrow)
                {
                    if (Console.CursorTop > 0)
                        --Console.CursorTop;
                }
                else if (readKeyResult.Key == ConsoleKey.DownArrow)
                {
                    if (Console.CursorTop < Console.BufferHeight - 1)
                        ++Console.CursorTop;
                }
    #endif
                else
                // handle all other keypresses
                {
                    if (IsInserting || curIndex == retString.Length)
                    {
                        retString += readKeyResult.KeyChar;
                        Console.Write(readKeyResult.KeyChar);
                        ++curIndex;
                    }
                    else
                    {
                        // Store char
                        char c = readKeyResult.KeyChar;
    
                        // Write char at position
                        Console.Write(c);
    
                        // Store cursor position
                        int curLeftPos = Console.CursorLeft;
    
                        // Clear console from curIndex to end
                        for (int i = curIndex; i < retString.Length; ++i)
                            Console.Write(' ');
    
                        // Go back
                        Console.SetCursorPosition(curLeftPos, Console.CursorTop);
    
                        // Write the chars from curIndex to end (with the new appended char)
                        for (int i = curIndex; i < retString.Length; ++i)
                            Console.Write(retString[i]);
    
                        // Restore again
                        Console.SetCursorPosition(curLeftPos, Console.CursorTop);
    
                        // Store in the string
                        retString = retString.Insert(curIndex, new string(c, 1));
    
                        // Sum one to the cur index (we appended one char)
                        ++curIndex;
                    }
                }
    
                if (char.IsControl(readKeyResult.KeyChar) &&
                    readKeyResult.Key != ConsoleKey.Enter &&
                    readKeyResult.Key != ConsoleKey.Backspace &&
                    readKeyResult.Key != ConsoleKey.Tab &&
                    readKeyResult.Key != ConsoleKey.Delete &&
                    readKeyResult.Key != ConsoleKey.RightArrow &&
                    readKeyResult.Key != ConsoleKey.LeftArrow &&
                    readKeyResult.Key != ConsoleKey.Insert)
                {
    #if DEBUG
                    if (readKeyResult.Key == ConsoleKey.UpArrow || readKeyResult.Key == ConsoleKey.DownArrow)
                        continue;
    #endif
    
                    ReadKey?.Invoke(readKeyResult);
    
                    Console.WriteLine();
                    return new ConsoleOutput(readKeyResult);
                }
            }
            while (true);
        }
    }
    

    如您所见,我实现了插入、箭头控制、删除等...(插入很重要,因为如果您使用此代码编写任何文本,您将看到插入键提供的行为)。

    以及使用示例:

    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.Write("Write test string: ");
            var test = ConsoleOutput.ReadLineOrKey();
    
            if (test.IsKey())
                Console.WriteLine(test.KeyName);
            else
                Console.WriteLine($"Output string: {test.OutputString}");
            Console.Read();
        }
    }
    

    您可以保持更新on this link(是我目前正在使用的库的链接)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-24
      • 1970-01-01
      • 2013-04-11
      • 1970-01-01
      相关资源
      最近更新 更多