Übergabe von Zeichenketten mit Leerzeichen an get-Befehle

Begonnen von tupol, 16 April 2016, 10:35:36

Vorheriges Thema - Nächstes Thema

tupol

Hallo Entwickler, halle Rudi,

ich möchte an einen get-Befehl mehrere Zeichenketten mit Leerzeichen übergeben. Also z. B.
get <device> command "dies ist ein Text" "Dies ist ein anderer Text"

Als Eingangswert in meine get-sub bekomme ich ja standardmäßig immer ein Array, welches aus dem get-Befehl gesplittet nach den Leerzeichen gebaut wird.
also
2 "dies
3 ist
4 ein
5 Text"
6 "Dies
...

Gibt es schon eine generelle Lösung wie man entweder ganze Zeichenketten in den einzelnen Befehls-Arrayfeldern übergeben bekommt oder gibt es bereits eine Unterfunktion, die diese Arrayfelder entsprechend aufarbeitet? Vielleicht hat auch schon jemand diese Unterfunktion für sein Modul erstellt.

Gruß
tupol

rudolfkoenig


tupol

#2
Der Befehl enthält einzelne Worte und mehrere Zeichenketten. Am Ende brauche ich jedes Wort und jede Zeichenkette als einzelnen Wert. (siehe https://forum.fhem.de/index.php/topic,29725.msg439865.html#msg439865)

   get <device> command "Dies ist ein Text" wort1 wort2 "Dies ist ein anderer Text" "Und dies ein weiterer Text"

sollte also im Sinne von ($$$$) folgendes ergeben

   2 Dies ist ein Text
   3 wort1
   4 wort2
   5 Dies ist ein anderer Text
   6 Und dies ein weiterer Text

Wenn es da noch nix gibt, vielleicht macht es Sinn, das global zur Verfügung zu stellen oder den FHEM-Befehl gleich nach (z.B.) Anführungsstrichen zu durchsuchen und vor dem Aufruf der get- und set-subs des Moduls die übergebenen Werte entsprechend zusammenzufassen.

Ansonsten muss ich mir was eigenes basteln. Ich könnte mir aber vorstellen, dass es noch weitere Interessenten für diese Aufarbeitung gibt und würde das Thema lieber zentral, im FHEM-Framework gelöst haben.

Dr. Boris Neubert

Hallo,

2013 bin ich das Thema mal motiviert angegangen, dann aber abgelenkt worden und nicht weiter gekommen.

Siehe bitte:

https://forum.fhem.de/index.php/topic,9977

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

Talkabout

Hallo zusammen,

in den meisten Modulen übernehmen die Entwickler die Arbeit, indem sie die Zeichenketten alle zusammenführen und dann mit regulären Ausdrücken parsen. Es ist natürlich nicht die perfekte Lösung, aber im Moment geht es nicht anders. Was man sich überlegen könnte wäre, dass FHEM intern beim Aufsplitten der Werte Anführungszeichen beachtet und diese als einzelne Array Elemente ablegt.

@tupol: wie wäre es wenn Du für diese Lösung einen Patch lieferst ;)

Gruss

tupol

Das mit den regulären Ausdrücken finde ich sehr gut. Wer hat das den schon gemacht?

Dr. Boris Neubert

Schau mal in das in meinem alten Beitrag referenzierte Tutorial.

Ich denke aber, dass der Parser in fhem.pl als Kommandozeilenparser sitzen muss, sonst bekommst Du mehrere Whitespaces zwischen den Non-Whitespaces nicht mit.

Wenn Du Dir die Diskussion aus dem alten Beitrag anschaust, siehst Du den höheren Zweck hinter der Idee mit dem Parser. Mit Gänsefüßchen demarkierte Zeichenketten wäre dann fast ein Abfallprodukt.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

ein paar unsortierte gedanken dazu:

das ganze kann im prinzip beliebig komplex werden, vor allem da es zur zeit keine einheitliche syntax für so etwas gibt die module die das benötigen alle irgend eine eigene lösung implementieren die nicht kompatibel zueinander sind und dann eventuell auch bei einer globalen lösung konflikte ergeben.

einen 'echten' parser einzubauen wäre zwar eine saubere lösung, es würde aber voraussetzen sich über die syntax gedanken zu machen. entweder global -> eventuell gibt es konflikte zwischen modulen, oder in jedem modul immer wieder -> unsauber, mehr arbeit für die modulentwickler. mit den im folgenden skizzierten ideen (besonders der letzten) kommt man aber eventuell genau so weit ohne die nachteile in kauf zu nehmen.

