C# Command Line Parser/Interpreter

lordfritte

Lieutenant
Registriert
Juli 2006
Beiträge
966
Hallo ich bin auf der suche nach einem Command Line Parser/Interpreter für C#, dem ich ein Muster vorgebe, z.b. "-f {Sting} {-y{-g String|-f Integer}}" und er die Eingabe danach abgleicht und zurück gibt ob die Eingabe passt oder ob sie nicht passt.
 
meinst du nicht, dass du das auch alleine schaffst? man brauch nur ein wenig logik dafür. ;)

aus einem projekt von mir:
Code:
AnsiString SubStringEx( const AnsiString &String, int Start, int End )
{
  AnsiString r;
  for( int i = Start; i <= End; i++ ) r += String[i];
  return r;
}
//---------------------------------------------------------------------------
AnsiString TrimQuote( AnsiString Subject )
{
  while( Subject[1] == '"' )
    Subject.Delete( 1, 1 );
  while( Subject[Subject.Length()] == '"' )
    Subject.Delete( Subject.Length(), 1 );
  return Subject;
}
//---------------------------------------------------------------------------
void TCommandLine::EvalCommandLine( void )
{
  Parameters->Clear();

  bool StringOpened = false;
  int params = 0;
  for( int i = 1; i <= CommandLine.Length(); i++ )
  {
    if( CommandLine[i] == '"' ) StringOpened = !StringOpened;
    if( !StringOpened && CommandLine[i] == ' ' )
    {
      Parameters->Add( EmptyStr );
      ++params;
    }
  }
  Parameters->Add( EmptyStr );

  StringOpened = false;
  int lastpos = 1;
  params = 0;
  for( int i = 1; i <= CommandLine.Length(); i++ )
  {
    if( CommandLine[i] == '"' ) StringOpened = !StringOpened;
    if( (!StringOpened && CommandLine[i] == ' ') || i == CommandLine.Length() )
    {
      Parameters->Strings[params] = TrimQuote( SubStringEx( CommandLine, lastpos, i ).Trim() );
      lastpos = i + 1;
      ++params;
    } 
  }
}
Parameters ist dabei eine TStringList, was eine einfache liste aus strings darstellt. übertragen schaffst du aber wohl allein. ;)
 
Mein Problem ist diese Verschachtelung, Klammern in Klammern und das eben alles in {} Optional sein soll, also es muss nicht aber kann.

Vielleicht hast du es ja falsch verstanden, aber ich möchte nicht nur eine Reihe von Parametern abklappern sondern auch mit Abhängigkeiten.
Z.b. Ich habe 3 Parameter, A, B und C
A kann ganz alleine stehen, A kann aber auch in Kombination mit B und/oder C stehen, aber B oder C können NICHT ohne A stehen.

Dann soll es bei den Parametern auch wieder Optionen geben, die man benutzen kann/muss.
 
Zuletzt bearbeitet:
beispiel: jeden "großen" bereich an optionen fängst du z.b. mit einem - oder / oder sonst was an, dann kannst du eingrenzen, wie du etwas bearbeitest.

gehen wir mal von folgendem aus:

-message <string> [buttons] -> zeigt eine messagebox an, mit dem text string, wo du optional die buttons angeben kannst (also ok, ja/nein, ja/nein/abbrechen und was es nicht noch gibt)

nehmen wir als beispielaufruf mal folgende kommandozeile:
Code:
test.exe -message "test nachricht 1" -message "test nachricht 2 mit ja/nein buttons" YES_NO -message "test nachricht 3 mit ok/abbrechen buttons" OK_CANCEL

