Der Regex Megathread

Begonnen von RichardCZ, 06 Mai 2020, 12:18:14

Vorheriges Thema - Nächstes Thema

RichardCZ

Es gibt neben Perl noch einen Formalismus, der sehr wichtig ist und der mit Perl eigentlich nichts zu tun (*) hat: Reguläre Ausdrücke.

Die beherrschen die Meisten hier auch nicht...  ;) (woah die Menge tobt)
...oder sind einfach nur zu faul bequem sie richtig zu machen. (die Menge tobt noch mehr obwohl ich ja so diplomatisch war)

Dieser Thread ist somit überfällig!

Praktisch jeder reguläre Ausdruck in FHEM ist falsch. Das mag sich jetzt deprimierend anhören, ist aber so.
Zur Aufmunterung schicke ich also gleich hinterher: Es ist verdammt schwer einen regulären Ausdruck tiptop richtig hinzubekommen.

Mein Ziel mit diesem Thread wäre es also den Regex-FHEM-Standard von "komplett daneben" zu "akzeptabel" zu hieven.

(*) Die Aussage stammt von Larry Wall persönlich




Kopf hoch, wenn einer ne Chance hat reguläre Ausdrücke zu knechten, dann sind das Perler mit ihrer Fähigkeit wirre Zeichenfolgen zu parsen. ;-)
Diese Aussage ist noch nicht einmal so übertrieben, denn wer sich z.B. ein wenig in der Python oder Java Community rumtreibt, wird nicht
selten die dort vorherrschende Meinung zu regulären Ausdrücken hören: "braucht man in 99% der Fälle nicht." und dann werden oft Turnübungen
veranstaltet (äquivalent zu substr, index, etc.) um ja Regexen zu vermeiden.

Fishermans Friend halt. ¯\_(ツ)_/¯

Hier ist mal so ein regulärer Ausdruck wahlfrei aus FHEM gefischt. Er soll ein Datum Jahr-Monat-Tag matchen, wobei die Jahr Angabe optional ist.

m/^((\d{4})-)?([01]\d)-([0-3]\d)$/;

Fehler 1: \d statt [0-9]

Klar, es ist bequemer \d zu schreiben als [0-9], aber \d matcht unter UTF-8 (und damit haben wir es oft zu tun, oder bekommen es damit zu tun so sicher wie das Amen in der Kirche) halt noch eine Menge anderen Sotter:

ZERO:  0٠۰߀०০੦૦୦௦౦೦൦๐໐0
ONE:   1١۱߁१১੧૧୧௧౧೧൧๑໑1
TWO:   2٢۲߂२২੨૨୨௨౨೨൨๒໒2
THREE: 3٣۳߃३৩੩૩୩௩౩೩൩๓໓3
FOUR:  4٤۴߄४৪੪૪୪௪౪೪൪๔໔4
FIVE:  5٥۵߅५৫੫૫୫௫౫೫൫๕໕5
SIX:   6٦۶߆६৬੬૬୬௬౬೬൬๖໖6
SEVEN: 7٧۷߇७৭੭૭୭௭౭೭൭๗໗7
EIGHT: 8٨۸߈८৮੮૮୮௮౮೮൮๘໘8
NINE:  9٩۹߉९৯੯૯୯௯౯೯൯๙໙9


siehe https://stackoverflow.com/questions/890686/should-i-use-d-or-0-9-to-match-digits-in-a-perl-regex

Fehler 2: Implizit [0-9] -> zu locker

siehe https://stackoverflow.com/questions/2137929/how-can-i-use-a-regular-expression-to-validate-month-input

(Interessant übrigens, wie auch in den Kommentaren durchscheint, dass Regexen für Validierung ein overkill sind.
Ich bin anderer Ansicht. Einen guten Teil der Validierung bekommt man bei guten Regexen "en passant" mit dem Matching.
Im Vorbeigehen also.)

Regex für Monat: (0[1-9]|1[012])
Regex für Tag: (0[1-9]|[12][0-9]|3[01])

Natürlich ist es wichtig den Regexen Begrenzungen mit auf den Weg zu geben (^ und $ - eigenlich \A und \z) und ja,
man könnte darüber nachdenken häufig benutzte reguläre Ausdrücke in Konstanten abzulegen zur Wiederverwendung