wenn es nur um das ersetzen/maskieren von leerzeichen geht kann man modul einfach mit s/../../ selber machen. welches zeichen man dem anwender dafür vorgibt hängt vom modul ab und ob diese(s) zeichen eine andere sinnvolle bedeutung haben. %20 oder + oder . ist eine möglichkeit. für den anwender ist das unter umständen nicht besonders gut zu schreiben.

im oben verlinkten fall kann man auch einfach so lange die folgenden parameter joinen wie er mit " anfängt und noch nicht mit " aufhört. so mache ich es readingsGroup modul. das geht für einfache dinge recht gut.

eine weiter möglichkeit ist es über mehrzeilige attribute oder kommandos zu gehen. in fhemweb geht beides über textField-long recht gut.

oder man verwendet als eingabe direkt einen ausgeschriebenen perl hash oder json. das lässt sich unzweideutig parsen und es gibt inzwischen auch den perlSyntaxCheck den verwenden kann um recht einfach auf fehler hinzuweisen. für dinge die man oft manuell eingib ist das vielleicht unhandlich, für attribute oder kommandos die man selten eingibt funktioniert das sehr gut und flexibel.

anführungszeichen zum gruppieren zu verwenden bietet sich zwar auf den ersten blick an, ist aber eigentlich nicht ideal da man anfang und ende nicht eindeutig unterscheiden kann. zusammengehörende pärchen wie {},(),[],<> sind eigentlich besser geeignet da sie beim verschachteln keine probleme machen. man muss nur zählen.

man muss sich überlegen wie man das betreffende zeichen maskiert wenn es innerhalb des textes vorkommen darf.

es gibt ein perl modul Text::Balanced mit dem man die beiden vorherigen punkte abdecken kann.


ich habe zur zeit aber einen ganz anderen favoriten: wie für das define im Weather modul vorgeschlagen könnte man die erlaubte syntax erweitern und für define, set und get parameter der form name=wert erlauben.

- ein modul das dieses feature nutzen möchte macht dies in Initialize bekannt
- für define, set und get wird die kommandozeile nicht mehr in ein array gesplittet sondern wie folgt geparsed
  - für alle name=wert argumente wird der wert mit dem namen als key in einen hash gesteckt
  - argumente ohne = werden wie bisher als wert in ein array gesteckt
  - wenn ein wert bei 1 oder 2 mit einem " beginnt aber nicht endet wird so lange der nächste parameter
    angehängt bis das schliessende " gefunden ist.
    eventuell "..." und '...' erlauben und so das maskieren erübrigen. verschachteln wird nicht erlaubt.
  - die DefFn, SetFn und GetFn werden mit ref auf den hash und das array aufgerufen.
- man könnte spezielle namen für das system reservieren und so im define erlauben z.b. direkt schon attribute zu initaliasieren
  oder den devspec FILTER= parameter umstellen.

gruss
  andre


hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Dr. Boris Neubert

Zitat von: justme1968 am 16 April 2016, 16:55:19
einen 'echten' parser einzubauen wäre zwar eine saubere lösung, es würde aber voraussetzen sich über die syntax gedanken zu machen. entweder global -> eventuell gibt es konflikte zwischen modulen, oder in jedem modul immer wieder -> unsauber, mehr arbeit für die modulentwickler. mit den im folgenden skizzierten ideen (besonders der letzten) kommt man aber eventuell genau so weit ohne die nachteile in kauf zu nehmen.

Meine Gedanken dazu waren/sind, dass ein Modul optional die Grammatik mitliefert und der Default ist, durch Whitespace getrennte Wörter wie gehabt zu splitten aber Text zwischen "..." und '..' als ein Wort zusammenzuhalten (unter Entfernung der Quotation Marks).

Nachteil an dem Parser: steile Lernkurve, aufwändig, in jedem Einzelfall vermutlich überdimensioniert, und deswegen auch bisher nicht realisiert.

Zitat
ich habe zur zeit aber einen ganz anderen favoriten: wie für das define im Weather modul vorgeschlagen könnte man die erlaubte syntax erweitern und für define, set und get parameter der form name=wert erlauben.

Das ist auch mein Favorit, zumal ich im Calendar-Modul die Syntax für get ... events genau so machen werde, um die komplexen Regeln für die Selektion und Formatierung von Terminen auf die Kommandozeile zu bringen.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

meine routine zum parsen der oben vorgeschlagenen syntax schaut zur zeit so aus:sub
parseParams($)
{   
  my($cmd) = @_;
  my(@a, %h);
   
  my @params = split(" ", $cmd);
  while (@params) {
    my $param = shift(@params);
    my ($key, $value) = split( '=', $param, 2 );
   
    if( !defined( $value ) ) {
      $value = $key;
      $key = undef;
    }
   
    while ($param && $value =~ m/^('|")/ && $value !~ m/$1$/ ) {
      my $next = shift(@params);
      last if( !defined($next) );
      $value .= " ". $next;
    }

    if( $value =~ m/^('|")/ && $value =~ m/$1$/ ) {
      $value =~ s/^.(.*).$/$1/;
    }
   
    if( defined($key) ) {
      $h{$key} = $value;
    } else {
      push @a, $value;
    }
   
  }
   
  return(\@a, \%h);
}


das ergebnis eines aufrufen mit my $cmd = 'set name test1 test2=abc test3 "test4 test4" test5="test5 test5" test6=\'test6=test6\' test7= test8="\'" test9=\'"\'';
print Dumper parseParams( $cmd );
ist$VAR1 = [
          'set',
          'name',
          'test1',
          'test3',
          'test4 test4'
        ];
$VAR2 = {
          'test8' => '\'',
          'test6' => 'test6=test6',
          'test9' => '"',
          'test5' => 'test5 test5',
          'test7' => '',
          'test2' => 'abc'
        };


etwas in der art könnte man zentral in fhem übernehmen und zunächst erst mal von hand aus den jeweiligen modul routinen aufrufen.


wenn rudi einverstanden ist könnte man auch noch mit dem nächsten schritt weiter machen und wie oben vorgeschlagen das parsen automatisieren. z.b. in dem man in Initialize $hash->{parseCmds} = 1 setzt. und die aufrufe von DefFn, SetFn und GetFn entsprechend so umbaut:my $ret = CallFn($name, "DefFn", \%hash, $modules{$m}->{parseCmds}?parseParams($def):$def);

wenn aus irgend einem grund das mit dem 'echten' parser doch noch nötig wird könnte man den dann z.b. mit $hash->{parseCmds} = 2 aktivieren.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

tupol

Finde ich gut. Automatisch sollte es nie übernommen werden, sonst kommte es evtl. zu Störungen in Modulen, die sich eigene Lösungen gebastelt haben. Bei meinen Modulen führt auch die missbräuchliche, vom Anwender gelegentlich hineininterpretiert Verwendung von "..." zu Fehlern.

rudolfkoenig

Ich habe nichts dagegen, aber auch keine Verwendung dafuer.
D.h. wenn jemand einen kompletten Patch macht, werde ich es einpflegen.

Markus Bloch

Find ich super, ich würde es aber anstatt nur auf "..." zu begrenzen, es mind.  auf {...} wegen Perl-Code erweitern. Evtl. noch (...)

Gruß
Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

anbei ein patch für den ersten teil. es wird eine parseParams routine zu fhem.pl hinzugefügt.

- parameter könne wie bis her einfach durch leerzeichen getrennt sein
- zusätzlich ist die variante mit name=wert möglich
- wenn ein wert mit ' oder " beginnt wird bis zum jeweils zugehörenden schliessenden zeichen alles zu einem wert zusammengefügt
- es ist kein maskieren von ' oder " vorgesehen. um diese zeichen tatsächlich am anfang oder ende eines parameters zu verwenden muss der parameter mit dem jeweils andere anführungszeichen eingeschlossen werden.
- ein paar von identischen anführungszeichen am anfang und ende eines wertes wird entfernt
- wenn ein wert mit { anfängt werden so lange alles zu einem wert zusammengefügt bist es zu jede öffnenden auch eine schliessende klammer gibt.

d.h. ein string
$cmd = 'set name test1 test2=abc test3 "test4 test4" test5="test5 test5" test6=\'test6=test6\' test7= test8="\'" test9=\'"\' {my $x = "abc"} test10={ { my $abc ="xyz" } }';

wird so aufgeteilt:$VAR1 = [
          'set',
          'name',
          'test1',
          'test3',
          'test4 test4',
          '{my $x = "abc"}'
        ];
$VAR2 = {
          'test10' => '{ { my $abc ="xyz" } }',
          'test5' => 'test5 test5',
          'test8' => '\'',
          'test2' => 'abc',
          'test7' => '',
          'test9' => '"',
          'test6' => 'test6=test6'
        };


eventuell könnte man damit sogar die fhem kommandozeile parsen und das verdoppeln der ; innerhalb vom {..} perl code los werden.

das optionale automatische parsen für DefFn, SetFn und GetFn schaue ich mir an und schlage einen zweiten patch vor.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Zitatanbei ein patch für den ersten teil.
Ist wirklich nur der erste Teil.
Habs aber eingecheckt.