Johnny B.: Frage zu Regex - Datum

Hallo geehrtes Forum,

$datum liegt in folgenden möglichen Formaten vor:

12.03.2010
   03.2010
      2010

Folgende Regexe liefern mir die einzelnen Elemente:

my ( $tag )     = ( $datum =~ /(\d\d)\.\d\d\.\d\d\d\d/ );  
my ( $monat )   = ( $datum =~ /(?:\d\d\.)*?(\d\d)\.\d\d\d\d/ );  
my ( $jahr )    = ( $datum =~ /(?:\d\d\.)*?(?:\d\d\.)*?(\d\d\d\d)/ );  
  
Nun habe ich versucht, die drei zusammenzufassen. Das klappt aber nicht:  
my ( $tag, $monat, $jahr )    = ( $datum =~ /(?:(\d\d\).)*?(?:(\d\d\).)*?(\d\d\d\d)/ );   

Einen Stringteil kann ich mit (?:xyz) gruppieren und mittels angehängtem *? im Falle des Nichtvorhandenseins ignorieren. Ist es möglich, innerhalb eines regulären Ausdrucks auch Variablen nur optional zu matchen?

Besten Gruß
JOhnnY

  1. my ( $tag )     = ( $datum =~ /(\d\d).\d\d.\d\d\d\d/ );

    my ( $monat )   = ( $datum =~ /(?:\d\d.)?(\d\d).\d\d\d\d/ );
    my ( $jahr )    = ( $datum =~ /(?:\d\d.)
    ?(?:\d\d.)*?(\d\d\d\d)/ );

    Nun habe ich versucht, die drei zusammenzufassen. Das klappt aber nicht:
    my ( $tag, $monat, $jahr )    = ( $datum =~ /(?:(\d\d).)?(?:(\d\d).)?(\d\d\d\d)/ );

      
    kaputter Code  
      
    
    > Einen Stringteil kann ich mit (?:xyz) gruppieren und mittels angehängtem \*? im Falle des Nichtvorhandenseins ignorieren. Ist es möglich, innerhalb eines regulären Ausdrucks auch Variablen nur optional zu matchen?  
      
    ()? setzt $1, unabhängig davon, ob der Part vorhanden war.  
      
      
    ~~~perl
    m/  
      (?:  
       (?:  
         (\d\d)\.    # tag  
       )?  
         (\d\d)\.    # monat  
      )?  
         (\d\d\d\d)  # yahr  
    /x and ($day, $mon, $year) = ( $1||'', $2||'', $3 );  
    
    

    mfg Beat

    --
    ><o(((°>           ><o(((°>
       <°)))o><                     ><o(((°>o
    Der Valigator leibt diese Fische
    1. Hallo Beat,

      ()? setzt $1, unabhängig davon, ob der Part vorhanden war.

      ok. cool!

      m/

      (?:
         (?:
           (\d\d).    # tag
         )?
           (\d\d).    # monat
        )?
           (\d\d\d\d)  # yahr
      /x and ($day, $mon, $year) = ( $1||'', $2||'', $3 );

        
      aaaaaaha, verschachtelte Gruppierungen. Logisch eigentlich, muß man nur drauf kommen.  
        
      Danke!  
      JOhnnY
      
      1. /x and ($day, $mon, $year) = ( $1||'', $2||'', $3 );

        hab gerade gemerkt, daß $day nicht immer zwei Ziffern hat. Angleichen könnte man es z.B. so:
        $day = $day < 10 ? '0'.$day : $day;

        Gibt es denn eine Möglichkeit, das auch noch direkt in den Regex einzubauen?

        1. /x and ($day, $mon, $year) = ( $1||'', $2||'', $3 );
          hab gerade gemerkt, daß $day nicht immer zwei Ziffern hat. Angleichen könnte man es z.B. so:
          $day = $day < 10 ? '0'.$day : $day;

          Gibt es denn eine Möglichkeit, das auch noch direkt in den Regex einzubauen?

          Gott bewahre - sagte EVA.

          mfg Beat

          --
          ><o(((°>           ><o(((°>
             <°)))o><                     ><o(((°>o
          Der Valigator leibt diese Fische
          1. Hallo Beat,

            Gott bewahre - sagte EVA.

            /x and ($day, $mon, $year) = ( $1||'', $2||'', $3 );

            oder vielleicht hier hinein?               ^^^^^^^

            Besten Gruß
            JOhnnY

            1. Hallo Beat,

              Gott bewahre - sagte EVA.

              /x and ($day, $mon, $year) = ( $1||'', $2||'', $3 );
              oder vielleicht hier hinein?               ^^^^^^^

              m/ ...... /x and $yyyy_mm_dd_date = sprintf("%04d-%02d-%02d", $3, $2||'', $1||'');

              Aber dann hast du keine nutzbaren Einzeldaten mehr.

              mfg Beat

              --
              ><o(((°>           ><o(((°>
                 <°)))o><                     ><o(((°>o
              Der Valigator leibt diese Fische
              1. Hallo Beat,

                m/ ...... /x and $yyyy_mm_dd_date = sprintf("%04d-%02d-%02d", $3, $2||'', $1||'');
                Aber dann hast du keine nutzbaren Einzeldaten mehr.

                ich dachte, es gäbe vielleicht eine Möglichkeit, im regulären Ausdruck sowas wie "0$1||$1" zu schreiben... ?

                lg
                JOhnnY

  2. $datum liegt in folgenden möglichen Formaten vor:

    12.03.2010
       03.2010
          2010

    Folgende Regexe liefern mir die einzelnen Elemente:

    Warum ein regulärer Ausdruck?

    my $datum = '5.3.2010';  
    my($jahr, $monat, $tag) = reverse split /\./, $datum);  
    printf '%02s.%02s.%04s', $tag, $monat, $jahr;  
    
    

    Damit Warnungen vermieden werden, kannst du hier auch eine Defaultwert angeben:
    my($jahr, $monat, $tag) = reverse (1970, 1, 1, (split /\./, $datum));

    Struppi.

    1. Hallo Struppi,

      Warum ein regulärer Ausdruck?

      ich nutze jede Gelegenheit, Regex zu üben. ;)

      Damit Warnungen vermieden werden, kannst du hier auch eine Defaultwert angeben:
      my($jahr, $monat, $tag) = reverse (1970, 1, 1, (split /\./, $datum));

      das genau ist, was ich _nicht_ will. Je nach übergebenen Parametern soll ein Tag, ein gesamter Monat oder ein ganzes Jahr angezeigt werden. Wenn kein Tag übergeben wird, soll der gesamte Monat angezeigt werden. Mit Defaultwerten gibt es keine Unterscheidung mehr zwischen dem 01. März und nur März, es sei denn ich nehme 'x' als Default und frage das nachher wieder ab. Aber das ist dann wieder sog. 'clever', wie man laut Perl Best Practices am besten nicht programmieren soll, um sich und andere nicht irgendwann später bös auszutricksen. Dann lieber undefinierte Werte nehmen.

      LG
      JOhnnY

      1. Warum ein regulärer Ausdruck?
        ich nutze jede Gelegenheit, Regex zu üben. ;)

        Ist aber in diesem Fall mit Kanonen auf Spatzen geschossen.

        Damit Warnungen vermieden werden, kannst du hier auch eine Defaultwert angeben:
        my($jahr, $monat, $tag) = reverse (1970, 1, 1, (split /\./, $datum));
        das genau ist, was ich _nicht_ will.

        Was du genau willst, hast du ja nicht gesagt. sondern nur was du trennen willst.

        Je nach übergebenen Parametern soll ein Tag, ein gesamter Monat oder ein ganzes Jahr angezeigt werden. Wenn kein Tag übergeben wird, soll der gesamte Monat angezeigt werden. Mit Defaultwerten gibt es keine Unterscheidung mehr zwischen dem 01. März und nur März, es sei denn ich nehme 'x' als Default und frage das nachher wieder ab.

        Hat ja keiner gessagt, dass du die Defaultwerte nehmen musst. Im gegenteil, mein Beispiel war ja ohne.

        Dann lieber undefinierte Werte nehmen.

        Da spricht nichts dagegen. Mein Vorschlag funktioniert ja auch so.

        Struppi.

        1. Ist aber in diesem Fall mit Kanonen auf Spatzen geschossen.

          Seit wann ist m//, das in Perl in der Regel schneller als split() ist, eine grössere Kanone?

          mfg Beat

          --
          ><o(((°>           ><o(((°>
             <°)))o><                     ><o(((°>o
          Der Valigator leibt diese Fische
          1. Ist aber in diesem Fall mit Kanonen auf Spatzen geschossen.

            Seit wann ist m//, das in Perl in der Regel schneller als split() ist, eine grössere Kanone?

            Wenn ich das Benchmarke ist split schneller, aber abgesehen davon, ist eine Zeile Code wesentlich einfacher zu verstehen als der Reguläre Ausdruck. Insofern sehe ich da keinen Vorteil.

            Struppi.

            1. Ist aber in diesem Fall mit Kanonen auf Spatzen geschossen.

              Seit wann ist m//, das in Perl in der Regel schneller als split() ist, eine grössere Kanone?

              Wenn ich das Benchmarke ist split schneller, aber abgesehen davon, ist eine Zeile Code wesentlich einfacher zu verstehen als der Reguläre Ausdruck. Insofern sehe ich da keinen Vorteil.

              split nimmt einen regulären Ausdruck!

              mfg Beat

              --
              ><o(((°>           ><o(((°>
                 <°)))o><                     ><o(((°>o
              Der Valigator leibt diese Fische
              1. Wenn ich das Benchmarke ist split schneller, aber abgesehen davon, ist eine Zeile Code wesentlich einfacher zu verstehen als der Reguläre Ausdruck. Insofern sehe ich da keinen Vorteil.

                split nimmt einen regulären Ausdruck!

                und der Vorteil eines komplizierten Regulären Ausdruck gegenüber einer Zeile split?

                Struppi.

                1. Wenn ich das Benchmarke ist split schneller, aber abgesehen davon, ist eine Zeile Code wesentlich einfacher zu verstehen als der Reguläre Ausdruck. Insofern sehe ich da keinen Vorteil.
                  split nimmt einen regulären Ausdruck!
                  und der Vorteil eines komplizierten Regulären Ausdruck gegenüber einer Zeile split?

                  Im konkreten Beispiel ist die Split-Version sicher einfacher.
                  Aber das Durchschauen der Gesamtlogik, die zu deinem Ziel führt ist nicht einfacher.

                  mfg Beat

                  --
                  ><o(((°>           ><o(((°>
                     <°)))o><                     ><o(((°>o
                  Der Valigator leibt diese Fische
                  1. Aber das Durchschauen der Gesamtlogik, die zu deinem Ziel führt ist nicht einfacher.

                    ... als ein Ausdruck über mehrere Zeilen oder was?

                    Struppi.

                    1. Aber das Durchschauen der Gesamtlogik, die zu deinem Ziel führt ist nicht einfacher.

                      ... als ein Ausdruck über mehrere Zeilen oder was?

                      Wenn ich deine Logik betrachte:

                      my($jahr, $monat, $tag) = reverse (1970, 1, 1, (split /\./, $datum));

                      ...fällt mir ein gravierender Fehler auf. Dir auch?

                      Wie viele Zeilen du für den Schuss in den Fuss brauchst, ist hier Nebensache.

                      mfg Beat

                      --
                      ><o(((°>           ><o(((°>
                         <°)))o><                     ><o(((°>o
                      Der Valigator leibt diese Fische
                      1. Wenn ich deine Logik betrachte:

                        Das war nicht die Logik, das war ein ungeprüfte Ergänzung. Die Logik war:

                        my $datum = '5.3.2010';  
                        my($jahr, $monat, $tag) = reverse split /\./, $datum);  
                        printf '%02s.%02s.%04s', $tag, $monat, $jahr;  
                        
                        

                        Da er sowieso undefinierte Werte wollte, ist dies soweit belanglos. Die Frage ist, warum einen langsameren und unleserlichen Ausdruck verwenden, wenn es eine kürzere und schnellere Variante gibt?

                        Struppi.