Readonly my $DAY_RX => qr{(0[1-9]|[12][0-9]|3[01])};

zum Beispiel. Und irgendwann dann stolpert man über https://metacpan.org/pod/Regexp::Common

Fehler 3: Die Jahreszahl

Das matcht 0001, was keine korrekte Jahreszahl ist. Nun könnte man sich z.B. auf 1000 to 2999 einigen

^[12][0-9]{3}$

oder 1900-2099

^(?:19|20)[0-9]{2}$ (Ja, wo man nur Alternierungen braucht sollte die Gruppe "non-capturing" sein)

oder man abstrahiert vom Y2K-Problem und

^[1-9][0-9]{3,}$

Alles Definitions-/Konventionsfrage, aber zumindest hat man dann nicht "mal was schnell dahingesch*", sondern sich was dabei gedacht.

Versäumnis 1: Lesbarkeit

m{...}xms

Leerzeichen, Kommentare... Hatten wir schon

Versäumnis 2: Named Captures

Seit Perl 5.10

m{\A
  ((?<year> (?:19|20)[0-9]{2})-)?    # here we explain to poor unenlightened people what we meant
  (?<month> (0[1-9]|1[012]))         # this leaves us space for some philosophical tractate
  -                                  # now we have enough space (and cognitive capacity) for other delimiters
  (?<day> (0[1-9]|[12][0-9]|3[01]))  # and here a little rant maybe?
  \z}xms


statt also $1, $2 rumzuzählen (und was passiert eigentlich wenn ne capture optional ist?) und die Zahlen zu vertauschen wenn was an der Regex geändert wird, kann man nun einfach
$+{year}, $+{month}, $+{day} rausfischen. (Wobei $+{year} u.U. undef ist.)

Vergleicht man das mit dem ursprünglichen Code, wird ganz klar warum bei vielen Programmierern die Einstellung "gut genug" vorherrscht.
Aber ist diese Einstellung "gut genug"?

PBP widmet regulären Ausdrücken das gesamte Kapitel 12 (235-271),
https://perldoc.perl.org/perlre.html ist einer der dicksten Doku-Oschis, den Perl zu bieten hat

Das Pferd ist nicht leicht zu zügeln, aber wenn man das (zumindest teilweise) schafft, dann pflügt euch das den Acker um
in einer Art und Weise, die ihr nie für möglich gehalten hättet.

positive & negative lookbehinds & lookaheads - sage ich da nur. Was könnte man alles an hanebüchenen Code knicken, wenn man nur die Tools hätte bzw. beherrschen würde.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

RichardCZ

Ein anderer Favorit von mir ist

my @a = split( "[ \t][ \t]*", $def );

habe ich schon erzählt davon - oder? Es gibt nicht viel an dieser Zeile was nicht falsch wäre und dennoch bringt die letzte FHEM-Volkszählung knapp 800 solcher (und ähnlicher) Zeilen hervor. Also bei

my @args = split m{\s+}xms, $def;

hätte ich jetzt Nichts gesagt und würde das einfach mal als Ersatz empfehlen (natürlich muss man dann $a[0] -> $args[0] im Code machen)

Aber wichtig wäre zu verstehen warum die ganz o.g. Zeile der Antichrist ist.




Ich habe jetzt keine historischen Untersuchungen angestellt, aber es scheint, als wäre "[ \t][ \t]*" eine 'Verbesserung' von  " "  (wobei mir langsam die Anführungszeichen ausgehen):

my @a = split( " ", $param );

Irgendwann wollte man dann wohl noch ein Tab matchen, irgendwann dann noch "ein oder mehrere". Tadaa! Und dann wurde das halt kopiert.




Dabei ist vermutlich alles was man möchte "einfach" durch "Leerzeichen" getrennte Argumente/Parameter aufzutrennen.
Wenn wir unseren Regex-Hut aufsetzen, reden wir nicht von Leerzeichen, sondern von "Whitespace". Und von der Sorte gibt es viel.

Siehe https://perldoc.perl.org/perlrecharclass.html - oder auch nicht.

