.NET 콘솔 앱에서 키 누름을 듣습니다.


224

키를 누를 때까지 (예 Esc: 눌릴 때까지) 콘솔 응용 프로그램을 계속 실행하려면 어떻게 해야합니까?

while 루프를 감싸고 있다고 가정합니다. ReadKey키 누르기를 계속 듣고 듣는 것이 아니라 작동을 차단하고 키를 요구하는 것이 마음에 들지 않습니다 .

어떻게 할 수 있습니까?

답변:


343

차단되지 않는다는 것을 알고있을 Console.KeyAvailable때만 전화 ReadKey할 수 있도록 사용하십시오 .

Console.WriteLine("Press ESC to stop");
do {
    while (! Console.KeyAvailable) {
        // Do something
   }       
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);

6
그러나 당신이 readLine () 대신에 이것을하면 "up"키를 눌러 기록 리콜을하는 멋진 기능을 잃게됩니다.
v.oddou

3
@ v.oddou 마지막 줄 이후에 간단하게 추가 Console.ReadLine()하면됩니다.
Gaffi

1
이 루프는 많은 CPU / RAM을 소비하지 않습니까? 그렇지 않다면 어떻게됩니까?
소피아

78

접근 방식을 약간 변경할 수 있습니다- Console.ReadKey()앱을 중지 하는 데 사용 하지만 백그라운드 스레드에서 작업을 수행하십시오.

static void Main(string[] args)
{
    var myWorker = new MyWorker();
    myWorker.DoStuff();
    Console.WriteLine("Press any key to stop...");
    Console.ReadKey();
}

에서 myWorker.DoStuff()기능은 다음 배경 스레드에서 다른 함수를 호출하는 것 (사용을 Action<>()또는 Func<>()그것을 할 수있는 쉬운 방법이다), 즉시 반환한다.


60

가장 짧은 방법 :

Console.WriteLine("Press ESC to stop");

while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
    // do something
}

Console.ReadKey()차단 기능이며 프로그램 실행을 중지하고 키 누름을 기다립니다. 그러나 Console.KeyAvailable먼저 확인 덕분에 while루프가 차단되지 않고 Esc를 누를 때까지 실행 됩니다.


지금까지 가장 쉬운 예입니다. 감사합니다.
joelc

18

Jason Curs ( http://www.pluralsight.com)의 C #에서 .NET 콘솔 응용 프로그램 구축 비디오 저주

여러 개의 실행중인 프로세스를 갖기 위해 다음을 수행 할 수 있습니다.

  static void Main(string[] args)
    {
        Console.CancelKeyPress += (sender, e) =>
        {

            Console.WriteLine("Exiting...");
            Environment.Exit(0);
        };

        Console.WriteLine("Press ESC to Exit");

        var taskKeys = new Task(ReadKeys);
        var taskProcessFiles = new Task(ProcessFiles);

        taskKeys.Start();
        taskProcessFiles.Start();

        var tasks = new[] { taskKeys };
        Task.WaitAll(tasks);
    }

    private static void ProcessFiles()
    {
        var files = Enumerable.Range(1, 100).Select(n => "File" + n + ".txt");

        var taskBusy = new Task(BusyIndicator);
        taskBusy.Start();

        foreach (var file in files)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Procesing file {0}", file);
        }
    }

    private static void BusyIndicator()
    {
        var busy = new ConsoleBusyIndicator();
        busy.UpdateProgress();
    }

    private static void ReadKeys()
    {
        ConsoleKeyInfo key = new ConsoleKeyInfo();

        while (!Console.KeyAvailable && key.Key != ConsoleKey.Escape)
        {

            key = Console.ReadKey(true);

            switch (key.Key)
            {
                case ConsoleKey.UpArrow:
                    Console.WriteLine("UpArrow was pressed");
                    break;
                case ConsoleKey.DownArrow:
                    Console.WriteLine("DownArrow was pressed");
                    break;

                case ConsoleKey.RightArrow:
                    Console.WriteLine("RightArrow was pressed");
                    break;

                case ConsoleKey.LeftArrow:
                    Console.WriteLine("LeftArrow was pressed");
                    break;

                case ConsoleKey.Escape:
                    break;

                default:
                    if (Console.CapsLock && Console.NumberLock)
                    {
                        Console.WriteLine(key.KeyChar);
                    }
                    break;
            }
        }
    }
}

