Laden...

Befehlszeile (einzelne Chars) mit spezieller Syntax auswerten

Letzter Beitrag vor 11 Jahren 11 Posts 2.601 Views
Befehlszeile (einzelne Chars) mit spezieller Syntax auswerten

Hallo,

ich scheitere im moment an folgender Problemstellung:

es kommen über eine spezielle Tastatur mit vielen Schnelltasten chars rein die ausgewertet werden müßen.
Jeder char kann endweder eine feststehende Bedeutung über die Schnelltasten(jeweils ein Char) haben:
8 Subjekte(zB. Disk, Folder,...), 10 Instructions (z.B. benenne, erstelle,...), 4 Präpostionen (z.B. vor, nach), Andere...
oder aber einfach nur ein Char (abc...usw.) für die Benennung eines Subjekts (z.B. benenne Folder "freier FolderName" vor Folder "vergebener FolderName") oder die Eingabe eines Wertes. Die feststehenden Bedeutungen können nicht mit einzelnen Chars zusammengesetzt werden.
Bei jedem Char Eingang muß ich prüfen, ob der Char im Zusammenhang gültig ist, und wenn nicht einen Hinweis ausgeben und den Char verweigern.
Der Aufbau der Befehlszeilen entspricht meistens folgendem Aufbau: Instruction -> Subjekt -> "Name" -> Präpostion -> "Wert" ,
wobei halt nicht jede Instruction mit jedem Subjekt möglich ist oder bei einer bestimmten Konstellation von Instruction und Subjekt nicht jede Präpostionen erlaubt ist usw.
Jede Befehlszeile wird am Ende mit einem Char "Ausführen" vom Bediener abgeschloßen, um den entsprechenden Befehl auszuführen.

Ich habe versucht das mit if, else, switch, break, return usw. zu bewerkstelligen und gehofft solange den Überblick zu behalten bis alle möglichen Fälle erschlagen sind, aber das war wohl nichts.

Jetzt bin ich schon länger auf der Suche nach einem neuen Lösungsansatz.
Wäre da das State Pattern eine Möglichkeit? Da müßte ich doch für jeden möglichen Zustand der Befehlszeile eine eigene Klasse erstellen (dürften ziemlich viele sein), oder?
Kann man soetwas mir RegEx, Linq, Lambda oder ähnlichem bewerkstelligen?

Ich hänge da jetzt schon mehrere Tage dran und wäre wirklich für jeden hinweis sehr dankbar.
(auch, wenn wohl nicht so wichtig, ich arbeite mit dem Compact Framework 3.5 )

Gruß luciluke

Hallo LuciLuke,

mit Regex könntest du das sicherlich gut abfragen und gleich zum Auswerten aufbereiten:


(?<Instruktion>benenne|erstelle)\s+(?<Subjekt>Disk|Folder)\s+(?<Name>(?:.(?!v))+)\s+(?<Preposition>vor|nach)\s(?<Wert>.*)

Die Gruppen sind benannt und haben die von dir gewünschte Reihenfolge.

Testen kannst du Regex Ausdrücke ausgezeichnet mit dem Tool hier: On-the-fly Regex-Tester: Regex-Lab

In C# kannst du dann ganz einfach auf die entsprechenden Gruppen der Reihe nach zugreifen und auswerten.

Lg, XXX

Hallo luciluke,

man braucht nicht gleich den State-Pattern mit einzelnen Klassen für jeden Zustand, um einen Zustandsautomaten zu implementieren. Für die Zustände reicht meistens ein Enum, für die Zustandsübergangsfunktion ein Dictionary<Key, NextState>, wobei Key ein zusammengesetzter Wert aus CurrentState und CurrentInput ist.

Ansonsten kannst du - quasi am oberen Ende der Skala - auch einen Parsergenerator. Oder - quasi am unteren oberen Ende der Skala - Regex verwenden, wobei Regex für Befehlzeilen mit einem sehr schematischen Aufbau sicher eine einfache und gute Lösung ist. Einige semantische Abhängigkeiten, kann man schon in der Pattern einbauen, aber einige wird man sinnvollerweise nachher prüfen.

herbivore

Wäre da das State Pattern eine Möglichkeit? Da müßte ich doch für jeden möglichen Zustand der Befehlszeile eine eigene Klasse erstellen (dürften ziemlich viele sein), oder?

Ja und?
Mit Vererbung und dem richtigen Ansatz schreibst du wahrscheinlich weniger Code als jetzt, der zudem noch übersichtlicher und dadurch auch Fehlerfreier und leichter zu warten ist.

nochmal Hallo,

und erstmal vielen Dank für die schnellen und hilfreichen Reaktionen, aufgrund derer ich mir jetzt nocheinmal 2 Stunden die Parser Generatoren angeschaut habe (hatte mich die letzten Tage schon einmal damit beschäftigt), jedoch wenn ich das richtig verstehe benötigen diese immer eine Einbindung Ihrer speziellen laufzeit.dll und selbst wenn diese in c# geschrieben sind müßen sie nicht unbedingt CF 3.5 kompatiebel sein.
Wobei z.B Coco/R wohl nur 2 Code.cs Dateien erstellt, die dann aber ja auch nicht unbedingt CF 3.5 kompatiebel sein müßen.
Ist meine Einschätzung zu den Parser Generatoren richtig oder habe ich die vollkommen falsch verstanden?
Wenn ich die Benutzung von Parser Generatoren letztendlich ausschließen könnte, wäre mein weiteres Vorgehen ja eindeutig ein vereinfachtes State Pattern oder Regex bzw. eine Mischung aus beiden.

Mit Vererbung und dem richtigen Ansatz...

Ich habe leider noch nie ein State Pattern implementiert, sodass das mit dem richtigen Ansatz nicht so leicht ist, vorallem da man im Netz unzählige Varianten der Impementierung findet.

Gruß luciluke

Hallo luciluke,

was eine Parsergenerator als Ausgabe erzeugt und ob einer eine Laufzeitbibliothek benötigt wird, kann man nicht pauschal sagen. Das hängt von dem konkreten Generator ab. Ich gehe davon aus, dass man welche findet, die fürs CF 3.5 geeignet sind und ohne Laufzeitbibliothek auskommen. Garantieren kann ich das aber nicht. Allerdings ist für deinen eher einfachen Fall ein Parsergenerator eher übertrieben (das meinte ich mit oberem Ende der Skala).

Ob es nun ein State-Pattern mit einer Klasse pro Zustand sein muss, oder ob man mit einem einfacher Implementierten Zustandsautomaten nicht besser fährt, habe ich ja schon oben in Frage gestellt.

herbivore

und ein weiteres Hallo,

nach längerer Beschäftigung, mit dem gesamt Konzept des Projekts, bin ich nun wieder bei der Befehlszeilenauswertung gelandet, wobei sich dieser Teil der Anwendung nun auf einem XP Rechner abspielt, sodaß ich nicht mehr auf CF beschrängt bin.
Ich habe das State Pattern mal anhand von State Pattern in C# in etwas abgeänderter Form implementiert, aber da ich den Grund Aufbau der Implementation (Reflection usw.) nicht komplett verstehe würde ich lieber etwas anderes machen (obwohl es halt funktioniert und das mit den Enums wirklich praktisch ist).

Für die Zustände reicht meistens ein Enum, für die Zustandsübergangsfunktion ein Dictionary<Key, NextState>, wobei Key ein zusammengesetzter Wert aus CurrentState und CurrentInput ist.

Ich wüße nicht wo ich da den State abhänigen Code unterbringen müßte. Im Moment habe ich ja in jeder State Klasse eine Methode die prüft ob der Input im Zusammenhang gültig ist.
Wenn ich dazu ein wenig schematischen Code als Hilfestellung bekommen könnte, wäre ich sehr dankbar. Es sei denn die genannte Implementierung von CodeProjekt ist eine gute Wahl, dann würde ich halt schauen dass ich Sie komplett verstehe...

Gruß luciluke

Hallo luciluke,

Ich wüße nicht wo ich da den State abhänigen Code unterbringen müßte.

dafür könntest du ein zweites Dictionary <State, Action<T>> verwenden.

herbivore

geht das so in die richtige Richtung?
Ich muß für jede mögliche Kombination einen Enumwert KB_State haben, eine Methode schreiben und einer Action<T> zuweisen, diese zusammen in ein Dictionary packen und das Dictionary für den Übergang erstellen.


enum Input { Char, Subject, Nothing }// enum KB_States ...
Input _currentInput;
KB_States _currentState = KB_States.KB_Empty;
Action<short> CheckCharAction;
Action<short> CheckSubjektAction;
Dictionary<KB_States, Action<short>> _dicStateAction;
Dictionary<KeyValuePair<KB_States, Input>, KB_States> _dicUebergang;
void DataArrived(short kbData)
{
    //würde dann natürlich woanders stehen...
    _dicStateAction = new Dictionary<KB_States, Action<short>>();
    _dicUebergang = new Dictionary<KeyValuePair<KB_States, Input>,KB_States>();
    CheckCharAction = CheckChars;
    CheckSubjektAction = CheckSubjekt;
    _dicUebergang.Add(new KeyValuePair<KB_States, Input>(KB_States.KB_Empty, Input.Subject), KB_States.KB_Sub1);
    _dicUebergang.Add(new KeyValuePair<KB_States, Input>(KB_States.KB_Empty, Input.Char), KB_States.KB_DirektBySpace);
    _dicStateAction.Add(KB_States.KB_DirektBySpace, CheckCharAction);
    _dicStateAction.Add(KB_States.KB_Sub1, CheckSubjektAction);
    //-------

    _currentInput = GetCurrentInput(kbData);
    _currentState = _dicUebergang[new KeyValuePair<KB_States, Input>(_currentState, _currentInput)];
    _dicStateAction[_currentState](kbData);//methode
}

private Input GetCurrentInput(short kbData)
{
    if (IsKBDataChar(kbData))//irgendein Vergleich
    {
        return Input.Char;
    }
    if (IsKBDataSubject(kbData))//irgendein Vergleich
    {
        return Input.Subject;
    }
    return Input.Nothing;

}

private void CheckChars(short kbData)
{
    if (kbData == 0x00)//wenn im Zusammenhang Gültig
    {
        //irgendwas machen
    }
}
private void CheckSubjekt(short kbData)
{
    if (kbData == 0x01)//wenn im Zusammenhang Gültig
    {
        //irgendwas machen
    }
}

Gruß luciluke

Hallo luciluke,

ich habe mir die Details nicht angeschaut, aber so in der Art habe ich es gemeint.

herbivore

Es gibt auch Komponenten, die einen beim Aufbau einer Statemachine unterstützen. Z.B.:
stateless A C# Hierarchical State Machine
Simple State Machine
Ist auch so zum Verständnis ganz lesenswert.