Space, Tabulator, Linefeed, Carriage Return, EN Quad, EM Quad, Paragraph Separator, und noch ungefähr 20 weitere.
Die meisten davon können als sog. Delimiter verwendet werden um zwischen Argumenten zu unterscheiden. Also verwenden wir doch das
was alle diese Delimiter matcht: \s

Und wenn "ein oder mehrere" gefragt sind, dann ist das immer +, nicht * (0 oder mehrere).

also es gilt immer: xx* = x+ = x*x

und man muss ja nicht unnötig komplexere Regexen schreiben als notwendig. Die werden auch so schon heftig genug manchmal. Folglich:

[ \t][ \t]* = [ \t]+

Naja und wenn man mehr matchen will (ja, die Regex wird an der Stelle lockerer, aber in diesem speziellen Fall eben "generischer"), dann eben

[ \t]+ ⊂ \s+ (soll heißen "ist/matcht Teilmenge von")

Also:

my @a = split( " ", $def );
->
my @a = split( "[ \t][ \t]*", $def );
->
my @a = split( "[ \t]+", $def );
->
my @args = split m{\s+}xms, $def;

* es ist kürzer (& lesbarer), mächtiger (& richtiger)
* es ist schneller, wenngleich nur ein "paar Nanosekunden"
* perlcritic lässt euch bis -2 in Ruhe (für diejenigen, die jetzt meinen xms sei für die kurze Regex overkill)
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

amenomade

Naja. Die Nutzung von Regex ist immer abhängig vom Kontext. Wenn man weiss, woher die Daten kommen, braucht man ggf. nicht UTF8 in einem Datum zu berücksichtigen.

Genauso soll die Komplexität der Regex (und dazugehörige Lesbarkeit des Codes - Du sagst es selbst im 2. Post: "man muss ja nicht unnötig komplexere Regexen schreiben als notwendig") abhängig vom Kontext bleiben.

Die Regex für das Datum kann man z.B. noch komplexer machen: deine Regex matcht auch den 30 Februar. Zu locker? Und was mit dem 29 Februar abhängig von Schaltjahr? Und so weiter.  Aber ist das wirklich nötig ??? ??
Pi 3B, Alexa, CUL868+Selbstbau 1/2λ-Dipol-Antenne, USB Optolink / Vitotronic, Debmatic und HM / HmIP Komponenten, Rademacher Duofern Jalousien, Fritz!Dect Thermostaten, Proteus

RichardCZ

#3
Zitat von: amenomade am 06 Mai 2020, 16:50:29
Die Regex für das Datum kann man z.B. noch komplexer machen: deine Regex matcht auch den 30 Februar. Zu locker? Und was mit dem 29 Februar abhängig von Schaltjahr? Und so weiter.  Aber ist das wirklich nötig ??? ??

Gutes Beispiel. Genau das ist in einer Regex nicht mehr nötig und ich bin sicher nicht auf dem
dogmatischen Dampfer "alles in die Regex", 

Reguläre Ausdrücke sind (normalerweise, die in Perl schon) nicht Turing-vollständig
und können daher prinzipiell nicht weit in die Semantik (Schaltjahre etc.) reingreifen.
Aber sie können als Filter dienen wie weit man nach so einer Regex eben Programmcode zur weiteren
Validierung bemühen muss.

Es macht schon einen Unterschied ob ich nur Februar <= 29 checke, oder mich u.a. auch mit dem
39.19.0001 herumschlagen muss.

ZitatWenn man weiss, woher die Daten kommen, braucht man ggf. nicht UTF8 in einem Datum zu berücksichtigen.

Ich erlaube mir den leicht stichelnden Einwurf, dass man bei Betrachtung der Quelltexte eher den Eindruck gewinnt,
der Betreffende weiß sicher nicht woher die Daten kommen, bzw. macht sich keine Gedanken. Zumindest hielte ich
das Argument "das hat alles so seine Richtigkeit - ist gewollt" für ... gewagt.

Erfahrung bedeutet "den richtigen Mittelweg finden". Leute die von einem Extrem (.*) ins Andere "kann man z.B. noch komplexer machen:"
schwingen haben nach meiner Erfahrung Probleme auf dem Mittelweg zu bleiben.  Jugendlicher Leichtsinn halt. ;)

