同时使用Readline()和ReadKey()

时间:2012-01-09 14:06:43

标签: c# console-application

有没有办法检测Readline和ReadKey,这样在大多数情况下它会表现为读取线,除了应该检测到的一些特殊键输入?

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

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

3 个答案:

答案 0 :(得分:3)

这是我刚才做的一个功能。

现在它只处理退格键,输入键和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);
    }

答案 1 :(得分:1)

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

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

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

大多数模型都是建立自己版本的ReadLine函数来增加超时(或者在某种情况下对特定字符(代码)进行特殊处理)或者使用某种线程。

这两种方式都是非平凡的或有自己的问题(请确保您查看评论,即使是已接受的答案)。

简而言之,我认为您需要根据Console.ReadKey推出自己的ReadLine版本,包括您的特殊处理以及您需要的大部分真正Console.ReadLine行为。请注意,这甚至包括诸如RETURN,ARROW KEYS,BACKSPACE处理等基本内容。

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

答案 2 :(得分:0)

针对@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(这是我目前正在使用的lib的链接)。