internal class ConsoleBusyIndicator
{
    int _currentBusySymbol;

    public char[] BusySymbols { get; set; }

    public ConsoleBusyIndicator()
    {
        BusySymbols = new[] { '|', '/', '-', '\\' };
    }
    public void UpdateProgress()
    {
        while (true)
        {
            Thread.Sleep(100);
            var originalX = Console.CursorLeft;
            var originalY = Console.CursorTop;

            Console.Write(BusySymbols[_currentBusySymbol]);

            _currentBusySymbol++;

            if (_currentBusySymbol == BusySymbols.Length)
            {
                _currentBusySymbol = 0;
            }

            Console.SetCursorPosition(originalX, originalY);
        }
    }

2
내 콘솔 응용 프로그램으로 동일한 작업을 수행하려고 시도한 방법에 대해 약간 설명 할 수 있습니까? 코드에 대한 설명은 훌륭합니다.
nayef harb

@nayefharb는 많은 작업을 설정하고 시작하며 WaitAll은 모두 돌아올 때까지 기다립니다. 따라서 개별 작업이 반환되지 않으며 CancelKeyPress가 트리거 될 때까지 계속 진행됩니다.
wonea

Console.CursorVisible = false;커서 깜박임을 피하는 것이 가장 좋습니다. 이 줄을 static void Main(string[] args)블록의 첫 줄로 추가하십시오 .
Hitesh

12

다음은 다른 스레드에서 무언가를 수행하고 다른 스레드에서 누른 키의 청취를 시작하는 방법입니다. 실제 프로세스가 종료되거나 사용자가 Esc키 를 눌러 프로세스를 종료하면 콘솔이 처리를 중지합니다 .

class SplitAnalyser
{
    public static bool stopProcessor = false;
    public static bool Terminate = false;

    static void Main(string[] args)
    {
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("Split Analyser starts");
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Press Esc to quit.....");
        Thread MainThread = new Thread(new ThreadStart(startProcess));
        Thread ConsoleKeyListener = new Thread(new ThreadStart(ListerKeyBoardEvent));
        MainThread.Name = "Processor";
        ConsoleKeyListener.Name = "KeyListener";
        MainThread.Start();
        ConsoleKeyListener.Start();

        while (true) 
        {
            if (Terminate)
            {
                Console.WriteLine("Terminating Process...");
                MainThread.Abort();
                ConsoleKeyListener.Abort();
                Thread.Sleep(2000);
                Thread.CurrentThread.Abort();
                return;
            }

            if (stopProcessor)
            {
                Console.WriteLine("Ending Process...");
                MainThread.Abort();
                ConsoleKeyListener.Abort();
                Thread.Sleep(2000);
                Thread.CurrentThread.Abort();
                return;
            }
        } 
    }

    public static void ListerKeyBoardEvent()
    {
        do
        {
            if (Console.ReadKey(true).Key == ConsoleKey.Escape)
            {
                Terminate = true;
            }
        } while (true); 
    }

    public static void startProcess()
    {
        int i = 0;
        while (true)
        {
            if (!stopProcessor && !Terminate)
            {
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Processing...." + i++);
                Thread.Sleep(3000);
            }
            if(i==10)
                stopProcessor = true;

        }
    }

}

5

Visual Studio를 사용하는 경우 디버그 메뉴에서 "디버깅없이 시작"을 사용할 수 있습니다.

"계속하려면 아무 키나 누르십시오."라고 자동으로 쓰여집니다. 응용 프로그램이 완료되면 콘솔에 표시되며 키를 누를 때까지 콘솔이 열린 상태로 유지됩니다.


3

다른 답변 중 일부가 잘 처리되지 않는 경우를 해결합니다.

