2017-10-09 56 views
8

Piszę aplikację konsoli .NET Core. Chciałem ograniczyć wejście konsoli do pewnej liczby znaków maksymalnych dla każdego wejścia. Mam kod, który to robi, budując ciąg z Console.ReadKey() zamiast Console.ReadLine() Wszystko działało doskonale testowanie go w systemie Windows. Następnie, kiedy wdrożyłem Raspberry Pi 3 z Raspbian, szybko napotkałem różnego rodzaju problemy. Przypomniałem sobie, że Linux obsługuje końcówki linii inaczej niż Windows, i wygląda na to, że backspace są również obsługiwane inaczej. Zmieniłem sposób, w jaki sobie z nimi poradziłem, wychodząc z Konsolowego klucza zamiast postaci, a problem z nową linią zniknął, ale cofają się tylko czasy. Ponadto, czasami znaki są wysyłane do konsoli poza polem wprowadzania, mimo że ustawiam ReadKey, aby nie był wyprowadzany na konsolę samodzielnie. Czy brakuje mi czegoś o tym, jak Linux obsługuje wejście konsoli?Dlaczego .NET Core traktuje ReadKey inaczej na Raspbian?

//I replaced my calls to Console.ReadLine() with this. The limit is the 
//max number of characters that can be entered in the console. 
public static string ReadChars(int limit) 
{ 
    string str = string.Empty; //all the input so far 
    int left = Console.CursorLeft; //store cursor position for re-outputting 
    int top = Console.CursorTop; 
    while (true) //keep checking for key events 
    { 
     if (Console.KeyAvailable) 
     { 
      //true to intercept input and not output to console 
      //normally. This sometimes fails and outputs anyway. 
      ConsoleKeyInfo c = Console.ReadKey(true); 
      if (c.Key == ConsoleKey.Enter) //stop input on Enter key 
       break; 
      if (c.Key == ConsoleKey.Backspace) //remove last char on Backspace 
      { 
       if (str != "") 
       { 
        tr = str.Substring(0, str.Length - 1); 
       } 
      } 
      else if (c.Key != ConsoleKey.Tab && str.Length < limit) 
      { 
       //don't allow tabs or exceeding the max size 
       str += c.KeyChar; 
      } 
      else 
      { 
       //ignore tabs and when the limit is exceeded 
       continue; 
      } 
      Console.SetCursorPosition(left, top); 
      string padding = ""; //padding clears unused chars in field 
      for (int i = 0; i < limit - str.Length; i++) 
      { 
       padding += " "; 
      } 
      //output this way instead 
      Console.Write(str + padding); 
     } 
    } 
    return str; 
} 
+0

Jest to * terminal *, który obsługuje naciśnięcia klawiszy. Nie dostaniesz żadnych klawiszy, które nie zostaną wysłane do Twojej aplikacji. Newlines nie mają z tym nic wspólnego. .NET (i Core) użyje ustawień systemu operacyjnego. Poza tym już rozpoznaje '\ n' jako znak nowej linii w Windows –

+0

Windows rozpoznaje znaki powrotu karetki,' \ r', podczas gdy Linux po prostu używa znaków nowej linii, '\ n'. Początkowo sprawdzałem "\ r", co oczywiście doprowadziło do problemów. Próbowałem dowiedzieć się, czy nie istnieją inne różnice, o których nie mówię. Pomyślałem, że sugeruje, że musi to wynikać z różnic w sposobie obsługi klawiszy przez terminal. Chodzi mi tylko o to, że połączenia z ReadKey dają różne wyniki na różnych komputerach, nawet jeśli wykonuję dokładnie te same naciśnięcia klawiszy, nie ma wątpliwości z powodu sposobu, w jaki różne systemy przetwarzają te klawisze. – tyjkenn

+4

Nie jestem pewien, jak to wszystko ma sens. W systemie Linux nadal naciskasz klawisz Enter, nie naciskaj klawisza Ctrl + J, aby uzyskać \ n. ReadKey powie ci o naciśniętym klawiszu, a nie o tym, jaki ma postać. Dopóki używasz Key, a nie KeyChar, nie powinno być problemu. Być może naraziłeś się na problem kompatybilności, to wszystko jest całkiem nowe, więc nie jest to nie do pomyślenia. Obsługują około dziesięciu różnych smaków Linuksa, a Raspian nie jest jednym z nich. Najlepiej im o tym powiedzieć, użyj przycisku [New Issue] (https://github.com/dotnet/coreclr/issues). –

Odpowiedz

2

Myślę, że podstawową kwestią jest narażona wypowiedź Stephena Toub w this GitHub issue:

może być myśląc o tym, że teraz wyłączamy tylko echo podczas wywołania ReadKey (przechwycić: true), więc w wyścigu między wpisaniem użytkownika a wywołaniem ReadKey (przechwycenie: true), klucz może być słyszalny nawet wtedy, gdy masz nadzieję nie byłoby, ale nie stracisz naciśnięcia klawisza.

Który jest zimny komfort, ale dokładne. To wyścig, który jest bardzo trudny do wygrania. Podstawowym problemem jest to, że terminal linuxowy działa zupełnie inaczej niż konsola systemu Windows. Działa znacznie bardziej jak teletechnikę z lat 70-tych.Uderzyłeś w klawiaturę, bez względu na to, czy komputer zwracał uwagę na to, co piszesz, telegraficznie po prostu echo tego, co wpisałeś na klawiaturze. Dopóki nie naciśniesz klawisza Enter, komputer zacznie wykręcać tekst.

Bardzo odmienna od konsoli Windows, wymaga aktywnego wywołania programu, aby wywołać echo dowolnego wpisanego tekstu.

Jest to dość podstawowe niedopasowanie z api konsoli. Potrzebna jest właściwość Echo, aby dać ci nadzieję na robienie tego poprawnie. Możesz więc ustawić go na false, zanim zaczniesz akceptować dane wejściowe i zadbać o samo echo. To wciąż wyścig, ale przynajmniej masz szansę na wyczyszczenie dowolnego wcześniej napisanego tekstu.

Jedynym przyzwoitym rozwiązaniem, jakie teraz masz, jest disable echo przed uruchomieniem programu. Wymaganie, aby wykonać wszystkie dane wejściowe za pomocą swojej metody.

+0

Na szczęście dla mnie uruchamianie wszystkich moich danych wejściowych za pomocą tej metody jest całkowicie w porządku, dlatego uważam to obejście za nieco bardziej "przyzwoite". Dzięki! – tyjkenn

2

I przetestowane i okazało się, że rzeczywiście ma pewne Console.ReadKey(true) błędów gdzie klucz faktycznie dostaje echo do konsoli, gdy jest szybkie wpisywanie lub powtórzyć klawisze są wypalane. To jest coś, czego się nie spodziewasz, ale dlaczego tak się dzieje, nie mam pojęcia.

Jeśli jesteś zainteresowany debugowanie go, można spojrzeć na poniższy kod źródłowy

https://referencesource.microsoft.com/#mscorlib/system/console.cs,1476

wybrałem położyć obejście problemu. W twoim podejściu jest niewiele problemów. Klucze Left Arrow i Right Arrow powinny być obsługiwane lub nie powinny być dozwolone. Wybrałem później dodając poniżej kod

if (c.Key == ConsoleKey.LeftArrow || c.Key == ConsoleKey.RightArrow) { 
    continue; 
} 

Podczas wpisywania znaków używając poniższego

Console.Write(str + padding); 

Zasadniczo przeszkadzać pozycję kursora, który także nie jest poprawna. Więc trzeba ustawić położenie kursora po tym za pomocą poniższego

Console.CursorLeft = str.Length; 

Teraz zaczyna się od manipulowania nieszczelne klawisze, które jest chyba bug .NET, dodałem poniżej kod

else 
{ 
    //ignore tabs and when the ilimit is exceeded 
    if (Console.CursorLeft > str.Length){ 

     var delta = Console.CursorLeft - str.Length; 
     Console.CursorLeft = str.Length; 
     Console.Write(new String(' ',delta)); 
     Console.CursorLeft = str.Length; 
    } 
    continue; 
} 

więc możemy sprawdzić dla każdego niewidocznego powodu coś zostało powtórzone, a następnie wymazać. Następnie stres przetestowane

$ docker run -it console 
Please enter some text: 
tarun6686e 
You entered: tarun6686e 

Poniżej jest ostateczny kod, który użył

using System; 

namespace ConsoleTest 
{ 
    public class Program { 
     public static string tr=""; 
     //I replaced my calls to Console.ReadLine() with this. The limit is the 
     //max number of characters that can be entered in the console. 
     public static string ReadChars(int limit) 
     { 
      string str = string.Empty; //all the input so far 
      int left = Console.CursorLeft; //store cursor position for re-outputting 
      int top = Console.CursorTop; 

      while (true) //keep checking for key events 
      { 
       if (Console.KeyAvailable) 
       { 
        //true to intercept input and not output to console 
        //normally. This sometimes fails and outputs anyway. 
        ConsoleKeyInfo c = Console.ReadKey(true); 
        string name = Enum.GetName(typeof(ConsoleKey), c.Key); 
        var key = c.KeyChar; 
        // Console.WriteLine(String.Format("Name={0}, Key={1}, KeyAscii={2}", name, key,(int)key)); 
        if (c.Key == ConsoleKey.Enter) //stop input on Enter key 
         { 
          Console.WriteLine(); 
          break; 
         } 
        if (c.Key == ConsoleKey.LeftArrow || c.Key == ConsoleKey.RightArrow) { 
         continue; 
        } 

        if (c.Key == ConsoleKey.Backspace) //remove last char on Backspace 
        { 
         if (str != "") 
         { 
          str = str.Substring(0, str.Length - 1); 
         } 
        } 
        else if (c.Key != ConsoleKey.Tab && str.Length < limit) 
        { 
         //don't allow tabs or exceeding the max size 
         str += c.KeyChar; 
        } 
        else 
        { 
         //ignore tabs and when the ilimit is exceeded 
         if (Console.CursorLeft > str.Length){ 

          var delta = Console.CursorLeft - str.Length; 
          Console.CursorLeft = str.Length; 
          Console.Write(new String(' ',delta)); 
          Console.CursorLeft = str.Length; 
         } 
         continue; 
        } 
        Console.SetCursorPosition(left, top); 
        string padding = ""; //padding clears unused chars in field 
        for (int i = 0; i < limit - str.Length; i++) 
        { 
         padding += " "; 
        } 
        //output this way instead 
        Console.Write(str + padding); 
        Console.CursorLeft = str.Length; 
       } 
      } 
      return str; 
     } 

     public static void Main(string[] args) { 
      Console.WriteLine("Please enter some text: "); 
      var text = ReadChars(10); 

      Console.WriteLine("You entered: " + text); 
     } 
    } 
} 
+0

Zły link, potrzebujesz specyficznego dla Unixa smaku konsoli w .NETCore. Myślę, że to [ten] (https://github.com/dotnet/corefx/blob/master/src/System.Console/src/System/ConsolePal.Unix.cs). Wygląda całkiem niewinnie. Być może zbyt niewinne :) –

+0

Dzięki za poprawkę @HansPassant, nie zdawałem sobie sprawy, że muszę spojrzeć na .NET core code –

+0

Po tym, jak z tym kodem poradziłem sobie, udało mi się uzyskać to podejście do pracy. Jednak w innej części mojego programu, w której korzystam z klawiatury w inny sposób (aby przeglądać strony), nie mogłem uchwycić niektórych klawiszy, podczas gdy cała konsola była ponownie rysowana. Ostatecznie musiałem wyłączyć echo. – tyjkenn