frankx: regexp und whitespace ("s" nach dem Ausdruck) und preg_match_all

Hellihello

in meinem Tests habe ich folgendes gefunden:

  
$subject = "<body>abcde  
<div>  
</div>  
ccfa  
sdasdfdef  
</body>";  
$pattern = '°<body>.*</body>°s';  
preg_match_all($pattern, $subject, $matches);  
print_r($matches);  

bringt:

Array
(
    [0] => Array
        (
            [0] => <body>abcde
<div>
</div>
ccfa
sdasdfdef
</body>
        )

)

Ich stelle fest, dass das "s" hinter dem eigentlichen Ausdruck bewirkt, dass im ".*" auch alle whitespaces (Absätze, vermutlich \n und \r sowie  Leerzeichen) mitgenommen werden. Ich finde aber keine Doku für diese Syntax.

Dann noch gleich die Frage, warum die Ausgabe ein verschachteltes Array ist?

$matches[0][0] ist ja der Bodyinhalt.

Dank und Gruß,

frankx

--
tryin to multitain  - Globus = Planet != Welt
  1. Hi,

    Ich stelle fest, dass das "s" hinter dem eigentlichen Ausdruck bewirkt, dass im ".*" auch alle whitespaces (Absätze, vermutlich \n und \r sowie  Leerzeichen) mitgenommen werden. Ich finde aber keine Doku für diese Syntax.

    Für die Modifier siehe http://de2.php.net/manual/en/reference.pcre.pattern.modifiers.php. s schaltet für . das Matchen von Newlines ein.

    Dann noch gleich die Frage, warum die Ausgabe ein verschachteltes Array ist?

    Das äußere Array ist für die verschiedenen Treffer bei preg_match_all (also für das "all").
    Das innere Array enthält auf Position 0 den gesamten getroffenen Text und in den weiteren Positionen die durch die capturing parentheses getroffenen Texte. (wobei sich über flags das Verhalten ändern läßt).

    Siehe auch http://de2.php.net/manual/en/function.preg-match-all.php

    $matches[0][0] ist ja der Bodyinhalt.

    Nein, es ist mehr, die body tags sind ja auch dabei.

    cu,
    Andreas

    --
    Warum nennt sich Andreas hier MudGuard?
    O o ostern ...
    Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
    1. Hellihello Andreas,

      merci!

      Für die Modifier siehe http://de2.php.net/manual/en/reference.pcre.pattern.modifiers.php. s schaltet für . das Matchen von Newlines ein.

      Also PHP-"Proprietär"?

      "This modifier is equivalent to Perl's /s modifier. A negative class such as [^a] always matches a newline character, independent of the setting of this modifier."

      Sollte/kann mensch das gewünschte auch anders ausdrücken, also im PREG-Style bleiben?

      Das äußere Array ist für die verschiedenen Treffer bei preg_match_all (also für das "all").
      Das innere Array enthält auf Position 0 den gesamten getroffenen Text und in den weiteren Positionen die durch die capturing parentheses getroffenen Texte. (wobei sich über flags das Verhalten ändern läßt).

      Siehe auch http://de2.php.net/manual/en/function.preg-match-all.php

      $matches[0][0] ist ja der Bodyinhalt.

      Nein, es ist mehr, die body tags sind ja auch dabei.

      Und wenn ich (.*?) setze, bringt es mir in $matches[0][0] den kompletten Suchstring, in $matches[0][1] den Inhalt der (ersten) Klammer.

      Oder mit PREG_SET_ORDER bekomme ich erst ein Array aller matches komplettmatches, und ein zweites mit den Ergebnissen in der Klammer. Fein. Das ist wohl geeigneter, wenn ich nur die Inhalte der Klammer haben will (zB. zum ersetzen). In welchem Bedarfsfalle bräuchte man denn PREG_OFFSET_CAPTURE?

      Dank und Gruß,

      frankx

      --
      tryin to multitain  - Globus = Planet != Welt
      1. (Hallo|Hi(ho)|Tag) frankx,

        Für die Modifier siehe http://de2.php.net/manual/en/reference.pcre.pattern.modifiers.php. s schaltet für . das Matchen von Newlines ein.

        Also PHP-"Proprietär"?

        Wieso proprietär?

        "This modifier is equivalent to Perl's /s modifier. A negative class such as [^a] always matches a newline character, independent of the setting of this modifier."

        Sollte/kann mensch das gewünschte auch anders ausdrücken,

        Klar.

        also im PREG-Style bleiben?

        Was ist PREG-Style?

        In welchem Bedarfsfalle bräuchte man denn PREG_OFFSET_CAPTURE?

        Wenn du die Byte-Offsets der gefundenen Teilstrings im PHP-Script weiterverarbeiten möchtest. Das sollte aber alles im PHP-Handbuch stehen.

        MffG
        EisFuX

        1. Hellihello !Fire[fuxx];

          (Hallo|Hi(ho)|Tag) frankx,

          Für die Modifier siehe http://de2.php.net/manual/en/reference.pcre.pattern.modifiers.php. s schaltet für . das Matchen von Newlines ein.

          Also PHP-"Proprietär"?
          Wieso proprietär?

          Ich habe mich wohl etwas wirr ausgedrückt. Ich dachte, preg steht für perl-regular-expression. ich gehe davon aus, dass es eine "reine" per-syntax gibt. ich hatte es jetzt so verstanden, das das hintenangestellte "s" /<body>.*</body>/s eine PHP-Erfindung ist.

          "This modifier is equivalent to Perl's /s modifier. A negative class such as [^a] always matches a newline character, independent of the setting of this modifier."

          Sollte/kann mensch das gewünschte auch anders ausdrücken,
          Klar.

          Aber wie finden ich denn zB. den body anders finden als mi

          $pattern = '°<body>(.*?)</body>°s';  
          
          

          also im PREG-Style bleiben?
          Was ist PREG-Style?

          (s.o.)

          In welchem Bedarfsfalle bräuchte man denn PREG_OFFSET_CAPTURE?
          Wenn du die Byte-Offsets der gefundenen Teilstrings im PHP-Script weiterverarbeiten möchtest. Das sollte aber alles im PHP-Handbuch stehen.

          Byte-Offset ist die Stelle im String, oder? Mir war jetzt kein Szenario klar, wo man sowas brauchen könnte, vielleicht, wenn man da was einfügen will?

          Dank und Gruß,

          frankx

          --
          tryin to multitain  - Globus = Planet != Welt
          1. gudn tach!

            ich hatte es jetzt so verstanden, das das hintenangestellte "s" /<body>.*</body>/s eine PHP-Erfindung ist.

            nein, in php wurde das aus perl uebernommen.

            Sollte/kann mensch das gewünschte auch anders ausdrücken,

            [offtopic]
            ja, kann man. oder meintest du nur frauen? im duden steht naemlich unter "Mensch":

            Mensch, das; -[e]s, -er [schon mhd. mensch (Neutr.) = der Mensch] (landsch., meist abwertend): weibliche Person, Frau.

            ;-)
            [/offtopic]

            Aber wie finden ich denn zB. den body anders finden als mi

            $pattern = '°<body>(.*?)</body>°s';

              
            statt . mit s-modifier kann man z.b. sowas schreiben wie  
              
             (?:.|\n|\r)  
              
            
            > > > In welchem Bedarfsfalle bräuchte man denn PREG\_OFFSET\_CAPTURE?  
            > > Wenn du die Byte-Offsets der gefundenen Teilstrings im PHP-Script weiterverarbeiten möchtest. Das sollte aber alles im PHP-Handbuch stehen.  
            >   
            > Byte-Offset ist die Stelle im String, oder?  
              
            mehr (ansi) oder weniger (utf8). siehe kommentare im manual.  
              
            
            > Mir war jetzt kein Szenario klar, wo man sowas brauchen könnte, vielleicht, wenn man da was einfügen will?  
              
            ja, z.b.  
              
            prost  
            seth
            
            1. Hellihello seth,

              [offtopic]
              ja, kann man. oder meintest du nur frauen? im duden steht naemlich unter "Mensch":

              Mensch, das; -[e]s, -er [schon mhd. mensch (Neutr.) = der Mensch] (landsch., meist abwertend): weibliche Person, Frau.

              ;-)
              [/offtopic]

              mensch, was soll mir das jetzt sagen (;-)?

              statt . mit s-modifier kann man z.b. sowas schreiben wie

              (?:.|\n|\r)

              Mensch, das heißt im letzten Teil wohl alles oder chr(10)LF  oder chr(13) CR, aber auch " " leerzeichen?

              was aber ist das "?:"? Aber wenn das andere auch Perl-Syntax ist, ists doch fein.

              In welchem Bedarfsfalle bräuchte man denn PREG_OFFSET_CAPTURE?
              Wenn du die Byte-Offsets der gefundenen Teilstrings im PHP-Script weiterverarbeiten möchtest. Das sollte aber alles im PHP-Handbuch stehen.

              prost

              mensch ja, wohl bekomms,

              Dank und Gruß,

              frankx

              --
              tryin to multitain  - Globus = Planet != Welt
              1. (Hallo|Hi(ho)|Tag) frankx,

                Die preg-Funktionen von PHP basieren auf PCRE -- einem möglichst kompatiblen Nachbau der RegExp-Funktionalität von Perl. In PHP waren sie früher tatsächlich nicht "Standard", sondern die POSIX-kompatiblen RegExp-Funktionen. Mittlerweile ist das aber genau umgekehrt.

                ... und, ähmm, ich weiß nicht, ob Sie's schon wussten, aber es gibt da zwei schöne lange Artikel im PHP-Handbuch:
                http://de3.php.net/manual/de/reference.pcre.pattern.syntax.php
                http://de3.php.net/manual/de/reference.pcre.pattern.modifiers.php
                Die sind zwar in Englisch verfasst und nicht besonders übersichtlich, erklären aber alles Wissenswerte und auch die Spezialitäten und Besonderheiten der PCRE.

                Und im ersten der beiden Texte steht zur Bedeutung von ".":

                match any character except newline (by default)

                Woraus man rasiermesserscharf schlussfolgern könnte, wie man einen "matche"-alles-Ausdruck mit der Hilfe des "." zusammenbasteln müsste:
                (.|\n)
                Das \r kann man weglassen, denn es wird ja laut Anleitung nicht benötigt, und zumindest bei der PCRE-Extension, die beim von mir getestesten PHP 5.2.2 dabei ist, stimmt das auch.

                Eine Zeichenklasse [.\n] passt übrigens nicht, denn dazu meint der Text

                Dot has no special meaning in a character class.

                In Zeichenklassen passt der "." also nur auf gewöhnliche Punkte.

                Höchstens [\x0-\xff] wäre noch denkbar. Das verzichtet auf die runden Klammern und funktioniert zumindest mit Nicht-UTF-8-Strings wie gewünscht.

                (?:.|\n|\r)
                Mensch, das heißt im letzten Teil wohl alles oder chr(10)LF  oder chr(13) CR, aber auch " " leerzeichen?

                Jain, es heißt wortwörtlich:
                * ein beliebiges Zeichen außer New Line "[^\xa]" oder
                * New Line "\xa" oder
                * Carriage Return "\xd"

                was aber ist das "?:"? Aber wenn das andere auch Perl-Syntax ist, ists doch fein.

                AFAIK ist das alles Perl-Syntax.

                Das ist ein "noncapturing subpattern", sprich: ein Teil-Suchmuster, dessen Suchergebnis nicht im Treffer-Array von preg_match() oder preg_match_all() auftaucht.

                MffG
                EisFuX

            2. /(Hallo|Hi(ho)|(gudn\s)Ta(g|ch))/i seth,

              Sollte/kann mensch das gewünschte auch anders ausdrücken,

              [offtopic]
              ja, kann man. oder meintest du nur frauen? im duden steht naemlich unter "Mensch":

              Mensch, das; -[e]s, -er [schon mhd. mensch (Neutr.) = der Mensch] (landsch., meist abwertend): weibliche Person, Frau.

              ;-)
              [/offtopic]

              Ich bevorzuge ja diese Menschendefinition. Die ist eher aufwertend.
              ;-)

              statt . mit s-modifier kann man z.b. sowas schreiben wie

              (?:.|\n|\r)

              ... oder doch eher
              [.\r\n]
              ?

              Wäre das nicht einfacher? ... und schneller?

              MffG
              EisFuX

              --
              Of course it doesn't work, but look how fast it is!
              1. Hi,

                (?:.|\n|\r)
                ... oder doch eher
                [.\r\n]
                ?
                Wäre das nicht einfacher? ... und schneller?

                Es ist vor allem etwas vollkommen anderes.
                (?:.|\n|\r) matcht ein beliebiges Zeichen, so wie es auch (?:.|[\r\n]) tut.

                [.\r\n] dagegen matcht ein Carriage Return oder ein Line-Feed oder einen Punkt.

                cu,
                Andreas

                --
                Warum nennt sich Andreas hier MudGuard?
                O o ostern ...
                Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                1. (Hallo|Hi(ho)|Tag) MudGuard,

                  (?:.|\n|\r)
                  ... oder doch eher
                  [.\r\n]
                  ?
                  Wäre das nicht einfacher? ... und schneller?

                  Es ist vor allem etwas vollkommen anderes.
                  (?:.|\n|\r) matcht ein beliebiges Zeichen, so wie es auch (?:.|[\r\n]) tut.

                  [.\r\n] dagegen matcht ein Carriage Return oder ein Line-Feed oder einen Punkt.

                  Mist! Ich wollte mich gerade selbst korrigieren, aber du warst schneller ...

                  Und weil wir gerade bei schneller sind: Am schnellsten ist wohl /[\x0-\xff]/, noch vor /./s. Zumindest legt das ein kleiner Test nahe, den ich mal kurz gemacht habe. Die Ergebnisse für jeweils 10000 preg_match_all()-Aufrufe:

                  411 ms -- for /[\x0-\xff]/
                  993 ms -- for /./s
                  1603 ms -- for /(.|\n)/
                  1407 ms -- for /(?:.|\n)/

                    
                  // runtime() holt das Ergebnis von microtime() als float  
                  // oder die Zeitdifferenz zum übergebenen Wert als float  
                  // $string enthaelt auch "\r" und "\n"  
                  $max = 1000;  
                    
                  $elapsed = runtime();  
                  for ($i = $max; $i; $i--) {  
                    preg_match_all('/./s', $string, $matches);  
                    ... // das ganze hier 10 mal  
                    preg_match_all('/./s', $string, $matches);  
                  }  
                  $elapsed = runtime($elapsed);  
                  debug_writefln('%d ms -- for /./s', $elapsed * 1000);  
                  
                  

                  MffG
                  EisFuX

                  1. Hi,

                    Und weil wir gerade bei schneller sind: Am schnellsten ist wohl /[\x0-\xff]/,

                    Das ist aber auch was anderes (zumindest in Perl, inwieweit PHP mit Unicode umgehen kann, weiß ich nicht).
                    Unicode kennt Zeichen jenseits von \xff - die mit (?:.|[\r\n]) gematcht werden, mit Deinem Konstrukt aber nicht.

                    cu,
                    Andreas

                    --
                    Warum nennt sich Andreas hier MudGuard?
                    O o ostern ...
                    Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                    1. (Hallo|Hi(ho)|Tag) MudGuard,

                      Und weil wir gerade bei schneller sind: Am schnellsten ist wohl /[\x0-\xff]/,

                      Ganz so schnell war es dann doch nicht. Ich hab mich schon gewundert ob der großen Unterschiede. Mir ist wohl ein "x"
                      beim RegEx-Zusammenbasteln abhanden gekommen. Also wurde nur nach "[\x0-\ff]" und nicht nach "[\x0-\xff]" gesucht ...
                      Hier mal aktualisierte Zeiten:

                      1551 ms -- for /./s
                      1773 ms -- for /[\x0-\xff]/
                      2084 ms -- for /[\x0-\x{10ffff}]/u
                      3001 ms -- for /(.|\n)/
                      2123 ms -- for /(?:.|\n)/

                      Man sieht immer noch deutlich, dass der Punkt alleine am schnellsten ist. Die UTF-benutzende Zeichenklasse ist aber
                      immer noch schneller als die Konstruktion mit den runden Klammern und liegt mit dem "noncapturing subpattern" etwa
                      gleichauf.

                      Das ist aber auch was anderes (zumindest in Perl, inwieweit PHP mit Unicode umgehen kann, weiß ich nicht).

                      Serienmäßig hat die Version 5.2.x mit Unicode nicht viel am Hut. Die PCRE-Befehle können aber (je nach Version) mit
                      UTF-8 umgehen, wenn der entsprechende Modifikator "/u" gesetzt wurde.

                      Unicode kennt Zeichen jenseits von \xff - die mit (?:.|[\r\n]) gematcht werden, mit Deinem Konstrukt aber nicht.

                      Allerdings war im OP auch nicht explizit die Rede von Unicode, und für die gestellte Aufgabe
                      ("fange alles zwischen <body> und </body>") und in UTF-8 kodiertes HTML sollte es trotzdem ausreichen.

                      MffG
                      EisFuX