RegEx für Dateinamen mit Jahresangaben

  • Eine gute Tageszeit,

    ich habe folgendes Problem mit RegEx bzw. dem Konzept von Regulären Ausdrücken. Ich habe eine Liste mit Dateinamen und möchte diese in einzelne Bestandteile aufteilen.


    Meine Dateien können wie folgt aussehen:

    • Dateiname1 (2012) {Info}
    • Dateiname2 {Info}
    • Dateiname3 (2012)
    • Dateiname4

    Nun habe ich folgenden Code versucht, der auch wunderbar bei der ersten Variante funktioniert, aber bei allen anderen Varianten scheitert. Das Programm steigt dann einfach aus.

    Code
    $foundedFileReplaced = "Dateiname1 (2012) {{Info}}";Option 3, global return, old AutoIt style$splitTitle = StringRegExp($foundedFileReplaced, '(.*?).(\(?)(\d\d\d\d)(.*?)', 3)For $i = 0 To UBound($splitTitle) - 1MsgBox(0, "RegExp Test with Option 3 - " & $i, $splitTitle[$i])Next$splitYear = $splitTitle[2]$splitTitle = $splitTitle[0]MsgBox("","Jahr/Titel",  "|" & $splitYear  & "|" & @CRLF & "|" & $splitTitle & "|")

    Meine Frage ist nun, gibt es eine Möglichkeit so einen Filter mit nur einem Ausdruck aufzubauen oder was würdet ihr mir raten?


    Es gibt in Autoit wohl kein Try/Catch Konstrukt und deswegen komme ich mit so etwas nicht weiter:

    Code
    $splitTitle = $splitTitle[0]If $i = 4 Then    $splitYear  = $splitTitle[2]Else   $splitYear = ""EndIf

    5 Mal editiert, zuletzt von r0m (28. Juli 2013 um 11:46)

    • Offizieller Beitrag

    Vielleicht so:

    Spoiler anzeigen
    [autoit]


    #include <Array.au3>
    $foundedFileReplaced = "Dateiname1 (2012) {Info}"

    [/autoit] [autoit][/autoit] [autoit]

    $splitTitle = StringRegExp($foundedFileReplaced, '(\H+)\h*(\(\d+\))*\h*(\{.+\})*', 3)

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDisplay($splitTitle)

    [/autoit] [autoit][/autoit] [autoit]

    $foundedFileReplaced = "Dateiname1 (2012)"

    [/autoit] [autoit][/autoit] [autoit]

    $splitTitle = StringRegExp($foundedFileReplaced, '(\H+)\h*(\(\d+\))*\h*(\{.+\})*', 3)

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDisplay($splitTitle)

    [/autoit] [autoit][/autoit] [autoit]

    $foundedFileReplaced = "Dateiname1"

    [/autoit] [autoit][/autoit] [autoit]

    $splitTitle = StringRegExp($foundedFileReplaced, '(\H+)\h*(\(\d+\))*\h*(\{.+\})*', 3)

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDisplay($splitTitle)

    [/autoit]
    • Offizieller Beitrag

    Andere Variante:

    [autoit]

    $p = "(\w+)\s?((\(\d{4}\))?)\s?((\{\w+\})?)"

    [/autoit][autoit][/autoit][autoit]

    $aSplit = StringSplit(StringRegExpReplace('Dateiname1 (2012) {Info}', $p, '$1 $2 $4'), ' ')
    ConsoleWrite('Name: ' & $aSplit[1] & @TAB & 'Jahr: ' & StringRegExpReplace($aSplit[2], '[\(\)]', '') & @TAB & 'Info: ' & StringRegExpReplace($aSplit[3], '[\{\}]', '') & @TAB & @LF)

    [/autoit][autoit][/autoit][autoit]

    $aSplit = StringSplit(StringRegExpReplace('Dateiname2 {Info}', $p, '$1 $2 $4'), ' ')
    ConsoleWrite('Name: ' & $aSplit[1] & @TAB & 'Jahr: ' & StringRegExpReplace($aSplit[2], '[\(\)]', '') & @TAB & 'Info: ' & StringRegExpReplace($aSplit[3], '[\{\}]', '') & @TAB & @LF)

    [/autoit][autoit][/autoit][autoit]

    $aSplit = StringSplit(StringRegExpReplace('Dateiname3 (2012)', $p, '$1 $2 $4'), ' ')
    ConsoleWrite('Name: ' & $aSplit[1] & @TAB & 'Jahr: ' & StringRegExpReplace($aSplit[2], '[\(\)]', '') & @TAB & 'Info: ' & StringRegExpReplace($aSplit[3], '[\{\}]', '') & @TAB & @LF)

    [/autoit][autoit][/autoit][autoit]

    $aSplit = StringSplit(StringRegExpReplace('Dateiname4', $p, '$1 $2 $4'), ' ')
    ConsoleWrite('Name: ' & $aSplit[1] & @TAB & 'Jahr: ' & StringRegExpReplace($aSplit[2], '[\(\)]', '') & @TAB & 'Info: ' & StringRegExpReplace($aSplit[3], '[\{\}]', '') & @TAB & @LF)

    [/autoit]
  • Hallo!

    Im Sinne der heiligen Vielfalt, hier mein Vorschlag:

    Einen Regex Pattern hab ich dir zwar nicht zu bieten, aber wenn deine Daten immer genau so angeortnet sind, wie du es oben vorgibst, funktioniert meine Lösung auch.

    Voraussetzung ist, dass jede Zeile durch einen Zeilenumbruch abgeschlossen ist.
    Getrennt wird entweder bei einem Leerzeichen oder bei am Zeilenende. Danach gibt es einen leeren Eintrag im Array, der sich als Delimiter zwischen den Datensätzen eignet.

    Der String muß so aussehen, wie du es vorgegeben hast. Sollten dort noch andere Informationen enthalten sein, funktioniert es nicht. Ich gehe ausserdem davon aus, dass das Datum immer von normalen und die Info immer von geschweiften Klammern umschlossen ist.

    Die Auswertung in der Funktion GetIt() ist nur provisorisch und schnell mal hingeschmiert. Die Reihenfolge von (Jahr) und {Info} ist beliebig, da der Typ erkannt wird. Aber da wird dir bestimmt was eleganteres einfallen...

    Spoiler anzeigen
    [autoit]

    #include <array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $sString = "Dateiname1 (2012) {Info}" & @CRLF & _
    "Dateiname2 {Info}" & @CRLF & _
    "Dateiname3 (2012)" & @CRLF & _
    "Dateiname4 {Info} (2012)" & @CRLF & _
    "Dateiname5"

    [/autoit] [autoit][/autoit] [autoit]

    $aSplit = StringSplit($sString, " " & @CRLF)
    _ArrayDisplay($aSplit)

    [/autoit] [autoit][/autoit] [autoit]

    _GetIt()

    [/autoit] [autoit][/autoit] [autoit]

    Exit

    [/autoit] [autoit][/autoit] [autoit]

    Func _GetIt()
    Local $iCount, $sSpalte, $aLabels[3] = ["Dateiname:" & @TAB, "Jahr:" & @TAB & @TAB, "Info:" & @TAB & @TAB]
    Do
    $iCount += 1

    [/autoit] [autoit][/autoit] [autoit]

    If StringInStr($aSplit[$iCount], "(") Then
    $sStripped = stringtrimright(StringTrimLeft($aSplit[$iCount], 1), 1)
    $sSpalte &= $aLabels[1] & $sStripped & @CRLF
    ElseIf StringInStr($aSplit[$iCount], "{") Then
    $sStripped = stringtrimright(StringTrimLeft($aSplit[$iCount], 1), 1)
    $sSpalte &= $aLabels[2] & $sStripped & @CRLF
    ElseIf $aSplit[$iCount] <> "" Then
    $sSpalte &= $aLabels[0] & $aSplit[$iCount] & @CRLF
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    If $aSplit[$iCount] = "" Or $iCount = $aSplit[0] Then
    MsgBox(0, "", $sSpalte)
    $sSpalte = ""
    EndIf
    Until $iCount = $aSplit[0]
    EndFunc ;==>_GetIt

    [/autoit]


    Sanfte Grüße :D

  • Hey Leute,

    danke für eure Hilfe, echt fantastisch! Aber ich habe da wohl in der Beschreibung einen Fehler gemacht. Der Dateiname kann natürlich auch Leerzeichen enthalten. Dann scheitern die Ausdrücke!
    Weil die ja genau das als Trenner erwarten. Wie würde das denn unter Berücksichtung von Leerzeichen oder vielleicht auch von Punkten aussehen?

    Also mit

    • Dateiname 1 (2012) {Info}


    Leider habe ich das RegEx-Konzept nicht wirklich verinnerlicht.

    • Offizieller Beitrag

    • Bevor wir jetzt weitermachen, gib bitte mal alle Zeichen an, die im Dateinamen vorkommen können (etwa auch Umlaute und ß ? ).
    • Ist das Jahr immer in runden und die Info immer in geschweiften Klammern?
    • werden alle Elemente immer durch genau ein Leerzeichen getrennt?

  • • Bevor wir jetzt weitermachen, gib bitte mal alle Zeichen an, die im Dateinamen vorkommen können (etwa auch Umlaute und ß ? ).
    • Ist das Jahr immer in runden und die Info immer in geschweiften Klammern?
    • werden alle Elemente immer durch genau ein Leerzeichen getrennt?


    Wenn die letzten beiden Punkte zutreffen würde meine Lösung so aussehen:

    Code
    (.+) (\(\d+\)) (\{.+\})


    Problematisch wird das nur, wenn z.B. nur Einträge mit gültigen Daten erkannt werden sollen.

  • Optimal wäre es, wenn der Dateiname alle Zeichen enthalten könnte, bis auf runde Klammern. Mit allen Zeichen, meine ich auch bspw. ausländische Zeichen (UTF8) - wenn es möglich ist. Das Datum steht immer in runden Klammern und ist vierstellig. Die Info ist immer in geschweiften Klammern.

    • Offizieller Beitrag

    Das ist zwar nicht unmöglich - aber hier wäre es dann sinnvoller, die Dateinamen zu regeln.
    Mit Windows XP ist die Unsitte aufgekommen, nicht nur die Basisbuchstaben des engl. Alphabets zuzulassen, sonder auch Sonderzeichen und umlaute - Das isr echt KRANK!!
    Bei mir werden wder Umlaute noch sonstige "abnormen" Zeichen erlaubt. Dateinamen haben sauber zu bleiben und Leerzeichen nee, nee, die haben da auch nix verloren: Wozu gibt es Unterstriche.

  • James:

    Dein Ausdruck passt leider nicht mehr, sobald kein Jahr angegeben wird.

    BugFix :

    Machst du Witze? Keine Leerzeichen in Dateinamen?
    Was ich u. a. mit dem Sting machen will ist im Netz Infos abzurufen und dafür brauche ich Unterstützung für andere Sprachen. Das wird dann zwar in URI gewandelt, aber der Ausgangswert ist halt immer noch aus Sonderzeichen bestehend.

    Wie würde denn eine Lösung aussehen, wenn da nur deutsche Sonderzeichen zugelassen wären?

    Einmal editiert, zuletzt von r0m (28. Juli 2013 um 11:41)

  • Ich denke es würde reichen, wenn ich das Jahr in der einen Variable hätte und in der anderen alles was ein Zeichen vor "(" steht. Aber der Ausdruck sollte in jedem Fall auch dann funktionieren, wenn das Jahr oder der Info-Text komplett fehlen. Kriegt man das hin?

    Dateiname = $var[1]
    Jahr = $var[2]


    Mehr brauche ich eigentlich nicht.

  • Es wäre von Vorteil gewesen, wenn du einen "echten" oder zumindest einen repräsentativen Datensatz angegeben hättest, aber so ist es leider (wie so oft) nur ein Stochern im Dunkeln...
    Wird der String Zeilenweise geliefert, oder wie in meinem Beispiel alle Zeilen in einer Variablen?

    *edit*
    Sorry, da hab ich wohl ein bisschen zu sehr quergelesen :S
    Habe echt übersehen, dass es sich hier um Dateinamen handelt.

    Wie auch immer, hier mal zwei Beispiele für beide Fälle mit StringSplit:
    Nuja, vieleicht kannst du ja trotzdem mit Stringsplit was anfangen:

    Spoiler anzeigen
    [autoit]

    #include <array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    ;==> Zeilenweise
    $sString = "Dätéi Nôme 08/15 (2012) {Info Bla Bla}"

    [/autoit] [autoit][/autoit] [autoit]

    $aSplit = StringSplit($sString, "({")
    _ArrayDisplay($aSplit, "Zeilenweise")

    [/autoit] [autoit][/autoit] [autoit]

    _GetIt()

    [/autoit] [autoit][/autoit] [autoit]

    ;==> All in one mit Zeilenumbruch als Trenner
    $sString = "Dateiname1 (2012) {Info}" & @CRLF & _
    "Datèiname 2 {Info Bla}" & @CRLF & _
    "Dateinôme 08/15 (01/04/2012)" & @CRLF & _
    "Datéina me4 {Info} (01.04.2012)" & @CRLF & _
    "Dateinäm e5" & @CRLF & _
    "D ateinöme6"

    [/autoit] [autoit][/autoit] [autoit]

    $aSplit = StringSplit($sString, "({" & @CRLF)
    _ArrayDisplay($aSplit, "All in one")

    [/autoit] [autoit][/autoit] [autoit]

    _GetIt()

    [/autoit] [autoit][/autoit] [autoit]

    Exit

    [/autoit] [autoit][/autoit] [autoit]

    Func _GetIt()
    Local $iCount, $sSpalte, $aLabels[3] = ["Dateiname:" & @TAB, "Jahr:" & @TAB & @TAB, "Info:" & @TAB & @TAB]
    Do
    $iCount += 1

    [/autoit] [autoit][/autoit] [autoit]

    If StringInStr($aSplit[$iCount], ")") Then
    $sStripped = StringTrimRight(StringStripWS($aSplit[$iCount], 2), 1) ; überflüssge Zeichen entfernen
    $sSpalte &= $aLabels[1] & $sStripped & @CRLF
    ElseIf StringInStr($aSplit[$iCount], "}") Then
    $sStripped = StringTrimRight(StringStripWS($aSplit[$iCount], 2), 1) ; überflüssge Zeichen entfernen
    $sSpalte &= $aLabels[2] & $sStripped & @CRLF
    ElseIf $aSplit[$iCount] <> "" Then
    $sStripped = StringStripWS($aSplit[$iCount], 2) ; überflüssge Zeichen entfernen
    $sSpalte &= $aLabels[0] & $sStripped & @CRLF
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    If $aSplit[$iCount] = "" Or $iCount = $aSplit[0] Then
    MsgBox(0, "", $sSpalte)
    $sSpalte = ""
    EndIf
    Until $iCount = $aSplit[0]
    EndFunc ;==>_GetIt

    [/autoit]
  • Meine Lösung ist auch noch im Rennen:

    Code
    (.+) (\(\d+\)) (\{.+\})

    ^ Lösung für Formatierung 1, die anderen sind im Skript

    [autoit]

    Local $string = BinaryToString("0xE38193E38293E381ABE381A1E381AFE4B896E7958C", 4) & " - Beginners Guide to Japanese.pdf (2013) {Hello World}"
    Local $result, $i, $data = "", $patterns[3] = ["(.+) (\(\d+\)) (\{.+\})", "(.+) (\{.+\})", "(.+) (\(\d+\))"]

    [/autoit][autoit][/autoit][autoit]

    $result = _StringRegExp($string, $patterns)
    If @error Then
    MsgBox(64, "Test", $string)
    Else
    For $i = 0 To UBound($result)-1
    $data &= ($i+1) & @TAB & $result[$i] & @CRLF
    Next
    MsgBox(64, "Test", $data)
    EndIf

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _StringRegExp($sTest, $asPatterns)
    If (Not IsArray($asPatterns)) Then
    Return SetError(1, 0, -1)
    EndIf
    Local $SRE, $i
    For $i = 0 To UBound($asPatterns)-1
    $SRE = StringRegExp($sTest, $asPatterns[$i], 3)
    If (Not @error) Then Return $SRE
    Next
    Return SetError(2, 0, -1)
    EndFunc

    [/autoit]

    Edit:
    Das Skript funktioniert jetzt in allen 4 Fällen.

    • Offizieller Beitrag

    Bei der gewünschten Vielfalt erweist sich eine Lösung mit RegExp nicht als erste Wahl.
    Die folgende Variante ist wesentlich einfacher umzusetzen. Einzige Bedingung: Steht der Dateiname alleine, darf er nicht auf ")" oder "}" enden! Denn dieses sind die Marker für die Zusatzinfos Jahr und Info und werden im String von rechts abgefragt.

    Spoiler anzeigen
    [autoit]

    $s1 = 'Da~tei•ÄÖÜß[ bla ] 1 (2012) {Info}'
    $s2 = 'Da~tei•ÄÖÜß[ bla ] 1 {Info}'
    $s3 = 'Da~tei•ÄÖÜß[ bla ] 1 (2012)'
    $s4 = 'Da~tei•ÄÖÜß[ bla ] 1'

    [/autoit] [autoit][/autoit] [autoit]

    $aRet = _SeperateFileName($s1)
    ConsoleWrite('NAME: ' & $aRet[0] & ', JAHR: ' & $aRet[1] & ', INFO: ' & $aRet[2] & @LF)
    $aRet = _SeperateFileName($s2)
    ConsoleWrite('NAME: ' & $aRet[0] & ', JAHR: ' & $aRet[1] & ', INFO: ' & $aRet[2] & @LF)
    $aRet = _SeperateFileName($s3)
    ConsoleWrite('NAME: ' & $aRet[0] & ', JAHR: ' & $aRet[1] & ', INFO: ' & $aRet[2] & @LF)
    $aRet = _SeperateFileName($s4)
    ConsoleWrite('NAME: ' & $aRet[0] & ', JAHR: ' & $aRet[1] & ', INFO: ' & $aRet[2] & @LF)

    [/autoit] [autoit][/autoit] [autoit]

    Func _SeperateFileName($_sFile)
    Local $aOut[3] ; == [NAME, JAHR, INFO]
    Local $pos
    Switch StringRight($_sFile, 1)
    Case ')'
    $pos = StringInStr($_sFile, '(', 1, -1)
    $aOut[0] = StringTrimRight(StringLeft($_sFile, $pos -1), 1)
    $aOut[1] = StringMid($_sFile, $pos +1, 4)
    Case '}'
    $pos = StringInStr($_sFile, '{', 1, -1)
    $aOut[2] = StringMid($_sFile, $pos +1, StringLen($_sFile) -$pos -1)
    If StringMid($_sFile, $pos -2, 1) = ')' Then
    $pos = StringInStr($_sFile, '(', 1, -1)
    $aOut[0] = StringTrimRight(StringLeft($_sFile, $pos -1), 1)
    $aOut[1] = StringMid($_sFile, $pos +1, 4)
    Else
    $aOut[0] = StringTrimRight(StringLeft($_sFile, $pos -1), 1)
    EndIf
    Case Else
    $aOut[0] = $_sFile
    EndSwitch
    Return $aOut
    EndFunc

    [/autoit]
  • @James1337: Er meint wohl "...sobald kein Jahr angegeben wird.", weil dann liefert Dein Script kein Ergebnis (RegExp schlägt fehl).


    Ok, danke, macht Sinn. Ich hatte schon wieder vergessen, dass es mehrere Möglichkeiten gibt, wie die Einträge formatiert sein können. Ich habe das Skript oben verbessert.

  • Vielen dank an alle Helfer, ihr seid super. Ich habe mir dann doch schon gedacht, dass ein RegEx Ausdruck zu komplex wird und hatte vor der Aktion auch was mit Split versucht, aber es wurde auch lang.

    Die Umsetzung von Friesel habe ich jetzt gecheckt und es scheint genau das zu sein was ich suche. Werde ich dann wohl so übernehmen. Das Thema ist dann wohl gelöst :)


    Euch einen schönen Sonntag noch!