  • 응답 : 키 누르기 처리 코드의 직접 실행; 폴링 지연 또는 지연 지연 방지
  • 선택 사항 : 전역 키 누르기가 선택되어 있습니다 . 그렇지 않으면 앱이 정상적으로 종료되어야합니다
  • 우려의 분리 : 덜 침습적 인 청취 코드; 일반 콘솔 앱 코드와 독립적으로 작동합니다.

이 페이지의 많은 솔루션에서에 대한 폴링 Console.KeyAvailable또는 차단 이 포함 되어 Console.ReadKey있습니다. Console여기 에서 .NET 이 매우 협조적이지 않다는 것은 사실이지만 Task.Run,보다 현대적인 Async듣기 모드로 전환 하는 데 사용할 수 있습니다 .

알아야 할 주요 문제는 기본적으로 콘솔 스레드가 Async작동하도록 설정되어 있지 않다는 것입니다. 즉, main함수 의 하단에서 떨어질 때 Async완료를 기다리지 않고 AppDoman 및 프로세스가 종료됨을 의미합니다. . 이를 해결하는 적절한 방법은 Stephen Cleary의 AsyncContext 를 사용 Async하여 단일 스레드 콘솔 프로그램을 완전히 지원하는 것입니다. 그러나 키 누름 대기와 같은 간단한 경우에는 전체 트램폴린을 설치하는 것이 과도 할 수 있습니다.

아래 예제는 일종의 반복 배치 파일에 사용되는 콘솔 프로그램입니다. 이 경우 프로그램이 작업으로 완료되면 일반적으로 키를 누르지 않아도 종료 한 다음 선택적 키 누르기를 허용 하여 앱이 종료 되지 않도록 합니다. 사이클을 일시 중지하여 사물을 검토하거나 재개하거나 일시 중지를 알려진 '제어점'으로 사용하여 배치 파일을 완전히 중단 할 수 있습니다.

static void Main(String[] args)
{
    Console.WriteLine("Press any key to prevent exit...");
    var tHold = Task.Run(() => Console.ReadKey(true));

    // ... do your console app activity ...

    if (tHold.IsCompleted)
    {
#if false   // For the 'hold' state, you can simply halt forever...
        Console.WriteLine("Holding.");
        Thread.Sleep(Timeout.Infinite);
#else                            // ...or allow continuing to exit
        while (Console.KeyAvailable)
            Console.ReadKey(true);     // flush/consume any extras
        Console.WriteLine("Holding. Press 'Esc' to exit.");
        while (Console.ReadKey(true).Key != ConsoleKey.Escape)
            ;
#endif
    }
}

1

아래 코드를 사용하면 콘솔 실행 중에 스페이스 바를 누른 상태에서 다른 키를 누를 때까지 일시 중지하고 주 루프를 끊을 때 EscapeKey를 청취 할 수 있습니다

static ConsoleKeyInfo cki = new ConsoleKeyInfo();


while(true) {
      if (WaitOrBreak()) break;
      //your main code
}

 private static bool WaitOrBreak(){
        if (Console.KeyAvailable) cki = Console.ReadKey(true);
        if (cki.Key == ConsoleKey.Spacebar)
        {
            Console.Write("waiting..");
            while (Console.KeyAvailable == false)
            {
                Thread.Sleep(250);Console.Write(".");
            }
            Console.WriteLine();
            Console.ReadKey(true);
            cki = new ConsoleKeyInfo();
        }
        if (cki.Key == ConsoleKey.Escape) return true;
        return false;
    }

-1

내 경험에 따르면 콘솔 응용 프로그램에서 마지막으로 누른 키를 읽는 가장 쉬운 방법은 다음과 같습니다 (화살표 키가있는 예).

ConsoleKey readKey = Console.ReadKey ().Key;
if (readKey == ConsoleKey.LeftArrow) {
    <Method1> ();  //Do something
} else if (readKey == ConsoleKey.RightArrow) {
    <Method2> ();  //Do something
}

루프를 피하기 위해 대신 메서드 내에서 위의 코드를 작성하고 "Method1"과 "Method2"의 끝에서 호출합니다. 따라서 "Method1"또는 "Method2", Console.ReadKey ()를 실행 한 후 키가 키를 다시 읽을 준비가되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.