Reguläre Ausdrücke sind ein extrem gut ausgestatterer Werkzeugkasten. Es ist schade mit anzusehen wie daraus immer
wieder nur der Hammer herausgeholt wird - auch in Fällen wo ein Korkenzieher besser gewesen wäre. Prost!

ZitatKontext ... Du sagst es selbst im 2. Post: "man muss ja nicht unnötig komplexere Regexen schreiben als notwendig")

Wenn man sich den Kontext anschaut wo ich das sage, dann ist das auf die Situation

"Habe ich zwei äquivalente reguläre Ausdrücke, nehme ich den einfacheren davon."  ([ \t][ \t]* = [ \t]+)

Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

zap

#4
Jeffrey Friedl: "Mastering regular expressions". Lesen, verstehen und anwenden.
😎
2xCCU3, Fenster, Rollläden, Themostate, Stromzähler, Steckdosen ...)
Entwicklung: FHEM auf AMD NUC (Ubuntu)
Produktiv inzwischen auf Home Assistant gewechselt.
Maintainer: FULLY, Meteohub, HMCCU, AndroidDB

RichardCZ

Zitat von: zap am 06 Mai 2020, 18:53:20
"Mastering regular expressions". Lesen, verstehen und anwenden.

Richtig! (Jeffrey Friedl

https://doc.lagout.org/programmation/Regular%20Expressions/

Es ist ja nicht so, dass es einen Mangel an Literatur/Dokumentation gäbe. Vielleicht ist das Problem eher, dass es zuviel davon gibt.
Ich dachte mir, ich picke wie üblich FHEM-Spezifika heraus und diskutiere die. Soll aber nicht in einen Monolog ausarten - wenn
jemand konkrete Fragen zu einer Regex hat, ist hier durchaus der Platz dafür.

https://www.heise.de/hintergrund/Regex-Katastrophen-Wenn-regulaere-Ausdruecke-Software-lahmlegen-4702727.html

;-)

Cloudflare SuperGAU.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

RichardCZ

Bereits vor 20 Jahren hat Ovid (Illustres Mitglied der Perl Community) auf perlmonks.org einen Artikel mit diesem Titel geschrieben: https://www.perlmonks.org/?node_id=24640

Im Grunde ging es darum man solle .* vermeiden. Wie üblich in Diskussionsforen  ;) - folgte darauf mal mehr, mal weniger Weißes Rauschen, aber Grundaussage bleibt bis heute valide bestehen:

.* ist schlecht. Meistens. Fast Immer.

Was bedeutet .* ? "Matche ein beliebiges Zeichen 0 oder mehrmals."  Bevor man eigentlich im Detail begreiflich machen kann wie schlecht dieses Konstrukt ist, muss man etwas über Gier und Backtracking erzählen.

Quantoren wie '+' und '*' sind gierig (greedy). Sie werden immer versuchen so viele Zeichen zu matchen wie es geht. Also angenommen man hat:

* einen string 'ABCDEGABEDGABCDEFGH'
* die Regex '.*AB'

dann matcht in diesem Fall .* den string 'ABCDEGABEDG' und nicht etwa den leeren String oder 'ABCDEG'.
Das kann durchaus das sein was man möchte, aber das schlägt leider sehr schnell in einen Performance-Albtraum um, wenn man z.B. sowas macht:

if ( $event =~ /(.*: +.*: +.*)+/ ) # ja, FHEM code

Denn jetzt wird jeder der gierigen .* versuchen einen möglichst langen String zu matchen.
Der Erste wird bis zum letzten Doppelpunkt mit folgendem Space matchen und happy sein.
Allerdings stellt dann der zweite fest (vereinfacht gesagt), dass der reguläre Ausdruck jetzt nicht mehr erfüllbar ist.
Die Regex-Engine vollführt nun Backtracking: Der eigentlich schon verdaute String wird wieder Zeichen für Zeichen
ausgespuckt bis der zweite .* genug hat um matchen zu können...
... und dann kommt das gleiche Spiel mit dem dritten, und vierten und ... (man sieht den + in der äußeren Klammer?)

Es wird schon funktionieren, aber die Laufzeit ist unter aller Sau.

Also wenn schon .*, dann non-greedy => .*? oder doch .+? oder doch [^:]+ (im obigen Beispiel)




Oder betrachten wir mal die Sache von der anderen Seite:

TcpServerUtils.pm:        if ( $cp =~ m,^(.*)/(.*?), && !-d $1 && !mkdir($1) ) {

Der Match funktioniert (vermutlich) nur deswegen, weil der zweite .* eben non-greedy ist. Ich frage mich
ob man das Fragezeichen von Anfang an so "richtig" drinhatte oder erst nach ein wenig Leidensweg.  ;)
Aber meine Hand ins Feuer legen, dass es funktioniert - würde ich nicht.




Und dann gibt es natürlich noch die Fälle wo .* einfach nur toter Code ist:

$cmd =~ m/^on.*/i

Was kann das, was

$cmd =~ m/^on/i

nicht könnte? Also außer Prozessorzyklen auffressen...




Also zusammengefasst und unterstrichen: Jedesmal wenn es euch gelüstet .* zu schreiben, sollten sich euch fortan die Nackenhaare aufstellen und statt .* sollte was besseres zum Zuge kommen.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

amenomade

#7
"Gutes" Beispiel heute im Forum gesehen, das mehrere Aussagen von dir illustriert:
Aus einem Text
12:07:43|0|noraw|1","003":"2|sur001|403470039|128|22|HUMB|86.7|2020-05-06
12:07:43|0|noraw|1","004":"3|sur001|403470039|128|22|SOILT|14.60|2020-05-06
12:07:43|0|noraw|1","005":"4|sur001|403470039|128|22|UV|32.27|2020-05-06
möchtet der TE die dezimale Zahl, die nach HUMB| steht extrahieren, also hier 86.7

Ihm wurde zuerst folgende Regex vorgeschlagen (ich vermute, der Autor wollte dich herausfordern ):
/^.*HUMB\|([0-9]+(\.[0-9])?)\|[0-9]{4}?.*/
187 steps. Backtracking ist mit "^.*" vom Anfang an angesagt. Je länger der Text nach dem gesuchten Wert, desto mehr steps.

Und warum ^ wenn sowieso danach .* kommt...? "Suche vom Anfang an, aber bitte mit so vielen Zeichen wie möglich danach , die mich nicht interessieren"

Und warum .* ganz am Ende? Wobei wir hier nur von einem einzigen zusätzlichen step reden.

Schon ohne ^.* am Anfang fällt man auf 16 steps.

Eigentlich reicht:
/HUMB\|([0-9.]+)/
9 steps, egal was danach steht. (OK, man kontrolliert damit nicht, dass nach der Zahl ein Pipe und 4 Ziffern kommen, und man kontrolliert auch nicht, dass in dem Wert der dezimale Punkt nicht mehrmals auftaucht. Zu locker? Abhängig vom Bedarf?)

Pi 3B, Alexa, CUL868+Selbstbau 1/2λ-Dipol-Antenne, USB Optolink / Vitotronic, Debmatic und HM / HmIP Komponenten, Rademacher Duofern Jalousien, Fritz!Dect Thermostaten, Proteus

Wzut

#8
Zitat von: RichardCZ am 06 Mai 2020, 19:32:19
wenn jemand konkrete Fragen zu einer Regex hat, ist hier durchaus der Platz dafür.
na sicher doch , als jemand der schon immer mit Regex auf Kriegsfuß steht und davon lebt die passende bei anderen abschreiben zu können :

a. meine Liebling Saubermach   =~ s/ //g; müsste ich zukünftig besser als =~ s/\s//g; schreiben ?

b. in 14_CUL_MAX steckt eine Regex die ich gerne loswerden möchte, aber natürlich keinen Plan habe wie
Es geht darum eine Nachricht in ihre Bestandteile zu zerlegen, bekannte Vorbedingung : Sie muß mit dem Buchstaben Z beginnen und mindestens 21 Zeichen lang sein. Prüfsumme gibt es keine, ausser das die richtige Gesammtlänge in den erste zwei Byte nach dem Z steckt. Die anderen Variablen haben jeweils eine fixe Länge bis auf die letzte $payload, daher wohl auch das "böse" .* am Ende :)

