Projektnummern mit RegularExpressions finden

Problemstellung

Folgendes Szenario dürften so einige Sachbearbeiter kennen: Es gibt ein Sammelbecken mit Dateien, die unter anderem eine Projektnummer oder Auftragsnummer beinhalten. Ziel für diese Dateien sind die entsprechenden Projektordner in einer zentralen Struktur. Auch die Projektordner beinhalten die Projektnummer oder Auftragsnummer. So eine Struktur dürfte in vielen Unternehmen, ob Old School auf einem Serverlaufwerk oder etwas aktueller in der Cloud.

Lösungsansatz

Doch wie findet man nun 1. die Nummer in dem Dateinamen und 2. den korrekten Zielordner(besonders wenn der nicht direkt im Stammverzeichnis liegt)? Das Stichwort ist hier RegularExpressions oder eben Reguläre Ausdrücke. Damit lassen sich aber nicht nur Nummernfolgen finden sondern komplexe Ausdrücke, Passwörter auf Konformität prüfen (min x Stellen, Groß/Kleinbuchstaben, Sonderzeichen, etc.), oder auch ganze Formelparser bauen. Soweit soll es hier aber nicht gehen.

Ok, wir nehmen mal an, dass unsere Auftragsnummer, nach der wir suchen möchten, beispielsweise folgendes Format hat: P<Jahreszahl>-<fortlaufende Nummer, 5 Stellen>. Um mal ohne viel Aufwand RegExs zu testen, eignet sich regex101.com super und soll hier auch zum Einsatz kommen. Als Testobjekte sollen dafür mal P2023-45627, P2015-33456, P1999-11223 und P1976-00132 dienen. Damit das nun nicht zu leicht wird, könnte vor/hinter der Projektnummer nichts, Unterlagen, Rev.B, Zeichnung-12345 stehen.

Um nun den Suchbegriff abzubilden, braucht es eigentlich nur folgendes:

([pP][0-9]{4}-[0-9]{5})
(        : Öffnet die Matchgruppe
[pP]     : Ein p oder P
[0-9]{4} : 4 Zeichen, alles von 0-9 erlaubt
-        : Trennzeichen (ggf. auch [-_] möglich, wenn Fehleingaben berücksichtigt werden sollen
[0-9]{5} : 5 Zeichen, alles von 0-9 erlaubt
)        : Schließt die Matchgruppe

Wenn der gesuchte Ordner direkt unter dem Stammverzeichnis liegt, kann man mit der gesuchten Projektnummer auch direkt finden. Auch wenn Unterordner durchsucht werden können, funktioniert das, performant ist das allerdings nicht zwingend. Also angenommen, die Projektordner liegen jeweils in einem Ordner mit der Jahreszahl, dann wäre es gut, wenn diese direkt aus der Projektnummer gezogen werden würde. Eine kleine Anpassung löst das Problem elegant:

([pP]([0-9]{4})(-[0-9]{5}))
(        : Öffnet die Matchgruppe
[pP]     : Ein p oder P
(        : Öffnet die erste Untergruppe
[0-9]{4} : 4 Zeichen, alles von 0-9 erlaubt
)        : Schließt die erste Untergruppe
(        : Öffnet die zweite Untergruppe (das Trennzeichen kann auch ausgeklammert werden
-        : Trennzeichen (ggf. auch [-_] möglich, wenn Fehleingaben berücksichtigt werden sollen
[0-9]{5} : 5 Zeichen, alles von 0-9 erlaubt
)        : Schließt die zweite Untergruppe
)        : Schließt die Matchgruppe

Welche Auswirkung hat die Änderung? Beim ersten Ansatz ist das Ergebnis „P2023-45627“. Beim zweiten „P2023-45627“, „2023“ und „-45627“. Mit dem ersten Teilergebnis lässt sich der Ordner unter dem Stammverzeichnis einfacher identifizieren und so schneller der Projektordner finden.

Suchen und finden in der PowerShell

Da manche Funktionen einfach bei diversen Nutzern im Hintergrund, als Task oder als geplante Task laufen kann, ist der Ansatz PowerShell interessant, da man nicht auf Programme und Compiler angewiesen ist. Code für die Powershell kann jede(r) mit Rechten und Wissen darüber erstellen oder editieren und ausführen. Wie oben beschrieben soll her RegularExpressions genutzt werden.

function getProjectNumber {
    // Übergabeparameter
    param ( $str )
    // RegularExpression definieren wie oben in Lösung I
    $obj_regex = [regex] '([pP]([0-9]{4})(-[0-9]{5}))'
    // Pattern nun gegen den String str prüfen    
    if ($str -match $obj_regex) {
        // Projektnummer zurückgeben, beim Match
        return $Matches.0
    } 
    // Wenn es kein Match gibt
    return ""
}
function getProjectPath {
    // Übergabeparameter
    param ( $str_ProjNo )
    $str_startpath = "\\<Server>\<sharefolder>\<projektstammordner>"
    // Wenn der Ordner existiert Pfad zurückgeben, sonst ""
    if ($str_ProjNo -ne "") {
        return Get-ChildItem -Path $str_startpath -Name $str_ProjNo
    }
    return ""
}

Suchen und finden in C#

C# ist schon interessant, da es in einigen Programmen für das Scripting genutzt werden kann, zB. EPlan P8. Es wäre möglich das noch weiter zu vereinfachen, dann wird es allerdings auch unleserlich und definitiv nicht einfach nachvollziehbar. Kern ist aber auch hier das Thema RegularExpressions.

public string getProjectNumber (string str) {
    // RegularExpression definieren wie oben in Lösung I
    Regex obj_regex = new Regex("([pP][0-9]{4}-[0-9]{5})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    // Pattern nun gegen den String str prüfen
    Match obj_match = obj_regex.Match(str);
    // Wenn es ein Match gibt, Projektnummer zurückgeben
    if (obj_match.Length > 0) { return obj_match.Value; }
    return "";
}

public string getProjectPath (string str_ProjNo) {
    string str_startpath = @"\\<Server>\<sharefolder>\<projektstammordner>"
    if (str_ProjNo != "") {
        try {
            // nur in der obersten Ebene Suchen, da auch Unterordner mit einer Projektnummer existieren könnten
            string[] str_dirs =  Directory.GetDirectories(str_startpath, str_ProjNo + "*", SearchOption.TopDirectoryOnly);
            return str_dirs[0];
        } catch (Exception) {
            return "";
        }
    }
    // vielleicht hier eine Fehlermeldung, weil nicht gefunden oder Problem aufgetreten
    return "";
}

Suchen und finden in VBA

Weil einiges eben auch auf Basis der MS-Office-Welt läuft, zB. dynamisches Links bilden in Excel, an dieser Stelle auch das Beispiel eben in VBA. So lassen sich zB in Mails nach einer Projektnummer suchen und mit einem Klick auf die Projektordner zugreifen, oder den Nutzer einfach prüfen, ob es eine valide Projektnummer ist, oder ein Zeichendreher bestehen könnte. Auch hier wieder das Thema RegularExpressions als Dreh und Angelpunkt.

Public Function getProjectNumber(ByVal str As String) As String
    ' allgemeine Variablen definieren
    Dim obj_regex, obj_match as Object
    Set obj_regex = CreateObject("vbascript.regexp")
    
    ' Suche:
    With obj_regex
        ' Casesensitiv abschalten
        .ignorecase = True
        ' RegularExpression definieren wie oben in Lösung I
        .Pattern = "([pP][0-9]{4}-[0-9]{5})"
        ' Pattern nun gegen den String str prüfen und wenn positiv dann ausführen
        If .test(str) Then
            Set obj_match = .Execute(str)
            ' Rückbgabe setzen, bei Lösungsansatz II einfach mehrere Submatches verketten
            getProjectNumber = obj_match(0).submatches(0)
        End If
        ' wenn Test negativ, leeres String zurück geben:
        getProjectNumber = ""
    End With
End Function

Public Function getProjectPath(ByVal str_ProjNo As String) As String
    Dim str_folder, str_startpath as String
    str_startpath = "\\<Server>\<sharefolder>\<projektstammordner>"
    
    str_folder = Dir(str_startpath + "\" + str_ProjNo + "*", vbDirectory)
    ' Wenn der Ordner existiert Pfad zurückgeben, sonst ""
    If Len(str_folder) > 0 Then
        getProjektPath = str_startpath + "\" + str_folder
    End If
    getProjektPath = ""
End Function

Das Problem lässt sich noch mit diversen anderen Sprachen lösen und vielleicht auch schöner. Was das Beispiel aber zeigen soll: Auch wenn manches Problem erst komplex wirkt, ist die Lösung nicht selten recht schlicht und einfach.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert