Regex - Suche nach Gruppe, die gewisses Wort nicht enthällt

  • Allgemein

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von DanCooper.

    Regex - Suche nach Gruppe, die gewisses Wort nicht enthällt

    Hallo miteinander

    ich versuche gerade eine Regex zu basteln, die ich zum Bearbeiten von XML-Dateien benötige.
    Ich will bei allen Dateien, bei über 2 <audio> Streams verfügen und noch keine Sprachangabe <language> haben, diese mit dem TextCrawler einfügen.
    Dabei habe ich jetzt das Problem, dass ich nicht weiss wie ich die XMLs ausschliessen kann, die bereits ein <language> Node haben.

    Hier mal die Regex, die ich mit den Modifiern "global", "single line" und "case insensitive" nutze und entsprechend gruppiert habe, wie ich sie später brauche:

    Quellcode

    1. (<streamdetails>)(?<STREAM1ANFANG>.*?<audio>.*?)(?<STREAM1ENDE><\/audio>.*?)(?<STREAM2ANFANG><audio>.*?)(?<STREAM2ENDE><\/audio>.*?)(?<VIDEOundREST><video>.*?<\/streamdetails>)


    Hier ein XML, das noch keine Sprachangabe hat:

    XML-Quellcode

    1. <fileinfo>
    2. <streamdetails>
    3. <audio>
    4. <bitrate>192</bitrate>
    5. <channels>2</channels>
    6. <codec>AAC</codec>
    7. </audio>
    8. <audio>
    9. <bitrate>128</bitrate>
    10. <channels>6</channels>
    11. <codec>dolbydigital</codec>
    12. </audio>
    13. <video>
    14. <aspect>1.839</aspect>
    15. <bitrate>2016</bitrate>
    16. <codec>avc1</codec>
    17. <durationinseconds>142</durationinseconds>
    18. <height>696</height>
    19. <scantype>Progressive</scantype>
    20. <width>1280</width>
    21. </video>
    22. </streamdetails>
    23. </fileinfo>


    So sollte die XML danach aussehen:

    XML-Quellcode

    1. <fileinfo>
    2. <streamdetails>
    3. <audio>
    4. <bitrate>192</bitrate>
    5. <channels>2</channels>
    6. <codec>AAC</codec>
    7. <language>ger</language>
    8. </audio>
    9. <audio>
    10. <bitrate>128</bitrate>
    11. <channels>6</channels>
    12. <codec>dolbydigital</codec>
    13. <language>eng</language>
    14. </audio>
    15. <video>
    16. <aspect>1.839</aspect>
    17. <bitrate>2016</bitrate>
    18. <codec>avc1</codec>
    19. <durationinseconds>142</durationinseconds>
    20. <height>696</height>
    21. <scantype>Progressive</scantype>
    22. <width>1280</width>
    23. </video>
    24. </streamdetails>
    25. </fileinfo>



    Nun sollten die Regex natürlich nicht matchen, wenn schon eine Node <language> bei einem der beiden <audio> Streams vorhanden ist.
    Wie muss ich jetzt die Regex anpassen, damit sie nicht matcht wenn <language> bereits vorkommt?

    Ich benutze zum testen übrigens die Website regex101.com/
    Das Problem ist, ich arbeite hier nicht mit VS sondern mit TextCrawler, hab also nur Regex zur Verfügung. Grund ist, dass nicht ich das benötige sondern jemand, der sonst nix anderes hat.

    Ich verstehe einfach nicht, wie ich ganze Wörter angeben kann, die bei vorkommen ein Match verhindern.
    Einfaches Beispiel wäre z.B:

    Quellcode

    1. ich bin lieber zuhause als unterwegs
    2. ich bin im Sommer häufig unterwegs
    3. ich bin nicht gern zuhause im Keller unterwegs
    4. ich bin gerne draussen unterwegs


    Ich möchte hier gerne folgendes Ergebnis jeweils als Gruppe:
    • muss mit ich beginnen und beim ersten Vorkommen von unterwegs stoppen
    • darf das Wort zuhause nicht enthalten
    Es dürfte also nur 2 und 4 als Match angezeigt werden.

    Ich hab da schon diverses versucht wie z.B. \Bzuhause oder [^(zuhause)], komme aber auf keinen grünen Zweig.
    Manches lässt sich allein mit Regex nicht lösen.

    Ein studierter Progger von enormer Kompetenz hat mal gesagt "Regex ist in der Chomsky-Klassifizierung eine Sprache 2. Ordnung. Deshalb kann Regex nicht zählen."

    Zählen wäre aber für dein Vorhaben unabdingbar.

    na, vlt. auch doch - ich probier mal mit meim Regextester
    Regex

    Quellcode

    1. ((ich bin )(?!zuhause).*?unterwegs)


    Ich kriegs bei diesem Beispiel wenigstens annähernd hin, wobei mit (ich bin ) die Abfolge fix festgelegt habe:
    • ich bin zuhause als unterwegs
    • ich bin draussen häufig als unterwegs
    • ich bin zuhause im Keller als unterwegs
    • ich bin gerne zuhause im Keller als unterwegs
    • ich bin gerne draussen als unterwegs
    1 und 2 werden nicht gematcht, die 4 aber trotzdem.

    ErfinderDesRades schrieb:

    Lösung für Problem aus post#3: ich.*?(?<!zuhause .*)unterwegs

    Aber ob dir das für dein Xml hilft...


    Damit kriege ich den Fehler
    Lookbehinds need to be zero-width, thus quantifiers are not allowed

    Der Stern passt ihm da nicht.
    Welche Software nutzt du um Regex zu testen? Ich nutze immer regex101.com/
    Hi,

    wieso, wo ist das Problem, die Matches mit ner zweiten Zeile Code zu prüfen?
    Aber vorab um Missverständnisse zu vermeiden, du sagtest
    muss mit ich beginnen und beim ersten Vorkommen von unterwegs stoppen

    WAS muss mit "ich" beginnen? Die Zeile? Oder der String?

    Ansonsten: frag zuerst ab ob es mit "ich" beginnt und mit "unterwegs" endet - soweit waren wir ja schon. Könnte mit ^ich\b.*?\bunterwegs klappen. Die Funde kannst du dir in ein Array laden. Dieses Array gehst du inner Schleife durch, und nur wenn das Muster \bzuhause\b nicht gefunden wird, ist die Bedingung true. Und wenn die true ist, dann mach eben was auch immer du damit machen willst.

    Link :thumbup:
    Hello World

    Link schrieb:

    Hi,

    wieso, wo ist das Problem, die Matches mit ner zweiten Zeile Code zu prüfen?
    Aber vorab um Missverständnisse zu vermeiden, du sagtest
    muss mit ich beginnen und beim ersten Vorkommen von unterwegs stoppen

    WAS muss mit "ich" beginnen? Die Zeile? Oder der String?

    Ansonsten: frag zuerst ab ob es mit "ich" beginnt und mit "unterwegs" endet - soweit waren wir ja schon. Könnte mit ^ich\b.*?\bunterwegs klappen. Die Funde kannst du dir in ein Array laden. Dieses Array gehst du inner Schleife durch, und nur wenn das Muster \bzuhause\b nicht gefunden wird, ist die Bedingung true. Und wenn die true ist, dann mach eben was auch immer du damit machen willst.

    Link :thumbup:

    Wie in Post 3 geschrieben hab ich kein VB für diesen Fall zur verfügung, sondern muss mit einer einzigen Regex zurecht kommen. Ansonsten wäre das alles natürlich kein Problem. Aber ich will nicht extra eine Anwendung dafür schreiben.
    Hi,

    ah ok. Überlesen.
    Naja gut je nachdem wie flexibel du mit deinen Anforderungen bist (beginnt mit "ich" und stoppt beim ersten Vorkommen von "unterwegs") könnte man die Bedingungen ja ein Stück lockern. Denn wenn du sicher bist dass der Satz nach dem "ich" auf jeden Fall immer ein "bin" dranstehen hat, ist es natürlich absolut einfach: ich bin (?!(zuhause)).*? unterwegs. Wenn du Sätze hast, bei denen nach "ich" nicht "bin" kommt oder nach bin noch ein "gern", klappt das natürlich so nicht mehr. Aber vielleicht löst das dein Problem ja bereits.
    //Edit: naja hab grad noch bissl rumprobiert, allzu optimal isses so auch nicht :/

    Link :thumbup:
    Hello World

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Link“ ()

    Hi,

    habe nun denk ich eine Lösung dafür :) Der Pattern hier ich (?!.*?(zuhause)).*?unterwegs sollte das gewünschte tun.

    Im regex101.com klappt's:


    Du kannst mehrere Wörter angeben die nicht vorkommen sollen. Mit dem Pattern ich (?!.*?(zuhause|im)).*?unterwegs würde zum Beispiel nur die letzte Zeile gefunden werden, weil da "zuhause" und "im" nicht drin vorkommen :)

    Weitere Infos (wen's interessiert):
    Spoiler anzeigen
    //EDIT: Was da passiert ist übrigens folgendes:
    - Suche nach "ich"
    - Suche alles (also ".") bis zum ersten Vorkommen von "zuhause" und negiere dann durch den negativen lookaround. Quasi "suche solange bis dieses und jenes nicht vorkommt". Aber nicht nur "vielleicht nicht" sondern "ganz sicher nicht". Es darf gar nicht vorkommen.
    - Suche weiter nach allen Zeichen (ob welche kommen oder nicht) bis "unterwegs" vorkommt.

    Hierzu mögliche weitere Überlegungen:
    - schreibe im lookaround anstatt "zuhause" vielleicht "zuhause?", sodass u.U. auch nur "zuhaus" matcht.
    - schreibe vor "unterwegs" noch ein "\b" oder ein Leerzeichen um eine Wortgrenze zu erzwingen
    - Verwende auf jeden Fall die Modifier "g" für global und "i" für ignore-case. Bei Bedarf noch "s" für singleline.



    Link :thumbup:
    Hello World

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Link“ ()

    Auch mit TextCrawler getestet?

    Falls es dir entgangen ist, nicht alle Regexe können was ein .net Regex kann.


    Nein. Aber warum testest du es nicht bitte mit TextCrawler und postest dazu, ob es funktioniert? Dann hättest du auch was sinnvolles beigetragen ;) Naja lass sein ich mach es gern. Ich editiere meinen Post dann und schreib dazu ob's klappt.

    //EDIT: Getestet mit TextCrawler: funktioniert :)

    Link :thumbup:
    Hello World

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Link“ ()

    Link schrieb:

    Hi,

    habe nun denk ich eine Lösung dafür :) Der Pattern hier ich (?!.*?(zuhause)).*?unterwegs sollte das gewünschte tun.

    Im regex101.com klappt's:
    vb-paradise.de/index.php/Attac…4744b6170f737800527e679c3

    Du kannst mehrere Wörter angeben die nicht vorkommen sollen. Mit dem Pattern ich (?!.*?(zuhause|im)).*?unterwegs würde zum Beispiel nur die letzte Zeile gefunden werden, weil da "zuhause" und "im" nicht drin vorkommen :)

    Weitere Infos (wen's interessiert):
    Spoiler anzeigen
    //EDIT: Was da passiert ist übrigens folgendes:
    - Suche nach "ich"
    - Suche alles (also ".") bis zum ersten Vorkommen von "zuhause" und negiere dann durch den negativen lookaround. Quasi "suche solange bis dieses und jenes nicht vorkommt". Aber nicht nur "vielleicht nicht" sondern "ganz sicher nicht". Es darf gar nicht vorkommen.
    - Suche weiter nach allen Zeichen (ob welche kommen oder nicht) bis "unterwegs" vorkommt.

    Hierzu mögliche weitere Überlegungen:
    - schreibe im lookaround anstatt "zuhause" vielleicht "zuhause?", sodass u.U. auch nur "zuhaus" matcht.
    - schreibe vor "unterwegs" noch ein "\b" oder ein Leerzeichen um eine Wortgrenze zu erzwingen
    - Verwende auf jeden Fall die Modifier "g" für global und "i" für ignore-case. Bei Bedarf noch "s" für singleline.



    Link :thumbup:
    Wow, sehr gut, danke!

    Jetzt muss ich das nur noch irgendwie in meine ursprüngliche Regex aus Post 1 einbauen :) Denke aber, das sollte gehen.

    EDIT:

    so, ich habs nun für meine Anfangsfrage die richtige Regex erstellt:

    Quellcode

    1. (<streamdetails>)(?<STREAM1ANFANG>\s*?<audio>(?!.*?(language)).*?)(?<STREAM1ENDE><\/audio>)(?<STREAM2ANFANG>\s*?<audio>(?!.*?(language)).*?)(?<STREAM2ENDE><\/audio>)\s*?(?<VIDEOundREST><video>.*?<\/streamdetails>)


    Damit werden jetzt nur die NFOs erkannt, die über zwei Audiospuren verfügen, bei denen keine der beiden Spuren ein <language> Node besitzt.
    Danke an Link für die Lösung :thumbsup: (wo finde ich eigentlich den Bedanken oder Nützlich Button?)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DanCooper“ ()