2017-10-09 1 views
8

Ich schreibe eine .NET Core-Konsolenanwendung. Ich wollte die Konsoleneingabe auf eine bestimmte Anzahl von Maximalzeichen für jeden Eingang beschränken. Ich habe etwas Code, der dies tut, indem Sie eine Zeichenfolge mit Console.ReadKey() anstelle von Console.ReadLine() erstellen Alles funktionierte perfekt testen auf Windows. Als ich dann auf einem Raspberry Pi 3 mit Raspbian entfaltete, stieß ich schnell auf alle möglichen Probleme. Ich erinnerte mich, dass Linux Zeilenenden anders als Windows behandelt, und es scheint, Backspaces werden auch anders behandelt. Ich habe die Art und Weise geändert, wie ich diese gehandhabt habe, indem ich den ConsoleKey anstelle des Zeichens ausgeschaltet habe, und das Newline-Problem ist verschwunden, aber Backspaces werden nur manchmal registriert. Manchmal werden Zeichen auch außerhalb meines Eingabefelds an die Konsole ausgegeben, obwohl ich den ReadKey so eingestellt habe, dass er nicht selbst auf die Konsole ausgegeben wird. Fehle ich etwas darüber, wie Linux Konsoleneingaben handhabt?Warum behandelt .NET Core ReadKey auf Raspbian anders?

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

Es ist das * Terminal *, das Tastenanschläge verarbeitet. Sie erhalten keine Tastenanschläge, die nicht an Ihre Anwendung gesendet werden. Newlines haben damit nichts zu tun. .NET (und Core) verwendet die Einstellungen des Betriebssystems. Außerdem erkennt es bereits '\ n' als Zeilenumbruch in Windows –

+0

Windows erkennt Wagenrücklaufzeichen,' \ r', während Linux nur Zeilenvorschubzeichen '\ n' verwendet. Ich habe ursprünglich nach '\ 'gesucht, was natürlich zu Problemen führte. Ich versuchte herauszufinden, ob es andere Unterschiede gab, für die ich nicht verantwortlich bin. Ich dachte, es wurde impliziert, dass dies mit Unterschieden in der Art und Weise, wie das Terminal Tastenanschläge handhabt, zu tun hat. Mein Punkt war nur, dass Aufrufe von ReadKey unterschiedliche Ergebnisse auf verschiedenen Rechnern ergeben, selbst wenn ich genau die gleichen Tastenanschläge mache, ohne Zweifel daran, wie die verschiedenen Systeme diese Tastenanschläge verarbeiten. – tyjkenn

+4

Nicht so sicher, wie das alles Sinn macht. Unter Linux drücken Sie weiterhin die Eingabetaste, Sie drücken nicht Strg + J, um \ n zu erhalten. ReadKey informiert Sie über die Taste, die gedrückt wurde, und nicht, um welches Zeichen es sich handelt. Solange Sie also Key und nicht KeyChar verwenden, sollte es kein Problem geben. Vielleicht hast du ein Kompatibilitätsproblem aufgedeckt, das ist alles ziemlich neu, also ist es nicht undenkbar. Und sie unterstützen etwa zehn verschiedene Linux-Varianten und Raspian gehört nicht dazu. Am besten, um ihnen davon zu erzählen, verwenden Sie den Button [New Issue] (https://github.com/dotnet/coreclr/issues). –

Antwort

2

ich glaube, die grundlegende Frage von Stephen Toub Kommentar in this GitHub issue ausgesetzt:

Sie sein kann Wenn wir daran denken, dass wir das Echo nur während eines ReadKey (intercept: true) -Aufrufs deaktivieren, wird der Schlüssel in einem Wettlauf zwischen dem Benutzer, der tippt und ReadKey (intercept: true) aufruft, auch dann noch echo, wenn Sie gehofft haben es wäre nicht, aber Sie werden den Tastenanschlag nicht verlieren.

Das ist kalt Komfort, aber genau. Dies ist ein Rennen, das sehr schwer zu gewinnen ist. Das Kernproblem ist, dass ein Linux-Terminal anders als die Windows-Konsole funktioniert. Es funktioniert viel mehr wie ein Fernschreiber in den 1970er Jahren.Du hast auf der Tastatur herumgeknallt, egal, ob der Computer dem, was du tippst, irgendwelche Aufmerksamkeit schenkte, der Fernschreiber gab nur wieder, was du tipptest, wenn du ihn auf Papier fällst. Bis Sie die Eingabetaste drücken, begann der Computer an dem Text zu starten.

Ganz anders als die Windows-Konsole, erfordert das Programm einen aktiven Leseaufruf, um jeden getippten Text zu echotieren.

Das ist also eine ziemlich grundlegende Diskrepanz mit der Konsole api. Es benötigt eine Echo Eigenschaft, um Ihnen die Hoffnung zu geben, dies richtig zu machen. Sie können es also auf false setzen, bevor Sie anfangen, Eingaben zu akzeptieren und sich um das Echo selbst zu kümmern. Es ist immer noch ein Wettrennen, aber zumindest hast du eine Chance, jeden vorgetippten Text zu löschen.

Die einzige halbwegs vernünftige Lösung, die Sie jetzt haben, ist disable echo, bevor Sie Ihr Programm starten. Sie müssen alle Eingaben mit Ihrer Methode machen.

+0

Zum Glück für mich ist es völlig in Ordnung, alle meine Eingaben über diese Methode laufen zu lassen, daher würde ich diese Problemumgehung ein wenig mehr als "halb-anständig" betrachten. Vielen Dank! – tyjkenn

2

I getestet und herausgefunden, dass Console.ReadKey(true) in der Tat einige Fehler hat, wo der Schlüssel tatsächlich an die Konsole Echo wird, wenn die Eingabe gefeuert schnell oder wiederholen Schlüssel ist. Das ist etwas, was Sie nicht erwarten, aber warum es passiert, habe ich keine Ahnung.

Wenn Sie in Debuggen es interessiert sind, können Sie unter den unten genannten Quellcode schauen

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

ich eine Abhilfe für das Problem zu setzen gewählt haben. Es gibt also wenige Probleme in Ihrem Ansatz. Die Schlüssel Left Arrow und Right Arrow sollten behandelt werden oder sie sollten nicht erlaubt sein. Ich wählte die später eine von unten Code hinzufügen

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

Wenn Sie die Zeichen mit unter

Console.Write(str + padding); 

Sie im Grunde auch die Cursorposition stören, die nicht korrekt ist. So müssen Sie die Cursor-Position nach diesem einzustellen Verwendung unter

Console.CursorLeft = str.Length; 

Jetzt kommt der Teil der undichten Tasten der Handhabung, die wahrscheinlich eine .NET-Fehler ist, habe ich unten Code

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

So überprüfen wir Aus irgendeinem unsichtbaren Grund wurde etwas erwidert, dann löschte es. Dann getestet Stress es

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

Unten ist der endgültige Code, die ich verwendet habe

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

Schlechte Verbindung, Sie benötigen den Unix-spezifischen Geschmack von Console in .NETCore. Ich denke, es ist [diese] (https://github.com/dotnet/corefx/blob/master/src/System.Console/src/System/ConsolePal.Unix.cs). Sieht ziemlich unschuldig aus. Vielleicht zu unschuldig :) –

+0

Danke für die Korrektur @HansPassant, ich erkannte nicht, müssen auf die .NET-Core-Codebasis zu schauen –

+0

Nachdem ich mit diesem Code herumspielen, gelang es mir, diesen Ansatz zu arbeiten. In einem anderen Teil meines Programms, in dem ich Tastatureingaben auf andere Weise benutze (um durch Seiten zu blättern), konnte ich einige der Schlüssel nicht fangen, während die gesamte Konsole neu gezeichnet wurde. Letztendlich musste ich das Echo deaktivieren. – tyjkenn

Verwandte Themen