Oliver: Conditional Expressions

Hallo liebe Gemeinde,

ich tüftelt gerade an einem Problem.
Ich habe einen String, dessen Inhalt ich bei Auftreten eines Semikolons trennen möchte. Allerdings können in dem String auch Entitities in der Form > bei denen ich natürlich nicht nach dem Semikolon trennen möchte.

Momentaner Ansatz:

my @terms = split(/(?<!&#62);/,$string);

Das funktioniert sehr gut, solange die Entität genau definiert ist. Allerdings kann man das nicht verallgemeinern zu &#.+ da Lookahead nur bei fester Länge funktioniert. Da kommt sonst die Meldung variable length not implemented.

Hat vielleicht jemand einen Lösungsansatz - eventuell auch ganz andere Herangehensweise?

Besten Dank
Oliver

  1. Hi,

    my @terms = split(/(?<!&#62);/,$string);
    Allerdings kann man das nicht verallgemeinern zu &#.+ da Lookahead nur bei fester Länge funktioniert.

    Falsch. Lookbehind ist nur bei fester Länge erlaubt (da die Regex-Maschine wissen muß, wo sie mit der Suche beginnen muß), bei Lookahead muß die Länge nicht fest sein, da die Anfangsstelle für die Suche ja bekannt ist.

    Du hast hier einen Lookbehind eingesetzt - der muß natürlich eine feste Länge haben.

    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. gudn tach!

      Du hast hier einen Lookbehind eingesetzt - der muß natürlich eine feste Länge haben.

      bloss der vollstaendigkeit halber: "natuerlich" ist es nicht fuer regexp an sich, sondern nur fuer die meisten implementierungen.
      bei vim bspw. sind auch variable-length look-behind assertions erlaubt.

      perl 6 laesst da uebrigens auch hoffen. (http://dev.perl.org/perl6/rfc/72.html)

      prost
      seth

  2. Hallo!

    my @terms = split(/(?<!&#62);/,$string);

    Ein wenig verallgemeinern könnte man noch per /(?<!&#\d\d);/. Damit fängst du zumindest schon mal alle zweistelligen Entitäten.

    Für Entitäten mit unterschiedlicher Stellenanzahl könnte man es z.B. noch zu /(?<!(.&#\d\d|&#\d{3}));/ oder so ausbauen. Aber damit wird eine zweistellige Entität die gleich am Anfang steht nicht mehr erkannt.

    Eine andere Herangehensweiße die mir noch einfällt wäre, zuerst alle Semikoleons von Entitäten durch ein anderes Zeichen zu ersetzen, danach die Zeichenkette zu zerteilen, um schlussendlich wieder alle Entitäten-Semikoleons einzusetzen.

    Hmm, elegante Varianten fallen mir da aber leider keine ein. ;)

    Grüße, Skrilax

    1. gudn tach!

      Für Entitäten mit unterschiedlicher Stellenanzahl könnte man es z.B. noch zu /(?<!(.&#\d\d|&#\d{3}));/ oder so ausbauen. Aber damit wird eine zweistellige Entität die gleich am Anfang steht nicht mehr erkannt.

      man kann einfach mehrere look-behinds hintereinander schreiben.
        /(?<!&#\d\d)(?<!&#\d{3});/

      prost
      seth

  3. (Hallo|Hi(ho)|Tag) Oliver,

    ich tüftelt gerade an einem Problem.
    Ich habe einen String, dessen Inhalt ich bei Auftreten eines Semikolons trennen möchte. Allerdings können in dem String auch Entitities in der Form &#62; bei denen ich natürlich nicht nach dem Semikolon trennen möchte.

    Momentaner Ansatz:

    my @terms = split(/(?<!&#62);/,$string);

    Das funktioniert sehr gut, solange die Entität genau definiert ist. Allerdings kann man das nicht verallgemeinern zu &#.+ da Lookahead nur bei fester Länge funktioniert. Da kommt sonst die Meldung variable length not implemented.

    Das Lookbehind-Assertions nicht mit variablen Längen funktionieren, ist ja nun geklärt ...

    Hat vielleicht jemand einen Lösungsansatz - eventuell auch ganz andere Herangehensweise?

    Schreib dir eine eigene Funktion, die $string nach Semikola und Entitäten abklappert, aber nur bei allein stehenden Semikola abschnippelt ...

    Wenn es denn aber unbedingt mit split() funktionieren muss, versuch mal das hier:

      
    my $string = 'Test; Bla &thetasym; Blubb; Fred &amp; Barney; Meyer & Co.; nicht &#8230; und so weiter';  
    my @terms;  
      
    @terms = split /(?<!&.{2})(?<!&.{3})(?<!&.{4})(?<!&.{5})(?<!&.{6})(?<!&.{7})(?<!&.{8});/, $string;  
    
    

    Sieht zwar nicht sehr elegant aus, dürfte aber seinen Zweck erfüllen. Schließlich kann man mehrere Assertions ohne weiteres nebeneinander setzen -- sie werden quasi UND-verknüpft.

    Dieses  Konstrukt deckt Entitäten ab, die in folgenden Bereichen liegen:

      
    # kürzeste  
    # &lt;  
    # &#1;  
      
    # längstmögliche  
    # &thetasym; # eine längere CCE kennt mein Opera nicht ;-)  
    # &#9999999;  
    # &#xFFFFFF;  
    
    

    Zusätzlich zu beachten wäre, dass ...

    ... keine freifliegenden "&"s im Text vorkommen dürfen, sonst passiert sowas wie oben bei "Meyer & Co;" angegeben.

    ... die RegEx-Engine dann natürlich bei jedem gefundenen Semikolon bis zu 7 Assertions prüfen muss.

    Wenn du im Ausgangstext nur numerische Character References hast, kannst du die Suchmuster auch entsprechend anpassen:

      
      
    /(?<!&#.{2})(?<!&#.{3})(?<!&#.{4})(?<!&#.{5})(?<!&#.{6})(?<!&#.{7});/  
    
    

    MffG
    EisFuX

    1. gudn tach!

      Schließlich kann man mehrere Assertions ohne weiteres nebeneinander setzen -- sie werden quasi UND-verknüpft.

      gib's zu! du hast die antwort, die ich ja eigentlich bereits gab (obgleich sehr kurz gefasst) nur wiederholt, um deinen kola-witz reissen zu koennen. ;-)

      prost
      seth

      1. (Hallo|Hi(ho)|Tag) seth,

        gib's zu! du hast die antwort, die ich ja eigentlich bereits gab (obgleich sehr kurz gefasst) nur wiederholt, um deinen kola-witz reissen zu koennen. ;-)

        Nicht ganz, ich wollte nur, dass der Thread lange genug "offen" bleibt (Was ja geklappt hat), bis jemand eine elegantere Lösung[1] einfällt (was bisher leider noch nicht passiert ist).

        Außerdem muss ich gestehen, dass ich deinen Post anscheinend nicht richtig gelesen hatte. Schande über mich!

        ============
        [1] Ich würde es irgendwie besser finden, eine universelle(re) Lösung (ohne die abgezählten Look-Behind-Assertions) hinzubekommen. Vielleicht mit ?{...}?

        MffG
        EisFuX

        1. (Hallo|Hi(ho)|Tag) EisFuX,

          [1] Ich würde es irgendwie besser finden, eine universelle(re) Lösung (ohne die abgezählten Look-Behind-Assertions) hinzubekommen. Vielleicht mit ?{...}?

            
          my @blubb = split(/((?:&(?:\#\d+|\w+);|[^;])+?);/, $text);  
          
          

          Weiter bin ich noch nicht gekommen ...
          Kann irgendwer die leeren Einträge im Ergebnis-Array wegmachen?

          MffG
          EisFuX

          1. Hallo!

            Weiter bin ich noch nicht gekommen ...
            Kann irgendwer die leeren Einträge im Ergebnis-Array wegmachen?

            Da würde ich ein simples

              
            my @blubb = grep /./ => split(/((?:&(?:\#\d+|\w+);|[^;])+?);/, $text);
            

            vorschlagen.

            Allerdings trennt er bei mir bei

              
            my $text = 'Test; Bla &thetasym; Blubb; Fred &amp; Barney; Meyer & Co.; nicht &#8230; und so weiter';
            

            auch nach dem letzten &#8230; auf. Anscheinend will der "#\d+"-Teil nicht so recht greifen :(

            Grüße, Skrilax

            1. gudn tach!

              Da würde ich ein simples

              my @blubb = grep /./ => split(/((?:&(?:#\d+|\w+);|[^;])+?);/, $text);

              
              > vorschlagen.  
                
              das sieht - mal abgesehen davon, dass es noch fehlerhaft ist - jetzt allerdings immer weniger nach einem fall fuer split aus.  
              mit grep /./ wuerde uebrigens irrtuemlich ein leeres element geloescht, wenn ein semikolon am anfang (oder ende) stehen wuerde oder auch bei zwei direkt aufeinander folgenden semikolons.  
                
              kurz: man kann sich auch einfach gleich einen kleinen parser bauen.  
              mein vorschlag:  
              ~~~perl
                
              $_ = 'Test; Bla &thetasym; Blubb; Fred &amp; Barney; Meyer & Co.; nicht &#8230; und so weiter';  
              my @arr = ($_); # fuer den fall, dass kein semikolon vorhanden ist -> vorinitialisieren  
              for(my $i=0; /\G((?:[^;&]|&(?:#\d+|\w+);|& )*);(?=(.*)$)/gc; ++$i){  
               $arr[$i] = $1;  
               $arr[$i+1] = $2;  
              }  
              map {print $_,"\n";} @arr;
              

              das alternative subpattern /& / kann raus, falls einzeln stehende und-zeichen nicht vorkommen koennen.
              das locale-abhaengige /\w+/ wuerde ich ersetzen durch die wirklich erlaubten zeichen.

              hab ich was uebersehen?

              prost
              seth

              ps:
              zum split-code:

              Allerdings trennt er bei mir bei

              my $text = 'Test; Bla &thetasym; Blubb; Fred &amp; Barney; Meyer & Co.; nicht &#8230; und so weiter';

              
              > auch nach dem letzten &#8230; auf. Anscheinend will der "\#\d+"-Teil nicht so recht greifen :(  
                
              doch (uebrigens muss die raute nicht maskiert werden), aber per backtracking wird schliesslich '&#8230' von /[^;]+/ gematcht wird.  
              aber auch /[^;&]+/ wuerde hier nicht zum ziel fuehren, weil bei dem split-regexp ja nirgends gesagt wird, ab welcher gematcht werden soll. es muss ja nicht jedes zeichen im string gematcht werden.  
                
              prost  
              seth
              
              1. (Hallo|Hi(ho)|Tag) seth,

                das sieht - mal abgesehen davon, dass es noch fehlerhaft ist - jetzt allerdings immer weniger nach einem fall fuer split aus.

                Ich habs zwar vergessen hinzuschreiben, aber es ging mir darum, rauszubekommen, ob das "Problem" mit split() alleine lösbar ist. Deswegen "Knobelaufgabe".

                mit grep /./ wuerde uebrigens irrtuemlich ein leeres element geloescht, wenn ein semikolon am anfang (oder ende) stehen wuerde oder auch bei zwei direkt aufeinander folgenden semikolons.

                An direkt aufeinander folgende Semikolons (heute ohne Witz) hatte ich auch nicht gedacht ...

                hab ich was uebersehen?

                Numerisch kodierte Entitäten mit Sedezimalwerten, wie "&#x130E9;" ...

                MffG
                EisFuX

                1. gudn tach!

                  das sieht - mal abgesehen davon, dass es noch fehlerhaft ist - jetzt allerdings immer weniger nach einem fall fuer split aus.

                  Ich habs zwar vergessen hinzuschreiben, aber es ging mir darum, rauszubekommen, ob das "Problem" mit split() alleine lösbar ist. Deswegen "Knobelaufgabe".

                  ok.
                  *ueberleg*
                  dann geb ich auf.

                  hab ich was uebersehen?
                  Numerisch kodierte Entitäten mit Sedezimalwerten, wie "&#x130E9;" ...

                  ach so, ja, gut, kann man ja noch um hex ergaenzen. was da sonst alles erlaubt ist, weiss ich als eingefleischter ascii-fan eh nicht.
                  ich meinte eigentlich, ob ich was "systematisches" uebersehen habe.

                  prost
                  seth

                  1. (Hallo|Hi(ho)|Tag) seth,

                    ok.
                    *ueberleg*
                    dann geb ich auf.

                    Schade, war dann wohl doch zu (unmöglich|schwer|nicht spannend genug). Ohne die Beschränkung auf "nur split()" hätte es ja viele Möglichkeiten gegeben. Die hier, zum Beispiel:

                      
                    my @parts = split( /;(?![a-zA-Z\d]+?#?&)/, reverse($text) );  
                    @parts = reverse( map ($_ = reverse, @parts) );  
                    
                    

                    Jetzt sehe ich auch warum ich das # mit einem Backslash versehen wollte: Der Forums-Highlighter hat die gleichen Probleme wie mein Editor und meint einen Kommentar zu erkennen ...

                    ...

                    Numerisch kodierte Entitäten mit Sedezimalwerten, wie "&#x130E9;" ...

                    ach so, ja, gut, kann man ja noch um hex ergaenzen. was da sonst alles erlaubt ist, weiss ich als eingefleischter ascii-fan eh nicht.

                    Mist, dann kannst du auch nicht sehen, was &#x130E9; darstellt, wenn denn eines Tages U+130E9 offizell zur Unicode-Familie gehört:
                    http://www.unicode.org/charts/PDF/U13000.pdf

                    ich meinte eigentlich, ob ich was "systematisches" uebersehen habe.

                    Nein, sonst hab ich keine weiteren Büge finden können ... ;-)

                    MffG
                    EisFuX

    2. Vielen Dank für die hilfreichen Tipps.

      Problemfälle mit Meyer &Co; sollte es nicht geben, da ich diese schon vorher bearbeite.

      Danke und Gruß
      Oliver