Regex quantifier bei komplexen Ausdruck

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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)
    }

    Comment


    • #3
      Originally posted by MissPiggy View Post
      ...
      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*

      Originally posted by MissPiggy View Post
      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.
      Last edited by fireweasel; 04-06-2014, 10:01.
      Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

      Comment

      Working...
      X