if ($rmsg !~ m/Z(..)(..)(..)(..)(......)(......)(..)(.*)/x) {
Log3 $shash,3, "$name, unknown message : $rmsg";
return $shash->{NAME};
    }

    my ($len,$msgcnt,$msgFlag,$msgTypeRaw,$src,$dst,$groupid,$payload) = ($1,$2,$3,$4,$5,$6,$7,$8);

    $len = hex($len);
    if (2*$len+3 != length($rmsg)) {
#+3 = +1 for 'Z' and +2 for len field in hex
Log3 $shash, 1, $name.', message len mismatch '.length($rmsg).' vs '.(2*$len+3);
return $shash->{NAME};
    }
   
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

Beta-User

Na ja, da du die Info aus dem letzten Element ja genau haben willst, gibt es vermutlich wenig Optimierungsmöglichkeiten, evtl. abgesehen davon, dass das (teilweise?) [0-9A-F]-Werte sind und man die Wiederholungsanzahl übersichtlicher schreiben kann?

Aber man kann das gleich benennen, wie das hier auch irgendwo schon steht. Für MySensors sieht das neuerdings so aus (Werte sind durch ";" getrennt):
sub parseMsg {
    my $txt = shift;

    use bytes;

    return if ($txt !~ m{\A
               (?<nodeid>  [0-9]+);
               (?<childid> [0-9]+);
               (?<command> [0-4]);
               (?<ack>     [01]);
               (?<type>    [0-9]{1,2});
               (?<payload> .*)
               \z}xms);

    return {
        radioId => $+{nodeid}, # docs speak of "nodeId"
        childId => $+{childid},
        cmd     => $+{command},
        ack     => $+{ack},
        subType => $+{type},
        payload => $+{payload}
    };
}
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Sidey

Wieso willst Du die regex komplett loswerden?

Wenn es mit Z beginnen muss würde ich auf ^Z am Anfang prüfen.
Da ich mir immer schwer tue, die . zu zählen hilft mir folgende Schreibweise:

(.{3}) dann müssen es drei beliebige Zeichen sein.
Anstelle von . kannst Du dir dann überlegen ob wirklich jedes Zeichen hier vorkommt:
Man kann auch min und max so angebrn, vielleicht hat der letzte Abschnitt ja eine Mindest und Maximallänge.

m/^Z(.{2})(.{2})(.{2})(.{2})(.{6})(.{6})(.{2})(.{1,})/x)

Ansonsten kannst Du das ja auch noch mit dem Vorschlag von Beta-User kombinieren :)

Gruß Sidey
Signalduino, Homematic, Raspberry Pi, Mysensors, MQTT, Alexa, Docker, AlexaFhem

Maintainer von: SIGNALduino, fhem-docker, alexa-fhem-docker, fhempy-docker

KernSani

Hänge mich mal zum Mitlesen mit rein. In TRX habe ich auch so undurchschaubare Konstrukte...


Kurz, weil mobil....
RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

Icinger

Dann würde mich auch mal die Meinung zu meinen OBIS-Konstrukten interessieren:

my $OBIS_value = qr{(<|>)?([-+]?\d+\.?\d*)\*?(.*)\).*}x;
my $hexreg=qr{([[:xdigit:]][[:xdigit:]])}x;

# Some known OBIS-Codes