bastel dir also eine schleife, die parameter für parameter durchgeht. vllt mal kurz in c-syntax veranschaulicht:
Code:
for( int i = 0; i < paramlength; i++ )
{
  if( cmdline[i] == "-message" ) // findet den -message parameter
  {
    string msg = cmdline[++i]; // der string der angezeigt werden soll
    int buttons = OK; // standardmäßiger button in der msg box wäre hier also der ok-knopf
    if( cmdline[i+1][0] != "-" ) // prüft, ob der nächste parameter noch zur messagebox gehört oder ein eigenständiger parameter ist
    {
      ++i;
      if( cmdline[i] == "OK_CANCEL" ) buttons = OK_CANCEL;
      else if( cmdline[i] == "YES_NO" ) buttons = YES_NO;
      // else if( ... ) ...
    }
    
    MessageBox( msg, buttons );
  }
}
es ist zwar nich das beste beispiel dafür, aber vllt siehst du ja, wie es gemacht wird. leg also für parameter, welche eine eigene bedeutung besitzen, einen präfix ein, wodurch du unterscheiden kannst, ob der parameter noch zum aktuellen bereich oder einem anderen/nächsten gehört. denk dir dabei aber auch eine sinnvolle anordnung der parameter aus, welche einfach nachvollziehbar und leicht umsetzbar sind. denk aber auch dran, dass optionale parameter immer ans ende einer parameterliste gehören und nicht irgendwo zwischendrin stehen sollten, da du so einfacher parsen und unterscheiden kannst. am einfachsten wäre es z.b. wenn du die anordnung der parameter wie in c++ o.ä. programmiersprachen einbindest (denke du weißt wie das mit mehreren standardwerten funktioniert).
 
Also das mit dem Algorithmus ist nicht mein Problem, mein Problem ist aus einem Zeichenkette den Algorithmus zu bilden.
Ich habe eine Funktion bool MatchCommandLine(string sMuster, string sCommandLine);
sMuster: Ist die Vorgabe wie es aussehen muss z.b. "-message <string> [buttons]"
sCommandLine: Ist die Eingabe "-message "test nachricht 1" -message "test nachricht 2 mit ja/nein buttons" YES_NO -message "test nachricht 3 mit ok/abbrechen buttons" OK_CANCEL"
und als Rückgabe wert kommt eben, true Eingabe passt ist das Muster, false Eingabe passt nicht ins Muster.

Ich denke das könnte man mit Regex machen, aber Regex ist sowas womit ich GARNICH klar komme, also ich habe mir schon einige Tutorials an gesehn, aber ka ich finde Regex unlogisch.
 
hm, eine extra prüfung wäre vllt. auch ein weg, aber dieser kostet imho unnötige rechenzeit, welche du einsparen kannst und den code nicht unnötig aufblähen musst, da du dies ohne probleme inline erledigen kannst.
reguläre ausdrücke wären ebenso fehl am platz, da dies genauso unnötig rechenzeit verbrät, denn einzelne string-funktionen arbeiten schneller und du kannst ebenso genauer matchen. es ist zwar ein komfortabler weg, aber regexps verwendet man (bzw. ich) nur bei größeren texten. z.b. bei html seiten mit bb-code oder ein gesamtes parsing einer seite sind ein guter fall für regexps, da man dies nur sehr umständlich mit kurzen string-funktionen lösen kann (bzw. den code nur unnötig aufblähen). aber wenn du unbedingt auf regexps ausweichen willst, kannst du das natürlich gerne tun. ein gutes tutorial gibt es z.b. bei regular-expressions.info. unlogisch aufgebaut ist es definitiv nicht, denn ein bestimmtes muster steckt immer dahinter. ;) vllt mal ein paar kleine beispiele anhand von bb-code:

