简体   繁体   English

同时使用 Readline() 和 ReadKey()

[英]Using Readline() and ReadKey() Simultaneously

Is there any way to detect both Readline and ReadKey, so that in most cases it behaves as a readline, except for some special key inputs that should be detected?有没有办法同时检测 Readline 和 ReadKey,以便在大多数情况下它的行为就像一个 readline,除了一些应该检测的特殊键输入?

I need some "parallel" implementation to introduce simultaneity.我需要一些“并行”实现来引入同时性。 The code below is synchronous and does not meet my need下面的代码是同步的,不符合我的需要

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
    }
}

Here's a function I just made to do this. 这是我刚才做的一个功能。

Right now it only handles backspace, enter, and Esc, but it could easily be modified to handle other keys if you deem them necessary. 现在它只处理退格,输入和Esc,但如果你认为有必要,可以很容易地修改它来处理其他键。

    // 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);
    }

No, not as such. 不,不是这样的。 Both methods block until the user enters something on the console. 两种方法都会阻塞,直到用户在控制台上输入内容。 So even if you would find a way to have both run in parallel, it will not be deterministic which one gets the first shot. 因此,即使你找到了一种让两者并行运行的方法,也不确定哪一个获得第一枪。

There is a (not obvious) similar problem: how to make Console.ReadLine() abort/break after a certain amount of time without user input. 有一个(不明显的)类似问题:如何在没有用户输入的情况下在一定时间后使Console.ReadLine()中止/中断。

There have been multiple attempts for this problem here: 这里有多次尝试解决这个问题:

Most are modelled around either creating your own version of a ReadLine function that adds a timeout (or in your case special handling for certain character (codes)) or the use some sort of threading. 大多数都建模为创建自己的ReadLine函数版本,增加超时(或在您的情况下特殊处理某些字符(代码))或使用某种线程。

Both ways are either non-trivial or have their own issues (make sure you review the comments, even for the accepted answers). 这两种方式都是非平凡的或有自己的问题(确保您审查评论,即使是已接受的答案)。

In short, I think you will need to roll your own version of ReadLine, based on Console.ReadKey with your special handling included and that much of the genuine Console.ReadLine behavior that you require. 简而言之,我认为您需要根据Console.ReadKey推出自己的ReadLine版本,包括您的特殊处理以及您需要的大部分真正的Console.ReadLine行为。 Note that this even include such basic things as RETURN, ARROW KEYS, BACKSPACE handling, etc. 请注意,这甚至包括RETURN,ARROW KEYS,BACKSPACE处理等基本功能。

Update : There is the getline.cs Code from the Mono project , which implements a line editing capability like it was provided by some venerable UNIX shells (EMACS mode, if you care). 更新Mono项目中getline.cs代码,它实现了一些行编辑功能,就像它由一些古老的UNIX shell(EMACS模式,如果你关心的话)提供的那样。 For that, I believe it will need to implement some sort of ReadLine replacement, although I haven't checked. 为此,我相信它需要实现某种ReadLine替换,尽管我还没有检查过。 Maybe you can use this as a starting point. 也许你可以用它作为起点。

In response to @Overlord Zurd , I improved the code provided by the user. 为响应@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);
    }
}

As you can see, I implemented Insert, Arrow control, Delete, etc etc... (Insert was an important thing because if you write any text with this code you will see behavior as Insert key provides). 如您所见,我实现了插入,箭头控制,删除等等...(插入是一件很重要的事情,因为如果您使用此代码编写任何文本,您将看到插入键提供的行为)。

And example of use: 和使用的例子:

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();
    }
}

You can keep updated on this link (is a link to my lib I'm currently working at). 你可以在这个链接上保持更新(这是我目前正在工作的lib的链接)。

Here is a method that I created that works great.这是我创建的一种非常有效的方法。 You do not have to press button twice in order for string to start appearing.您不必按两次按钮即可开始出现字符串。 Basically this replaces Console.ReadLine() but it also looks for Esc key pressed.基本上这取代了Console.ReadLine()但它也寻找按下的Esc键。 Just look at return type of method if it is null then you know Esc was pressed.只需查看方法的返回类型,如果它为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(); ;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我正在尝试使用ReadLine()或Read()或ReadKey来使用C#读取整数 - I'm trying to to use ReadLine(), or Read() or ReadKey to read in integers using C# 保持自托管服务堆栈服务作为docker swarm服务打开,而不使用控制台readline或readkey - Keep a self hosted servicestack service open as a docker swarm service without using console readline or readkey Console.ReadKey与带有Timer的Console.ReadLine - Console.ReadKey vs Console.ReadLine with a Timer 通过使用ReadKey()移动槽数组 - Moving trough Array by using ReadKey() 在 switch 中使用 ReadKey 终止程序 - Terminating program using ReadKey in switch 调用AttachConsole(-1)后使用Console.ReadKey()将应用程序挂在ReadKey()上 - Using Console.ReadKey() after calling AttachConsole(-1) hangs application on ReadKey() 使用控制台作为 readkey 输入和作为 serilog 日志记录的接收器 - Using the console as readkey input AND as sink for serilog logging 使用Console.ReadKey()时不清除退格键 - Backspace not erasing when using Console.ReadKey() C#-使用readkey()方法的动画问题 - C# - Problems with the animations, using readkey() method 如何让ac#控制台应用程序(同步)等待一段时间的用户输入(ReadKey / ReadLine)? - How to have a c# console application wait (synchronously) for user input (ReadKey/ReadLine) for a duration?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM