Tobi: RegExp

Sers Leute,

also ich hab mich in letzter Zeit etwas mit dem RegExp Objekt beschäftigt
( trotzdem noch Anfänger =D ). Doch irgendwie hab ich ein kleines Problem bei dem ihr mir hoffentlich helfen könnt ;)

Beispielcode:

  
window.onload = function() {  
var x = new RegExp("\\d\\d", "g");  
alert( x.test("20"));  
alert( x.test("40"));  
}  

Also das Problem ist bei mir das ich den regulären Ausdruck bei zwei Zeichenketten anwenden will. Aber wie bei dem Beispielcode zu sehen ist, kommt bei dem zweiten test nur false. Bei der Funktion exec ist ähnlich :<

So jetzt mein Frage kann man einen regulären Ausdruck auf zwei Zeichenketten anwenden und wie? Oder stellt ich mich einfach nur blöd an ( bzw. hab etwas übersehen).

Mfg Tobi

  1. Hi,

    window.onload = function() {
    var x = new RegExp("\d\d", "g");
    alert( x.test("20"));
    alert( x.test("40"));
    }

    
    >   
    > Also das Problem ist bei mir das ich den regulären Ausdruck bei zwei Zeichenketten anwenden will. Aber wie bei dem Beispielcode zu sehen ist, kommt bei dem zweiten test nur false. Bei der Funktion exec ist ähnlich :<  
      
    Das ist eine Eigenart des RegExp-Objektes, dass dieses unter gewissen Umständen nach einmaliger Anwendung „verbraucht“ ist. (Genauer kann ich dir das auch nicht erklären, da müssen wir auf Mathias oder jemand anderen warten.)  
      
    
    > So jetzt mein Frage kann man einen regulären Ausdruck auf zwei Zeichenketten anwenden und wie?  
      
    Du kannst die compile-Methode nutzen - die ist sowieso empfehlenswert, weil sie den Ausdruck in ein Format „kompiliert“, mit dem er bei mehrfacher Ausführung m.W. schneller ausgewertet werden kann.  
      
    ~~~javascript
    var x = new RegExp("\\d\\d", "g");  
    x.compile();  
    alert( x.test("20"));  
    alert( x.test("40"));
    

    MfG ChrisB

    --
    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    1. Du kannst die compile-Methode nutzen - die ist sowieso empfehlenswert, weil sie den Ausdruck in ein Format „kompiliert“, mit dem er bei mehrfacher Ausführung m.W. schneller ausgewertet werden kann.

      var x = new RegExp("\d\d", "g");

      x.compile();
      alert( x.test("20"));
      alert( x.test("40"));

      
      >   
      > MfG ChrisB  
      >   
        
      Ok hab jetzt deinen Vorschlag mal ausprobiert. Doch irgendwie klappts immernoch nicht so ganz:  
        
      ~~~javascript
        
      window.onload = function() {  
      var x = new RegExp("\\d\\d", "g");  
      x.compile();  
      alert( x.test("20"));  
      alert( x.test("zz"));  
      }  
      
      

      Naja bei dem Beispeil kommt nämlich zweimal true, obwohl es eigentlich beim zweiten test() ein false geben müsste.

      1. Hi,

        window.onload = function() {
        var x = new RegExp("\d\d", "g");
        x.compile();
        alert( x.test("20"));
        alert( x.test("zz"));
        }

        
        >   
        > Naja bei dem Beispeil kommt nämlich zweimal true, obwohl es eigentlich beim zweiten test() ein false geben müsste.  
          
        Stimmt, compile lässt sich in der Form nicht nutzen, sondern muss den Ausdruck (in String-Form) und ggf. die Flags als Parameter übergeben bekommen:  
          
        ~~~javascript
        var x = new RegExp;  
        x.compile("\\d\\d", "g");  
        alert( x.test("20"));  
        alert( x.test("zz"));
        

        Btw., wozu immer das window.onload in deinen Beispielen? JS-Code, der nicht auf das DOM zugreift, kannst du auch direkt ausführen lassen. Und du musst zum testen nicht mal ein HTML-Dokument erstellen, sondern kannst bspw. http://jconsole.com/ nutzen.

        MfG ChrisB

        --
        RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    2. Also das Problem ist bei mir das ich den regulären Ausdruck bei zwei Zeichenketten anwenden will. Aber wie bei dem Beispielcode zu sehen ist, kommt bei dem zweiten test nur false. Bei der Funktion exec ist ähnlich :<

      test() ruft intern exec() auf.

      Das ist eine Eigenart des RegExp-Objektes, dass dieses unter gewissen Umständen nach einmaliger Anwendung „verbraucht“ ist.

      Das kann man so nicht sagen.

      (Genauer kann ich dir das auch nicht erklären, da müssen wir auf Mathias oder jemand anderen warten.)

      Wir haben es kurzerhand in SELFHTML reingeschrieben, damit ich nicht immer anwesend sein muss. ;)

      http://de.selfhtml.org/javascript/objekte/regexp.htm#exec
      Siehe zweites Beispiel

      Wenn man mehrfach exec() bei einem regulären Ausdruck mit dem global-Modifier aufruft, dann kann man damit alle Treffer durchlaufen. Der Vorteil gegenüber String.prototype.match() ist, dass man komplexen Ausdrücken mit Klammern für Teiltreffer Zugriff auf die Teiltreffer jedes der Treffer hat.

      Dieses Durchlaufen funktioniert so: Beim exec() speichert JS die Position, an der der erste Treffer gefunden hat, und sucht beim nächsten exec()-Aufruf erst ab dieser Position weiter. Die Position wird in der Eigenschaft lastIndex gespeichert (vgl. EC5).

      Erst wenn die Suche man am Stringende angelangt ist, wird lastIndex auf 0 zurückgesetzt:

      r = /\d/g

      /\d/g

      r.exec("a1b2d3e4")

      ["1"]

      r.lastIndex

      2

      r.exec("a1b2d3e4")

      ["2"]

      r.lastIndex

      4

      r.exec("a1b2d3e4")

      ["3"]

      r.lastIndex

      6

      r.exec("a1b2d3e4")

      ["4"]

      r.lastIndex

      8

      r.exec("a1b2d3e4")

      null
      // Am Ende angelangt, daher wurde lastIndex zurückgesetzt:

      r.lastIndex

      0

      r.exec("a1b2d3e4")

      ["1"]
      // Jetzt gehts wieder von vorne los

      r.lastIndex

      2

      usw. ad infinitum

      Sinn macht das natürlich nur, wenn man exec() immer wieder auf *denselben* String anwendet, bis kein Treffer mehr gefunden ist, weil die Suche am Stringende angelangt ist (wie im obigen Beispiel). Wenn man den global-Flag setzt und exec() ständig auf unterschiedliche Strings anwendet, kommt Quatsch heraus:

      r.exec("20")

      ["20"]

      r.lastIndex

      2

      r.exec("40")

      null

      r.lastIndex

      0
      // Hier wurde zurückgesetzt, also wird beim dritten exec() wieder von vorne gesucht:

      r.exec("20")

      ["20"]

      r.lastIndex

      2

      r.exec("40")

      null

      r.lastIndex

      0
      usw.

      Diese RegExp kann man also durchaus mehrfach verwenden und auf unterschiedliche Strings, aber sie sucht eben nicht immer von vorne, sondern merkt sich die Position von der Anwendung auf den jeweils letzten String.

      So jetzt mein Frage kann man einen regulären Ausdruck auf zwei Zeichenketten anwenden und wie?

      Lass einfach den global-Flag weg.

      r = /\d\d/

      /\d\d/

      r.test("20")

      true

      r.test("40")

      true

      Funktioniert wie erwartet.

      Du kannst die compile-Methode nutzen - die ist sowieso empfehlenswert, weil sie den Ausdruck in ein Format „kompiliert“, mit dem er bei mehrfacher Ausführung m.W. schneller ausgewertet werden kann.

      Das ist meines Wissens nicht nötig. Die neueren JS-Engines optimieren selbstständig. compile() war m.W. auch nie standardisiert, sondern eine Netscape-3-Spezialität. Netscape JavaScript 1.5 (Netscape 4) hat compile() schon als deprecated erklärt. (Ich find überhaupt nichts neueres dazu.)

      Mathias

  2. @@Tobi:

    nuqneH

    var x = new RegExp("\d\d", "g");

    Warum mit Konstruktor und nicht einfach so?
    var x = /\d\d/g;

    Ohne g-Modifier gibt es beide Male true (in beiden Varianten). Womit das zusammenhängt, kann ich dir jetzt auch nicht sagen.

    Statt:

    alert( x.test("20"));
    alert( x.test("40"));

    andersrum:

    alert(!!"20".match(x));  
    alert(!!"40".match(x));
    

    dann gibt es auch mit g-Modifier beide Male true. Und intuitiver finde ich die Variante auch.

    Qapla'

    --
    Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
    (Mark Twain)
    1. Statt:

      alert( x.test("20"));
      alert( x.test("40"));

      andersrum:

      alert(!!"20".match(x));

      alert(!!"40".match(x));

      
      >   
      > dann gibt es auch mit g-Modifier beide Male true.  
        
      Der g-Modifier macht hier keinen Sinn, daher sollte man ihn weglassen, dann verhalten sich beide Methoden gleich.  
        
      
      > Und [intuitiver](http://forum.de.selfhtml.org/archiv/2008/3/t167751/#m1094554) finde ich die Variante auch.  
        
      Geschmackssache. test() gibt halt direkt einen Boolean zurück, während match() den Treffer samt Teiltreffer zurückgibt. Genauer, einen Array (mit mindestens einem Eintrag) oder null. In der Anwendung macht das aber keinen Unterschied, man kann getrost if (string.match(regexp)) schreiben, da ein Array (auch ein leerer) immer truthy ist und null falsy.  
        
      Mathias
      
      1. Geschmackssache. test() gibt halt direkt einen Boolean zurück, während match() den Treffer samt Teiltreffer zurückgibt. Genauer, einen Array (mit mindestens einem Eintrag) oder null. In der Anwendung macht das aber keinen Unterschied, man kann getrost if (string.match(regexp)) schreiben, da ein Array (auch ein leerer) immer truthy ist und null falsy.

        Das Array muss aber auch erst befüllt werden (ok, die Werte sind im RegExp-Objekt bereits vorhanden). Es wäre dennoch mal interessant, festzustellen, ob .match mehr Speicher benötigt als .test, insbesondere für die mobilen Anwendungen, bei denen Speicher ohnehin Mangelware ist.

        Gruß, LX

        --
        RFC 1925, Satz 2: Egal, wie fest man schiebt, ganz gleich, wie hoch die Priorität ist, man kann die Lichtgeschwindigkeit nicht erhöhen.
        1. Das Array muss aber auch erst befüllt werden (ok, die Werte sind im RegExp-Objekt bereits vorhanden).

          Gemäß ECMAScript rufen sowohl test als auch match intern immer exec aus. Es steht den JS-Engine-Programmierern natürlich frei, das zu optimieren. exec muss beim Aufruf über test ja nicht wirklich die Treffer speichern und einen Array bauen.

          Es wäre dennoch mal interessant, festzustellen, ob .match mehr Speicher benötigt als .test, insbesondere für die mobilen Anwendungen, bei denen Speicher ohnehin Mangelware ist.

          Speicher weiß ich nicht, aber test ist tatsächlich das schnellste. Testcase dazu:
          http://jsperf.com/regexp-performance

          Mathias

          1. Also hab mir mal alles durchgelesen und wollt mich nur mal für die großartige Hilfe bedanken! ;)

            Mfg Tobi