Regex quantifier bei komplexen Ausdruck

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Regex quantifier bei komplexen Ausdruck

    Aus einem String mit dem Aufbau
    Irgendeinbegriff -> term_id(2),term_id(15),term_id(32) soll der Begriff vor dem Pfeil sowie die jeweiligen id's ausgelesen werden, von denen ein bis drei Vorkommen können.
    Wende ich aber einen Quantifier an wird je nach Art nur eine Ziffer als Treffer gewertet, ich brauche aber alle. Wie modifiziere ich den Regex dass er alle als Treffer wertet ?

  • #2
    PHP-Code:
    $string 'Irgendeinbegriff -> term_id(2),term_id(15),term_id(32)';

    $stringParts explode(' -> '$string);

    $term $stringParts[0];
    $ids array_map(
        function (
    $value) {
            return 
    sscanf($value'term_id(%d)')[0];
        },
        
    explode(','$stringParts[1])
    );

    var_dump($term$ids); 
    Code:
    string(16) "Irgendeinbegriff"
    array(3) {
      [0] =>
      int(2)
      [1] =>
      int(15)
      [2] =>
      int(32)
    }

    Kommentar


    • #3
      Zitat von MissPiggy Beitrag anzeigen
      ...
      Code:
      [B][SIZE=2][COLOR=#ffffff] ([COLOR=#000000]+[/COLOR])([COLOR=#000000]+[/COLOR])[/COLOR][COLOR=#33cc33]
      / [COLOR=#993333]____[/COLOR] \
      \ [COLOR=#993333]\__/[/COLOR] /
      <\    />
       \/\/\/[/COLOR][/SIZE][/B]
      *SCNR*

      Zitat von MissPiggy Beitrag anzeigen
      Aus einem String mit dem Aufbau
      Irgendeinbegriff -> term_id(2),term_id(15),term_id(32) soll der Begriff vor dem Pfeil sowie die jeweiligen id's ausgelesen werden, von denen ein bis drei Vorkommen können.
      Wende ich aber einen Quantifier an wird je nach Art nur eine Ziffer als Treffer gewertet, ich brauche aber alle.
      Generell bekommst du mit preg_match() nur den zuletzt gefundenen Teilstring, wenn du ein Subpattern mit Quantifier (> 1) dahinter baust.

      Willst du alle haben, musst du entweder preg_match() mit Offset-Parameter und PREG_OFFSET_CAPTURE-Flag in einer Schleife anwenden; oder du benutzt eine der Funktionen, die wiederholt suchen können: also preg_match_all(), preg_split() oder preg_replace_callback().

      Für preg_split() benötigst du dann aber einen speziell aufs Teilen zugeschnittenen PCRE. Bei preg_replace_callback() muss die Callback-Funktion in eine vorher mitgegebene Variable die Suchergebnisse ablegen. Alles nicht sehr elegant.

      Wie modifiziere ich den Regex dass er alle als Treffer wertet ?
      Du baust so viele optionale Subpattern ein, wie du auslesen willst:
      PHP-Code:
      $pcre '/
          # ...
          .+? term_id\( [0-9]+ \)
          (.+? term_id\( [0-9]+ \))?
          (.+? term_id\( [0-9]+ \))?
      /x'

      Das funktioniert, solange du eine feste Maximalzahl erwartest. Bei einer beliebigen Anzahl an IDs geht das natürlich nicht.

      Allerdings müsste man jetzt bei einer Änderung des Musters immer jedes einzelne Subpattern umbauen. Das lässt sich vermeiden, wenn du "(?(DEFINE) (...))"-Subroutinen benutzt.

      Die haben nur wieder den Nachteil, dass sie sich die Teilstrings nicht merken können, die mit ihrer Hilfe gefunden werden. Deswegen hab ich das Term-ID-Suchmuster geteilt in "(?&open)", Zahl und "(?&close)".

      Außerdem erzeugen Subroutinen im Treffer-Array einige unschöne Einträge (meist leere). Die schneiden wir per array_slice() ab.

      PHP-Code:
      function fw_repeatedpattern($haystack) {
          
      $pcre '/
              (?(DEFINE)(?<open> .+? (?<= term_id\( ) ))
              (?(DEFINE)(?<close> (?= \) ) ))

              \A\s* (.+?) \s*->
              (?&open) ([0-9]+) (?&close) # einmal
              (?: (?&open) ([0-9]+) (?&close) )? # zweimal
              (?: (?&open) ([0-9]+) (?&close) )? # get the idea?

          /xs'
      ;
          
      // skip 5 entries which were generated by the DEFINE-subroutines
          
      return preg_match($pcre$haystack$h) ? array_slice($h5) : null;

      Wesentlich einfacher wird es, wenn du die Arbeit aufteilst: Erst den Teil vor dem "->" mit preg_match() holen, dann die mehrfachen IDs mit preg_match_all() auslesen:

      PHP-Code:
      function fw_pm_pma($haystack) {
          if (!
      preg_match('/\A .+? (?=->)/x'$haystack$h_term)) {
              return 
      null;
          }
          return 
      preg_match_all('/(?<=term_id\() [0-9]+ (?=\))/x'$haystack$h_id)
              ? 
      array_merge($h_term$h_id)
              : 
      null;

      Das funktioniert wie (von dir) gewünscht:
      PHP-Code:
      $tests = array (
          
      // valid
          
      'irgendein begriff -> term_id(1),term_id(22),term_id(333)',
          
      'irgendein begriff -> term_id(1),term_id(22)',
          
      'irgendein begriff -> term_id(1)',
          
      // invalid
          
      'irgendein begriff -> ',
          
      'term_id(2),term_id(3),term_id(32)',
          
      'should not match',
      );

      foreach (
      $tests as $haystack) {
          
      var_dump($haystackfw_pm_pma($haystack));
      }
      foreach (
      $tests as $haystack) {
          
      var_dump($haystackrw_repeatedpattern($haystack));

      ... ohne Warnungen oder Notices bei nicht erkannten oder nicht validen Eingabe-Daten.
      Zuletzt geändert von fireweasel; 04.06.2014, 11:01.
      Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

      Kommentar

      Lädt...
      X