my %OBIS_codes = (
    "DevInfo"      => qr{\/                    # Full Answer: /AAAB\@nnnnnnnnnnnnnn
                         (...)(.)            # AAA=Short Manufacturer        B=max. Baudrate
                         \\                 # Delimeter
                         (.*)}x,            # Device-ID
    "Serial"       => qr{^0-0:96.1.255(?:.\d+)?\((.*?)\).*}x,                                # 0-0:96.1.255*255(11400158)
    "Serial"       => qr{^(?:1-0:)?0\.0\.[1-9]+(?:.\d+)?\((.*?)\).*}x,
    "Owner"        => qr{^1.0.0.0.0(?:.\d+)?\((.*?)\).*}x,                                   # 1-0:0.0.0*255(GETTONE)
    "Status"       => qr{^1.0.96.5.5(?:.\d+)?\((.*?)\).*}x,                                  # 1-0:96.5.5*255(@)
    "Powerdrops"   => qr{^0.0.96.7.\d(?:.\d+)?\((.*?)\).*}x,                                 # 0-0:96.7.21(00001)
    "Time_param"   => qr{^0.0.96.2.1(?:.\d+)?\((.*?)\).*}x,
    "Time_current" => qr{^0.0.1.0.0(?:.\d+)?\((.*?)\).*}x,                                   # 0-0:1.0.0(160515181000S)
    "Channel_sum"  => qr{^(?:1.0.)?(\d+).1.7(?:.0|.255)?(?:\(.*?\))?\($OBIS_value}x,         # 1-0:2.1.7*255(07568.01*kWh)
    "Channels"     => qr{^(?:\d.0.)?(\d+).7\.\d+(?:.0|.255)?(?:\(.*?\))?\($OBIS_value}x,     # 1-0:21.7.255*255(0000.2264*kW)
    "Channels2"    => qr{^(?:0.1.)?(\d+).2\.\d+(?:.0|.255)?(?:\(.*?\))?\($OBIS_value}x,      # 0-1:24.2.1(160515180000S)(00041.661*m3)
    "Counter"      => qr{^(?:1.\d.)?(\d).(8)\.(\d).(\d+)?(?:\(.*?\))?\($OBIS_value}x,        # 1-0:1.8.0*255(17483.88*kWh)
    "ManufID"      => qr{^129-129:199\.130\.3(?:.\d+)?\((.*?)\).*}x,                            # 129-129:199.130.3*255(ESY)
    "PublicKey"    => qr{^129-129:199\.130\.5(?:.\d+)?\((.*?)\).*}x,
);

my %SML_specialities = (
    "TIME" => [
        qr{0.0.96.2.1
           |0.0.1.0.0}x,
        sub {
            return strftime( "%d-%m-%Y %H:%M:%S",
                localtime( unpack( "i", pack( "I", hex(@_) ) ) ) );
        }
    ],
    "HEX2" => [
        qr{1-0:0\.0\.[0-9]    }x,
        sub {
            my $a = shift;
            if ( $a =~ /^[0-9a-fA-F]+$/x ) { $a =~ s/(..)/$1-/xg; $a =~ s/-$//x }
            return $a;
        }
    ],
    "HEX4" => [
        qr{1.0.96.5.5|0.0.96.240.\d+|129.129.199.130.5}x,
        sub {
            my $a = shift;
            if ( $a =~ /^[0-9a-fA-F]+$/x ) { $a =~ s/(....)/$1-/xg; $a =~ s/-$//x }
            return $a;
        }
    ],
    "INFO" => [
        qr{1-0:0\.0\.[0-9]|129.129.199.130.3}x, ""
    ],
);


lg, Stefan
Verwende deine Zeit nicht mit Erklärungen. Die Menschen hören (lesen) nur, was sie hören (lesen) wollen. (c) Paulo Coelho

Wzut

Zitat von: Sidey am 07 Mai 2020, 08:16:08
Wieso willst Du die regex komplett loswerden?
perlcritic -> $1,$2 usw.
Das Bsp von Beta-User sieht optisch verdammt gut aus (auch wenn ich es noch nicht verstehe).
Werde ich heute Abend mal ein mini Programm machen und das mit diversen Telegrammen füttern.
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

Beta-User

Nur kurz als Merkposten:

Eine der "beliebtesten" Stellen, an denen regexe@FHEM stehen ist notify. Das macht immer "\A" und "\z" dazu, was bedeutet, dass man häufig "auffüllen" muß, also der Einfachheit den "bösen" ".*" hinten anfügt.

Habe jetzt  verstanden, dass es schneller ist, ein "(.*)?" ranzupappen bzw. entsprechende Abwandlungen davon, falls man weiß, dass da ein Zeichen sein muß.

Aus
defmod n_FK_TK_notify notify ((Dachf|F)enster|.*tuer)_.*:contact:.(open|tilted|closed)..to.VCCU. { myWinContactNotify ($NAME, $EVENT, 30) }

wurde jetztdefmod n_FK_TK_notify notify ((Dachf|F)enster|(.+)?tuer)_(.+)?:contact:.(open|tilted|closed)..to.VCCU. { myWinContactNotify ($NAME, $EVENT, 30) }
.

Hat jemand für einen DAM wie mich eine Größenordnung, um wie viel schneller das ist? Oder übersehe ich mal wieder was wesentliches?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files