Code:
#\[b\](.*?)\[/b\]#si
dieser regexp matcht alles, was zwischen den tags [ b] und [ /b] steht (leerzeichen wegdenken) und ersetzt sie mit dem jeweiligen äquivalent, welches du im nachhinein angibst (natürlich nur wenn du wünscht). beispiele kennst du dafür ja bestimmt zu genüge. halt jeder text, der fett geschrieben wird, kann so gematcht werden.
Code:
#\[url=www.(.*?)\]\[img\]www.(.*?)\[/img\]\[/url\]#
dieser regexp matcht z.b. alle verlinkten bilder im format [ url=http://www.example.tld/][ img]http://www.example.tld/foo/bar/foo.png[ /img][ /url].
Code:
#\[img=(.*?)\|(.*?)\](.*?)\[/img\]#si
matcht alles was im format [ img=breite|höhe] http://www.example.tld/foo/bar/foo.png[ /img] auftritt.

vllt mal eine kurze erklärung:

regexps müssen in zeichen begrenzt sein (in den oberen beispielen, betrifft dies das #). bei manchen implementierungen brauchst du diese nicht, aber in javascript (= /.../), php (= frei wählbar) und noch anderen werden diese gebraucht. das si bei den oberen beiden hinter dem begrenzer bedeutet, dass der ausdruck nur auf einer zeile auftreten darf (betrifft das s) und kontext-insensitiv (betrifft das i) sein muss (d.h. es ist wichtig, ob groß- oder kleinschreibung von interesse ist). die escape sequenzen kennst du ja auch aus c (z.b. für einen tab-stopp \t, neue zeile \n oder einen carriage return (wagenrücklauf) \r), denn die zeichen [ und ] haben eine andere bedeutung. alle sachen, welche in klammern stehen, bedeuten, dass diese als "rückgabewert" gehandhabt werden. mit diesen kannst du also arbeiten, d.h. diese ersetzen, korrigieren, irgendwo anders einfügen oder oder oder. der punkt bedeutet, dass irgend ein zeichen auftreten darf, egal ob ä, #, $, ü, a, 0, f, ... . das sternchen heißt, dass dieses zeichen bzw. der ausdruck, welche davor steht, x mal auftreten kann (egal ob 0 mal, 20 mal, 100 mal oder 1.000.000 mal). der "gegensatz" dazu ist das +, was heißt, dass der ausdruck davor, mindestens 1 mal, aber bis unendlich mal auftreten kann. das fragezeichen bedeutet, dass dieser text vorkommen kann, aber nicht muss.

ich kann dir ja mal weitere beispiele nennen, an welchem du die regulären ausdrücke verstehen kannst (und evtl. selbst nachbauen solltest). wenn du selbst welche erstellen willst, fang am besten mit den bb-code tags an. diese sind imo die einfachsten die es gibt und man kann an diesen schnell erlernen, wie eigentlich dieser länge smiley der welt funktioniert. ;)

vllt mal ein paar weitere beispiele, an denen du üben (verstehen und nachbauen) kannst:

Code:
#\[img\](.*?)\[/img\]#si -> [ img]<url>[ /img]
\[img=http://(.*?)\]#si -> [ img=http://<url>]
\[url\]http://(.*?)\[img\]http://(.*?)\[/img\]\[/url\]#si -> [ url]http://<url>[ img]http://<url>[ /img][ /url]
\[url\]www.(.*?)\[img\]www.(.*?)\[/img\]\[/url\]#si -> [ url]www.<url>[ img]www.<url>[ /img][ /url]
\[url=http://(.*?)\]\[img\]http://(.*?)\[/img\]\[/url\]#si -> [ url=http://<url>][ img]http://<url>[ /img][ /url]

am einfachsten erlernst du es, wenn du dir mit php ein paar seiten parst. lies z.b. mal von heise.de alle news-titel heraus. oder lies mal alle bilder von einer seite aus. übung macht den meister ist hier die devise, denn anders lernst du es nicht.

irgendwann schaffst du es dann, solche regulären ausdrücke zu schreiben (matcht eine valide email-adresse nach rfc 2822):
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

und so als tipp: am anfang bin ich auch verzweifelt, aber mittlerweile läuft das wie geschmiert. ;)
 
Ich glaub der Thread kommt in meine Bookmarks :)

@claW.: Machst du C# beruflich?
 
IgG schrieb:
@claW.: Machst du C# beruflich?
nein, aber seit 6 jahren programmiere ich. angefangen mit delphi (pascal), über c++ zu php (diese beiden hauptsächlich) und mittlerweile auch java und c# (studienbedingt, aber teils auch privat) und nebensächliches wie sql, css, html, latex, ...
 
Hi,

hoffe ich verstehe das jetzt richtig, aber je nachdem wie umfangreich du es haben/machen willst und auch in Bezug auf die Erweiterbarkeit könntest du dafür einen Tokenizer + Interpreter bauen. Mit etwas Aufwand verbunden, aber durchaus nützlich und es spart String-Gefrickel im Quellcode.

Was heißt Tokenizer + Interpreter:

Eine Tokenizer wandelt z.B. String-Zeichen bzw. Zeichenfolgen in sogenannte Tokens um. Ein Token ist einfach nur ein Symbol mit welches man dann weiterarbeitet - Google spuckt da recht interessante Artikel zu aus.

Diese Tokens + Parameter steckt man in einen Stack und lässt sie vom Interpreter abarbeiten.

Nur ein Denkanstoss wie man es noch machen können - für Erweiterbarkeit jedenfalls eine wunderbare Sache (außerdem lernt man dabei noch wie ein Compiler unter anderem arbeitet).

MFG,
swift.-
 
Zurück
Oben