Meowsalot: Alles zwischen zwei Zeichen ausschneiden

Hallo,

ich habe eine Bezeichnung z.B.

ich78bin_einzwei_5tg7g_zu_ein

jetzt möchte ich alles ausschneiden was zwischen dem ersten und dem zweiten _ ist. Dieser Wert soll dann in einer Variable gespeichert werden. Im dritten Schritt soll aus dem was in der Variable steht nach den ersten drei Zeichen (Zahlen oder Buchstaben) ein / eingefügt werden.

Wie gehe ich da am besten vor?

Meine Idee wäre preg_match allerdings verstehe ich ehrlich gesagt die suchmuster nicht wirklich.

Bis bald! Meowsalot (Bernd)

  1. Tach!

    jetzt möchte ich alles ausschneiden was zwischen dem ersten und dem zweiten _ ist.

    Konkret: Finde ein _, dann alle Zeichen, die kein _ sind, gefolgt von einem _. Der Teil zwischen den beiden _ sollte gruppiert werden, damit man auf ihn zugreifen kann.

    /_([^_])_/ das wäre der Ausdruck dazu. Im Paramter $matches von preg_match() findest du sowohl die gesamte Fundstelle als auch die Gruppe. Am besten mit print_r() oder var_dump() anschauen.

    Im dritten Schritt soll aus dem was in der Variable steht nach den ersten drei Zeichen (Zahlen oder Buchstaben) ein / eingefügt werden.

    Das ist recht einfache Stringverarbeitung, mit substr() Teile bilden und zusammen mit dem / zusammenfügen. Kann man auch mit einem Regex-Replace machen, indem man die ersten drei Zeichen gruppiert und dann diese Gruppe durch sich selbst mit angehängtem / ersetzt.

    Meine Idee wäre preg_match allerdings verstehe ich ehrlich gesagt die suchmuster nicht wirklich.

    Das ist aber empfehlenswert, weil das ein sehr mächtiges Werkzeug ist, das zwar recht komplex ist, aber auch viele Dinge abkürzen kann.

    dedlfix.

    1. @@dedlfix

      /_([^_])_/ das wäre der Ausdruck dazu.

      Der Ausdruck sieht aus wie ein Mercedes in einem alternativen Stadtviertel: der Stern fehlt.

      _([^_]*)_ wäre der Ausdruck. (Die Begrenzungszeichen / gehören nicht zum Ausdruck.)

      LLAP 🖖

      --
      „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
      1. Tach!

        /_([^_])_/ das wäre der Ausdruck dazu.

        Der Ausdruck sieht aus wie ein Mercedes in einem alternativen Stadtviertel: der Stern fehlt.

        Stimmt, richtig hätte es zudem heißen müssen: "alles was kein _ ist und davon beliebig viele".

        _([^_]*)_ wäre der Ausdruck. (Die Begrenzungszeichen / gehören nicht zum Ausdruck.)

        Korrekt, aber nicht wirklich hilfreich, denn man muss diese (oder andere) Begrenzungszeichen notieren, damit es letztlich funktioniert.

        dedlfix.

      2. Hallo Gunnar,

        Der Ausdruck sieht aus wie ein Mercedes in einem alternativen Stadtviertel: der Stern fehlt. _([^_]*)_ wäre der Ausdruck. (Die Begrenzungszeichen / gehören nicht zum Ausdruck.)

        ok, so funktioniert dieser

        preg_match('/_([^_]*)_/', 'ich78bin_einzwei_5tg7g_zu_ein', $matches);
        var_dump($matches);
        

        Als Ausgabe erhalte ich

        array(2) {
          [0]=>
          string(9) "_einzwei_"
          [1]=>
          string(7) "einzwei"
        }
        

        Warum bekomme ich zwei Ausgaben? Einmal einer mit _ und einmal nur das Wort?

        Bis bald!
        Meowsalot (Bernd)

        1. Tach!

          Warum bekomme ich zwei Ausgaben? Einmal einer mit _ und einmal nur das Wort?

          Bitte Handbuch lesen. Das ist so definiert, dass du sowohl die komplette Fundstelle als auch die Gruppierungen einzeln im Ergebnis findest.

          dedlfix.

          1. Hallo dedlfix,

            hab es jetzt so gelöst. War dieses der richtig weg oder hätte es noch eine einfacheren gegeben?

            preg_match('/_([^_]*)_/', 'ich78bin_AAABBB_5tg7g_zu_ein', $matches);
            //var_dump($matches);
            
            $teil1 =  substr ($matches[1], 0,3);
            $teil2 =  substr ($matches[1], 3,6);
            
            echo $teil1 ."/".$teil2;
            

            Bis bald!
            Meowsalot (Bernd)

            1. Tach!

              hab es jetzt so gelöst. War dieses der richtig weg oder hätte es noch eine einfacheren gegeben?

              Definiere "einfach"! Mit preg_replace() wären es zwei Zeilen weniger, aber nicht unbedingt einfacher zu verstehen.

              dedlfix.

            2. Hallo Meowsalot,

              hab es jetzt so gelöst. War dieses der richtig weg oder hätte es noch eine einfacheren gegeben?

              Vielleicht. Ohne Regex

              $str = 'ich78bin_einzwei_5tg7g_zu_ein';
              $str = strstr(substr(strstr($str,'_'),1),'_',true);
              echo $str; 
              
              

              oder auch mit explode, ach gibt so viele Möglichkeiten…

              Gruss
              Henry

              1. hallo

                Hallo Meowsalot,

                hab es jetzt so gelöst. War dieses der richtig weg oder hätte es noch eine einfacheren gegeben?

                Vielleicht. Ohne Regex

                $str = 'ich78bin_einzwei_5tg7g_zu_ein';
                $str = strstr(substr(strstr($str,'_'),1),'_',true);
                echo $str; 
                
                

                oder auch mit explode, ach gibt so viele Möglichkeiten…

                Also in Perl wäre re schon bequemer. Ich kam auf dies:

                substr($str, index($str,"_")+1, index($str,"_", index($str,"_")+1 ) - index($str,"_") -1 )
                

                gg

                --
                Neu im Forum! Signaturen kann man ausblenden!
              2. Tach!

                oder auch mit explode, ach gibt so viele Möglichkeiten…

                Stimmt auch wieder, in dem einfachen Fall für den ersten Teil: explode('_', $str)[1].

                dedlfix.

    2. Hallo dedlfix,

      /_([^_])_/ das wäre der Ausdruck dazu. Im Paramter $matches von preg_match() findest du sowohl die gesamte Fundstelle als auch die Gruppe. Am besten mit print_r() oder var_dump() anschauen.

      vielen Dank für deine Erklärung, meine Idee war danach folgende

      preg_match('/_([^_])_/', 'ich78bin_einzwei_5tg7g_zu_ein', $matches);
      var_dump($matches);
      echo "<br>";
      print_r($matches);
      

      leider halte ich bei der Ausgabe nur ein

      array(0) { }
      Array ( )

      Siehst du den Fehler?

      Bis bald!
      Meowsalot (Bernd)

  2. hallo

    Hallo,

    ich habe eine Bezeichnung z.B.

    ich78bin_einzwei_5tg7g_zu_ein

    jetzt möchte ich alles ausschneiden was zwischen dem ersten und dem zweiten _ ist.

    Das ist jetzt Perl Syntax, aber das sollte eigentlich protierbar sein.

    Dieser Wert soll dann in einer Variable gespeichert werden.

    if( "ich78bin_einzwei_5tg7g_zu_ein" =~ /^.*?[_](.+?)[_]/x; ){
       $captured = $1
    }
    

    Im dritten Schritt soll aus dem was in der Variable steht nach den ersten drei Zeichen (Zahlen oder Buchstaben) ein / eingefügt werden.

    my $test = "ich78bin_einzwei_5tg7g_zu_ein";
    my $captured = undef;
    if( $test =~ /^[^_]*[_](.+?)[_]/ ){
       $captured = $1;
       if( $captured =~ /^(.{3})(.*)$/ ){
         $captured = sprintf("%s/%s", $1, $2 );
       }
    }
    
    • [_] steht für undercore char
    • [^_] steht für not underscore char
    • /^ $/ verankert das pattern am Start und Ende des teststrings
    --
    Neu im Forum! Signaturen kann man ausblenden!
    1. @@beatovich

      if( "ich78bin_einzwei_5tg7g_zu_ein" =~ /^.*?[_](.+?)[_]/x; ){
      

      Was sollen die ? da?

      LLAP 🖖

      --
      „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
      1. hallo

        @@beatovich

        if( "ich78bin_einzwei_5tg7g_zu_ein" =~ /^.*?[_](.+?)[_]/x; ){
        

        Was sollen die ? da?

        • .* nennt man greedy capture
        • .*? nennt man sparsames capture.

        Du willst sicherstellen, dass nicht ein _ übersprungen wird.

        In der zweiten Fassung habe ich es geändert zu

        if( $test =~ /^[^_]*[_](.+?)[_]/ ){
        

        weil es hoffentlich klarer ist.

        LLAP 🖖

        „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann

        --
        Neu im Forum! Signaturen kann man ausblenden!
      2. Tach!

        if( "ich78bin_einzwei_5tg7g_zu_ein" =~ /^.*?[_](.+?)[_]/x; ){
        

        Was sollen die ? da?

        Abgesehen davon, dass der Anfang ^.*? nicht benötigt wird, ebensowenig die []-Klammern um die _, braucht es das ? innerhalb der Gruppe um die Gierigkeit wegzubekommen. Sonst wird alles bis zum letzten _ gefunden.

        dedlfix.

        1. hallo

          Abgesehen davon, dass der Anfang ^.*? nicht benötigt wird, ebensowenig die []-Klammern um die _, braucht es das ? innerhalb der Gruppe um die Gierigkeit wegzubekommen. Sonst wird alles bis zum letzten _ gefunden.

          Die Anforderng war vom ersten _ bis zum zweiten _ Nicht vone einem _ zum nächsten _

          Ich denke meine zweite Fassung macht das klarer.

          Dass ich auch Einzelzeichen in Zeichengruppen definiere ist meine persönliche Gewohnheit. Es kann das Lesen erleichtern.

          --
          Neu im Forum! Signaturen kann man ausblenden!
          1. Tach!

            Abgesehen davon, dass der Anfang ^.*? nicht benötigt wird, ebensowenig die []-Klammern um die _, braucht es das ? innerhalb der Gruppe um die Gierigkeit wegzubekommen. Sonst wird alles bis zum letzten _ gefunden.

            Die Anforderng war vom ersten _ bis zum zweiten _ Nicht vone einem _ zum nächsten _

            Ich denke meine zweite Fassung macht das klarer.

            preg_match() findet sowieso nur die erste Stelle. Für alle Stellen muss man preg_match_all() verwenden. Auch in Javascript arbeiten die Ausdrücke so, solange man nicht mit dem g(lobal)-Modifier daherkommt. Müsste auch für Perl gelten.

            dedlfix.

            1. hallo

              Tach!

              Abgesehen davon, dass der Anfang ^.*? nicht benötigt wird, ebensowenig die []-Klammern um die _, braucht es das ? innerhalb der Gruppe um die Gierigkeit wegzubekommen. Sonst wird alles bis zum letzten _ gefunden.

              Die Anforderng war vom ersten _ bis zum zweiten _ Nicht vone einem _ zum nächsten _

              Ich denke meine zweite Fassung macht das klarer.

              preg_match() findet sowieso nur die erste Stelle. Für alle Stellen muss man preg_match_all() verwenden. Auch in Javascript arbeiten die Ausdrücke so, solange man nicht mit dem g(lobal)-Modifier daherkommt. Müsste auch für Perl gelten.

              Aber hier gehts nicht darum, ein oder alle Exemplare zu matchen, sondern welches.

              $test = "rew_tetteq_rewqt_tewqt_teq";

              --
              Neu im Forum! Signaturen kann man ausblenden!
              1. Tach!

                preg_match() findet sowieso nur die erste Stelle. Für alle Stellen muss man preg_match_all() verwenden. Auch in Javascript arbeiten die Ausdrücke so, solange man nicht mit dem g(lobal)-Modifier daherkommt. Müsste auch für Perl gelten.

                Aber hier gehts nicht darum, ein oder alle Exemplare zu matchen, sondern welches.

                $test = "rew_tetteq_rewqt_tewqt_teq";

                Ja, das ist ohne den Vorsatz gegeben, denn es geht nicht um irgendeine Stelle, sondern die erste.

                dedlfix.

  3. Hallo,

    Wie gehe ich da am besten vor?

    Meine Idee wäre preg_match allerdings verstehe ich ehrlich gesagt die suchmuster nicht wirklich.

    Einfach mal machen:

    $s = "ich78bin_einzwei_5tg7g_zu_ein";
    preg_match("/_(.*?)_/", $s, $r);
    
    # Gucken was dabei rauskommt
    print_r($r);
    

    Im Detail. Das Ist zwar für Perl, aber RE's sind plattformübergreifend.

    MfG

  4. Hallo Meowsalot,

    Suchmuster lernt man kennen, indem man die Doku studiert und damit experimentiert. Es braucht auch etwas Geduld; Regexe gehören zu den sperrigeren Lerninhalten.

    Regex-Portal im Self-WIKI

    Mein Lieblings-Experimentierkasten - kann Suchen und auch Ersetzen

    Rechts auf dieser Seite findest Du eine Aufschlüsselung, wie er dein Pattern verstanden hat, und auch eine Kurzreferenz über die verwendbaren Zeichen.

    Dein Pattern könnte übrigens auch so aussehen: /_([^_]{0;3})([^_]*)_/. Dann hat dein $matches-Array drei Einträge: den kompletten Match, die ersten drei Zeichen zwischen den _, und den Rest. Ich nehm's mal auseinander:

    _ | Findet ein _
    (...) | Runde Klammern bilden eine Match-Gruppe, d.h. einen Eintrag im $matches Array [^] | Eckige Klammern bilden eine Menge aus Zeichen, von denen eins vorkommen muss. [a-z] findet genau einen Kleinbuchstaben. Das ^ vorneweg negiert die Menge, hier also „alles außer “ ...{0;3} | {n;m} ist, genau wie *, ein Quantifizierer - eine Mengenangabe. * bedeutet „kommt null bis beliebig oft mal vor“, mit {n,m} kannst Du Unter- und Obergrenze vorgeben. ([^]{0;3}) | ist also eine Folge beliebiger Zeichen, die nicht _ sind, die 0-3 Zeichen umfasst. ([^]*) | Bildet eine Matchgruppe, die 0-n Zeichen enthält, die nicht _ sind _ | Und noch ein _

    Durch die Aufteilung in 0-3 Zeichen und dann beliebig viele Zeichen brauchst Du kein substr mehr.

    Rolf

    --
    sumpsi - posui - clusi