Wie interpretiert der Windows-Befehl RENAME Platzhalter?

77

Wie interpretiert der Windows-Befehl RENAME (REN) Platzhalter?

Die eingebaute HELP-Funktion hilft nicht weiter - sie spricht überhaupt keine Platzhalter an.

Die Microsoft Technet XP Online-Hilfe ist nicht viel besser. Hier ist alles, was es zu Platzhaltern zu sagen hat:

"Sie können in jedem Dateinamenparameter Platzhalter ( *und ?) verwenden. Wenn Sie Platzhalter in Dateiname2 verwenden, stimmen die durch die Platzhalter dargestellten Zeichen mit den entsprechenden Zeichen in Dateiname1 überein."

Keine große Hilfe - es gibt viele Möglichkeiten, wie Aussagen interpretiert werden können.

Ich habe es manchmal geschafft, Platzhalter im Parameter " filename2" erfolgreich zu verwenden , aber es war immer ein Versuch und Irrtum. Ich konnte nicht vorhersehen, was funktioniert und was nicht. Häufig musste ich ein kleines Stapelskript mit einer FOR-Schleife schreiben, die jeden Namen analysiert, damit ich jeden neuen Namen nach Bedarf erstellen kann. Nicht sehr praktisch.

Wenn ich die Regeln für die Verarbeitung von Platzhaltern kennen würde, könnte ich den Befehl RENAME effektiver verwenden, ohne so oft auf Batches zurückgreifen zu müssen. Die Kenntnis der Regeln würde natürlich auch der Chargenentwicklung zugute kommen.

(Ja - dies ist ein Fall, in dem ich eine gepaarte Frage und Antwort poste. Ich hatte es satt, die Regeln nicht zu kennen, und entschied mich, alleine zu experimentieren. Ich glaube, viele andere interessieren sich vielleicht für das, was ich entdeckt habe.)

dbenham
quelle
Hier finden Sie unzählige
Matthew Lock
5
@MatthewLock - Interessanter Link, aber diese Regeln und Beispiele gelten für MSDOS 7, nicht für Windows. Es gibt signifikante Unterschiede. Beispielsweise erlaubt MSDOS nicht, zusätzliche Zeichen anzufügen, nachdem *Windows dies tut. Das hat enorme Konsequenzen. Ich wünschte, ich hätte etwas über diese Seite gewusst. es hätte meine Untersuchung erleichtern können. Die MSDOS7-Regeln unterscheiden sich erheblich von den alten DOS-Regeln vor langen Dateinamen und sind ein Schritt in die Richtung, wie Windows damit umgeht. Ich hatte die DOS-Regeln für vorlange Dateinamen gefunden und sie waren für meine Untersuchung wertlos.
Dbenham
Das wusste ich nicht;)
Matthew Lock

Antworten:

116

Diese Regeln wurden nach umfangreichen Tests auf einem Vista-Computer entdeckt. Es wurden keine Tests mit Unicode in Dateinamen durchgeführt.

RENAME erfordert 2 Parameter - eine Quellmaske, gefolgt von einer Zielmaske. Sowohl die Quellmaske als auch die Zielmaske können Platzhalter *und / oder ?Platzhalter enthalten. Das Verhalten der Platzhalter ändert sich geringfügig zwischen Quell- und Zielmaske.

Hinweis - REN kann zum Umbenennen eines Ordners verwendet werden. Beim Umbenennen eines Ordners sind jedoch weder in der Quellmaske noch in der Zielmaske Platzhalter zulässig. Wenn die Quellmaske mindestens einer Datei entspricht, werden die Dateien umbenannt und Ordner ignoriert. Wenn die Quellmaske nur mit Ordnern und nicht mit Dateien übereinstimmt, wird ein Syntaxfehler generiert, wenn in der Quelle oder im Ziel Platzhalter angezeigt werden. Wenn die Quellmaske mit nichts übereinstimmt, wird der Fehler "Datei nicht gefunden" ausgegeben.

Beim Umbenennen von Dateien sind Platzhalter nur im Dateinamensbereich der Quellmaske zulässig. Platzhalter sind im Pfad zum Dateinamen nicht zulässig.

sourceMask

Die Quellmaske dient als Filter, um zu bestimmen, welche Dateien umbenannt werden. Die Platzhalter funktionieren hier genauso wie bei jedem anderen Befehl, der Dateinamen filtert.

  • ?- Stimmt mit einem beliebigen 0- oder 1-Zeichen überein, mit Ausnahme von . Dieses Platzhalterzeichen ist gierig. Wenn es kein Zeichen ist, wird immer das nächste Zeichen verwendet..

  • *- Stimmt mit 0 oder mehr Zeichen überein . (mit einer Ausnahme unten). Diese Wildcard ist nicht gierig. Es stimmt so wenig oder so viel überein, wie erforderlich ist, damit nachfolgende Zeichen übereinstimmen.

Alle Zeichen, die keine Platzhalterzeichen sind, müssen mit Ausnahme einiger Ausnahmen für Sonderfälle übereinstimmen.

  • .- Stimmt mit sich selbst überein oder es kann mit dem Ende des Namens (nichts) übereinstimmen, wenn keine weiteren Zeichen verbleiben. (Hinweis - Ein gültiger Windows-Name darf nicht mit enden. .)

  • {space}- Stimmt mit sich selbst überein oder es kann mit dem Ende des Namens (nichts) übereinstimmen, wenn keine weiteren Zeichen verbleiben. (Hinweis - Ein gültiger Windows-Name darf nicht mit enden. {space})

  • *.am Ende - Spiele alle 0 oder mehr Zeichen außer . dem Abschluss .tatsächlich ein beliebige Kombination sein können , .und {space}solange das allerletzte Zeichen in der Maske . Dies ist die einzige Ausnahme , wo *einfach keine Gruppe von Zeichen entsprechen.

Die obigen Regeln sind nicht so komplex. Es gibt jedoch eine weitere sehr wichtige Regel, die die Situation verwirrt: Die Quellmaske wird sowohl mit dem langen Namen als auch mit dem kurzen 8.3-Namen (sofern vorhanden) verglichen. Diese letzte Regel kann die Interpretation der Ergebnisse sehr schwierig machen, da es nicht immer offensichtlich ist, wann die Maske über den Kurznamen übereinstimmt.

Mit RegEdit können Sie die Generierung kurzer 8.3-Namen auf NTFS-Volumes deaktivieren. In diesem Fall ist die Interpretation der Dateimaskenergebnisse viel einfacher. Alle Kurznamen, die vor dem Deaktivieren der Kurznamen generiert wurden, bleiben erhalten.

targetMask

Hinweis - Ich habe keine strengen Tests durchgeführt, aber es scheint, dass dieselben Regeln auch für den Zielnamen des Befehls COPY gelten

Die targetMask gibt den neuen Namen an. Es wird immer auf den vollständigen Langnamen angewendet. Die Zielmaske wird niemals auf den kurzen 8.3-Namen angewendet, selbst wenn die Quellmaske mit dem kurzen 8.3-Namen übereinstimmt.

Das Vorhandensein oder Fehlen von Platzhaltern in der Quellmaske hat keinen Einfluss darauf, wie Platzhalter in der Zielmaske verarbeitet werden.

In der folgenden Diskussion - cstellt jedes Zeichen , das nicht *, ?oder.

Die Zielmaske wird ausschließlich von links nach rechts ohne Rückverfolgung anhand des Quellennamens verarbeitet.

  • c- Verschiebt die Position innerhalb des Quellennamens, solange das nächste Zeichen nicht vorhanden ist, .und wird can den Zielnamen angehängt . (Ersetzt das Zeichen, das in der Quelle enthalten war c, aber niemals ersetzt .)

  • ?- Stimmt mit dem nächsten Zeichen des Quell-Langnamens überein und hängt es an den Zielnamen an, solange das nächste Zeichen nicht ist. . Wenn das nächste Zeichen .oder das Ende des Quellnamens ist, wird dem Ergebnis und dem aktuellen kein Zeichen hinzugefügt Die Position innerhalb des Quellennamens bleibt unverändert.

  • *am Ende von targetMask - Hängt alle verbleibenden Zeichen von der Quelle an das Ziel an. Wenn schon am Ende der Quelle, dann nichts.

  • *c- Ordnet alle Quellzeichen von der aktuellen Position bis zum letzten Auftreten von c(case sensitive gierige Übereinstimmung) zu und hängt den übereinstimmenden Zeichensatz an den Zielnamen an. Wird dies cnicht gefunden, werden alle verbleibenden Zeichen aus der Quelle angehängt. c Dies ist die einzige mir bekannte Situation, in der bei der Windows-Dateimustererkennung zwischen Groß- und Kleinschreibung unterschieden wird.

  • *.- Stimmt mit allen Quellzeichen von der aktuellen Position bis zum letzten Auftreten von .(gierige Übereinstimmung) überein und fügt den übereinstimmenden Zeichensatz an den Zielnamen an. Wird dies .nicht gefunden, werden alle verbleibenden Zeichen aus der Quelle angehängt, gefolgt von.

  • *?- Hängt alle verbleibenden Zeichen von der Quelle an das Ziel an. Wenn schon am Ende der Quelle dann nichts.

  • .without *in front - Verschiebt die Position in der Quelle durch das erste Vorkommen, .ohne Zeichen zu kopieren, und hängt sie .an den Zielnamen an. Wird .es in der Quelle nicht gefunden, wird zum Ende der Quelle weitergeschaltet und .an den Zielnamen angehängt .

Nachdem die targetMask erschöpft ist, werden alle nachfolgenden .und {space}abgeschnittenen Einträge am Ende des resultierenden Zielnamens angezeigt, da Windows-Dateinamen nicht mit .oder enden können{space}

Einige praktische Beispiele

Ersetzen Sie ein Zeichen an der ersten und dritten Stelle vor einer Erweiterung (fügt ein zweites oder drittes Zeichen hinzu, falls es noch nicht vorhanden ist)

ren  *  A?Z*
  1        -> AZ
  12       -> A2Z
  1.txt    -> AZ.txt
  12.txt   -> A2Z.txt
  123      -> A2Z
  123.txt  -> A2Z.txt
  1234     -> A2Z4
  1234.txt -> A2Z4.txt

Ändern Sie die (endgültige) Erweiterung jeder Datei

ren  *  *.txt
  a     -> a.txt
  b.dat -> b.txt
  c.x.y -> c.x.txt

Fügen Sie an jede Datei eine Erweiterung an

ren  *  *?.bak
  a     -> a.bak
  b.dat -> b.dat.bak
  c.x.y -> c.x.y.bak

Entfernen Sie alle zusätzlichen Erweiterungen nach der anfänglichen Erweiterung. Beachten Sie, dass ausreichend ?verwendet werden muss, um den vollständigen vorhandenen Namen und die ursprüngliche Erweiterung beizubehalten.

ren  *  ?????.?????
  a     -> a
  a.b   -> a.b
  a.b.c -> a.b
  part1.part2.part3    -> part1.part2
  123456.123456.123456 -> 12345.12345   (note truncated name and extension because not enough `?` were used)

Wie oben, jedoch werden Dateien mit einem Anfangsnamen und / oder einer Erweiterung von mehr als 5 Zeichen herausgefiltert, damit sie nicht abgeschnitten werden. (Offensichtlich könnte ?an beiden Enden der Zielmaske eine zusätzliche hinzugefügt werden, um Namen und Erweiterungen mit bis zu 6 Zeichen Länge beizubehalten.)

ren  ?????.?????.*  ?????.?????
  a      ->  a
  a.b    ->  a.b
  a.b.c  ->  a.b
  part1.part2.part3  ->  part1.part2
  123456.123456.123456  (Not renamed because doesn't match sourceMask)

Ändern Sie die Zeichen nach dem Nachnamen _und versuchen Sie, die Erweiterung beizubehalten. (Funktioniert nicht richtig, wenn _in der Erweiterung angezeigt wird)

ren  *_*  *_NEW.*
  abcd_12345.txt  ->  abcd_NEW.txt
  abc_newt_1.dat  ->  abc_newt_NEW.dat
  abcdef.jpg          (Not renamed because doesn't match sourceMask)
  abcd_123.a_b    ->  abcd_123.a_NEW  (not desired, but no simple RENAME form will work in this case)

Jeder Name kann in Komponenten unterteilt werden, die durch . Zeichen begrenzt sind. Diese dürfen nur am Ende jeder Komponente angehängt oder gelöscht werden. Zeichen können nicht am Anfang oder in der Mitte einer Komponente gelöscht oder hinzugefügt werden, während der Rest mit Platzhaltern beibehalten wird. Ersetzungen sind überall erlaubt.

ren  ??????.??????.??????  ?x.????999.*rForTheCourse
  part1.part2            ->  px.part999.rForTheCourse
  part1.part2.part3      ->  px.part999.parForTheCourse
  part1.part2.part3.part4   (Not renamed because doesn't match sourceMask)
  a.b.c                  ->  ax.b999.crForTheCourse
  a.b.CarPart3BEER       ->  ax.b999.CarParForTheCourse

Wenn Kurznamen aktiviert sind, stimmt eine Quellmaske mit mindestens 8 ?für den Namen und mindestens 3 ?für die Erweiterung mit allen Dateien überein, da sie immer mit dem Kurznamen 8.3 übereinstimmt.

ren ????????.???  ?x.????999.*rForTheCourse
  part1.part2.part3.part4  ->  px.part999.part3.parForTheCourse


Nützliche Eigenart / Fehler? zum Löschen von Namenspräfixen

In diesem SuperUser-Beitrag wird beschrieben, wie mit einer Reihe von Schrägstrichen ( /) führende Zeichen aus einem Dateinamen gelöscht werden können. Für jedes zu löschende Zeichen ist ein Schrägstrich erforderlich. Ich habe das Verhalten auf einem Windows 10-Computer bestätigt.

ren "abc-*.txt" "////*.txt"
  abc-123.txt        --> 123.txt
  abc-HelloWorld.txt --> HelloWorld.txt

Diese Technik funktioniert nur, wenn sowohl die Quell- als auch die Zielmaske in doppelte Anführungszeichen eingeschlossen sind. Alle folgenden Formulare ohne die erforderlichen Anführungszeichen schlagen mit diesem Fehler fehl:The syntax of the command is incorrect

REM - All of these forms fail with a syntax error.
ren abc-*.txt "////*.txt"
ren "abc-*.txt" ////*.txt
ren abc-*.txt ////*.txt

Mit dem /können keine Zeichen in der Mitte oder am Ende eines Dateinamens entfernt werden. Es können nur führende (Präfix-) Zeichen entfernt werden.

Technisch /funktioniert das nicht als Wildcard. Vielmehr handelt es sich um eine einfache Zeichenersetzung. Nach der Ersetzung erkennt der Befehl REN jedoch, dass /ein Dateiname ungültig ist, und entfernt die führenden /Schrägstriche aus dem Namen. REN gibt einen Syntaxfehler aus, wenn er /in der Mitte eines Zielnamens erkannt wird.


Möglicher RENAME-Fehler - Ein einzelner Befehl kann dieselbe Datei zweimal umbenennen!

In einem leeren Testordner starten:

C:\test>copy nul 123456789.123
        1 file(s) copied.

C:\test>dir /x
 Volume in drive C is OS
 Volume Serial Number is EE2C-5A11

 Directory of C:\test

09/15/2012  07:42 PM    <DIR>                       .
09/15/2012  07:42 PM    <DIR>                       ..
09/15/2012  07:42 PM                 0 123456~1.123 123456789.123
               1 File(s)              0 bytes
               2 Dir(s)  327,237,562,368 bytes free

C:\test>ren *1* 2*3.?x

C:\test>dir /x
 Volume in drive C is OS
 Volume Serial Number is EE2C-5A11

 Directory of C:\test

09/15/2012  07:42 PM    <DIR>                       .
09/15/2012  07:42 PM    <DIR>                       ..
09/15/2012  07:42 PM                 0 223456~1.XX  223456789.123.xx
               1 File(s)              0 bytes
               2 Dir(s)  327,237,562,368 bytes free

REM Expected result = 223456789.123.x

Ich glaube, die Quellmaske stimmt *1*zuerst mit dem langen Dateinamen überein und die Datei wird in das erwartete Ergebnis von umbenannt 223456789.123.x. RENAME sucht dann weiter nach weiteren zu verarbeitenden Dateien und findet die neu benannte Datei über den neuen Kurznamen von 223456~1.X. Die Datei wird dann erneut umbenannt, was das Endergebnis von ergibt 223456789.123.xx.

Wenn ich die 8.3-Namensgenerierung deaktiviere, gibt der RENAME das erwartete Ergebnis aus.

Ich habe nicht alle Auslösebedingungen ausgearbeitet, die vorliegen müssen, um dieses merkwürdige Verhalten hervorzurufen. Ich befürchtete, dass es möglich sein könnte, einen nie endenden rekursiven RENAME zu erstellen, aber ich war nie in der Lage, einen zu induzieren.

Ich glaube, all das Folgende muss stimmen, um den Fehler auszulösen. Jeder fehlerhafte Fall, den ich sah, hatte die folgenden Bedingungen, aber nicht alle Fälle, die die folgenden Bedingungen erfüllten, waren fehlerhaft.

  • Kurze 8.3-Namen müssen aktiviert sein
  • Die Quellmaske muss mit dem ursprünglichen Langnamen übereinstimmen.
  • Die anfängliche Umbenennung muss einen Kurznamen generieren, der auch mit der Quellmaske übereinstimmt
  • Der ursprüngliche umbenannte Kurzname muss später als der ursprüngliche Kurzname sortiert werden (falls vorhanden?)
dbenham
quelle
6
Was für eine gründliche Antwort. +1.
Meder Omuraliev
Sehr aufwändig!
Andriy M
13
Auf dieser Grundlage sollte Microsoft einfach "Informationen zur Verwendung finden Sie unter superuser.com/a/475875 " in hinzufügen REN /?.
Efotinis
4
@CAD - Bei dieser Antwort handelt es sich um 100% Originalinhalt, den Simon auf meine Anfrage auf seiner Website bereitgestellt hat. Schauen Sie sich das Ende dieser SS64-Seite an und Sie werden sehen, dass Simon mir Anerkennung für die Arbeit zollt.
Dbenham
2
@ JacksOnF1re - Neue Informationen / Techniken zu meiner Antwort hinzugefügt. Sie können Ihr Copy of Präfix tatsächlich mit einem dunklen Schrägstrich löschen :ren "Copy of *.txt" "////////*"
dbenham
4

Ähnlich wie bei exebook gibt es hier eine C # -Implementierung, um den Zieldateinamen aus einer Quelldatei abzurufen.

In dbenhams Beispielen habe ich 1 kleinen Fehler gefunden:

 ren  *_*  *_NEW.*
   abc_newt_1.dat  ->  abc_newt_NEW.txt (should be: abd_newt_NEW.dat)

Hier ist der Code:

    /// <summary>
    /// Returns a filename based on the sourcefile and the targetMask, as used in the second argument in rename/copy operations.
    /// targetMask may contain wildcards (* and ?).
    /// 
    /// This follows the rules of: http://superuser.com/questions/475874/how-does-the-windows-rename-command-interpret-wildcards
    /// </summary>
    /// <param name="sourcefile">filename to change to target without wildcards</param>
    /// <param name="targetMask">mask with wildcards</param>
    /// <returns>a valid target filename given sourcefile and targetMask</returns>
    public static string GetTargetFileName(string sourcefile, string targetMask)
    {
        if (string.IsNullOrEmpty(sourcefile))
            throw new ArgumentNullException("sourcefile");

        if (string.IsNullOrEmpty(targetMask))
            throw new ArgumentNullException("targetMask");

        if (sourcefile.Contains('*') || sourcefile.Contains('?'))
            throw new ArgumentException("sourcefile cannot contain wildcards");

        // no wildcards: return complete mask as file
        if (!targetMask.Contains('*') && !targetMask.Contains('?'))
            return targetMask;

        var maskReader = new StringReader(targetMask);
        var sourceReader = new StringReader(sourcefile);
        var targetBuilder = new StringBuilder();


        while (maskReader.Peek() != -1)
        {

            int current = maskReader.Read();
            int sourcePeek = sourceReader.Peek();
            switch (current)
            {
                case '*':
                    int next = maskReader.Read();
                    switch (next)
                    {
                        case -1:
                        case '?':
                            // Append all remaining characters from sourcefile
                            targetBuilder.Append(sourceReader.ReadToEnd());
                            break;
                        default:
                            // Read source until the last occurrance of 'next'.
                            // We cannot seek in the StringReader, so we will create a new StringReader if needed
                            string sourceTail = sourceReader.ReadToEnd();
                            int lastIndexOf = sourceTail.LastIndexOf((char) next);
                            // If not found, append everything and the 'next' char
                            if (lastIndexOf == -1)
                            {
                                targetBuilder.Append(sourceTail);
                                targetBuilder.Append((char) next);

                            }
                            else
                            {
                                string toAppend = sourceTail.Substring(0, lastIndexOf + 1);
                                string rest = sourceTail.Substring(lastIndexOf + 1);
                                sourceReader.Dispose();
                                // go on with the rest...
                                sourceReader = new StringReader(rest);
                                targetBuilder.Append(toAppend);
                            }
                            break;
                    }

                    break;
                case '?':
                    if (sourcePeek != -1 && sourcePeek != '.')
                    {
                        targetBuilder.Append((char)sourceReader.Read());
                    }
                    break;
                case '.':
                    // eat all characters until the dot is found
                    while (sourcePeek != -1 && sourcePeek != '.')
                    {
                        sourceReader.Read();
                        sourcePeek = sourceReader.Peek();
                    }

                    targetBuilder.Append('.');
                    // need to eat the . when we peeked it
                    if (sourcePeek == '.')
                        sourceReader.Read();

                    break;
                default:
                    if (sourcePeek != '.') sourceReader.Read(); // also consume the source's char if not .
                    targetBuilder.Append((char)current);
                    break;
            }

        }

        sourceReader.Dispose();
        maskReader.Dispose();
        return targetBuilder.ToString().TrimEnd('.', ' ');
    }

Und hier ist eine NUnit-Testmethode zum Testen der Beispiele:

    [Test]
    public void TestGetTargetFileName()
    {
        string targetMask = "?????.?????";
        Assert.AreEqual("a", FileUtil.GetTargetFileName("a", targetMask));
        Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b", targetMask));
        Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b.c", targetMask));
        Assert.AreEqual("part1.part2", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
        Assert.AreEqual("12345.12345", FileUtil.GetTargetFileName("123456.123456.123456", targetMask));

        targetMask = "A?Z*";
        Assert.AreEqual("AZ", FileUtil.GetTargetFileName("1", targetMask));
        Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("12", targetMask));
        Assert.AreEqual("AZ.txt", FileUtil.GetTargetFileName("1.txt", targetMask));
        Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("12.txt", targetMask));
        Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("123", targetMask));
        Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("123.txt", targetMask));
        Assert.AreEqual("A2Z4", FileUtil.GetTargetFileName("1234", targetMask));
        Assert.AreEqual("A2Z4.txt", FileUtil.GetTargetFileName("1234.txt", targetMask));

        targetMask = "*.txt";
        Assert.AreEqual("a.txt", FileUtil.GetTargetFileName("a", targetMask));
        Assert.AreEqual("b.txt", FileUtil.GetTargetFileName("b.dat", targetMask));
        Assert.AreEqual("c.x.txt", FileUtil.GetTargetFileName("c.x.y", targetMask));

        targetMask = "*?.bak";
        Assert.AreEqual("a.bak", FileUtil.GetTargetFileName("a", targetMask));
        Assert.AreEqual("b.dat.bak", FileUtil.GetTargetFileName("b.dat", targetMask));
        Assert.AreEqual("c.x.y.bak", FileUtil.GetTargetFileName("c.x.y", targetMask));

        targetMask = "*_NEW.*";
        Assert.AreEqual("abcd_NEW.txt", FileUtil.GetTargetFileName("abcd_12345.txt", targetMask));
        Assert.AreEqual("abc_newt_NEW.dat", FileUtil.GetTargetFileName("abc_newt_1.dat", targetMask));
        Assert.AreEqual("abcd_123.a_NEW", FileUtil.GetTargetFileName("abcd_123.a_b", targetMask));

        targetMask = "?x.????999.*rForTheCourse";

        Assert.AreEqual("px.part999.rForTheCourse", FileUtil.GetTargetFileName("part1.part2", targetMask));
        Assert.AreEqual("px.part999.parForTheCourse", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
        Assert.AreEqual("ax.b999.crForTheCourse", FileUtil.GetTargetFileName("a.b.c", targetMask));
        Assert.AreEqual("ax.b999.CarParForTheCourse", FileUtil.GetTargetFileName("a.b.CarPart3BEER", targetMask));

    }
amrunning
quelle
Vielen Dank für den Hinweis auf den Fehler in meinem Beispiel. Ich habe meine Antwort bearbeitet, um sie zu korrigieren.
Dbenham
1

Vielleicht kann jemand dies nützlich finden. Dieser JavaScript-Code basiert auf der Antwort von dbenham oben.

Ich habe nicht sourceMaskviel getestet , aber es targetMaskpasst zu allen Beispielen von dbenham.

function maskMatch(path, mask) {
    mask = mask.replace(/\./g, '\\.')
    mask = mask.replace(/\?/g, '.')
    mask = mask.replace(/\*/g, '.+?')
    var r = new RegExp('^'+mask+'$', '')
    return path.match(r)
}

function maskNewName(path, mask) {
    if (path == '') return
    var x = 0, R = ''
    for (var m = 0; m < mask.length; m++) {
        var ch = mask[m], q = path[x], z = mask[m + 1]
        if (ch != '.' && ch != '*' && ch != '?') {
            if (q && q != '.') x++
            R += ch
        } else if (ch == '?') {
            if (q && q != '.') R += q, x++
        } else if (ch == '*' && m == mask.length - 1) {
            while (x < path.length) R += path[x++]
        } else if (ch == '*') {
            if (z == '.') {
                for (var i = path.length - 1; i >= 0; i--) if (path[i] == '.') break
                if (i < 0) {
                    R += path.substr(x, path.length) + '.'
                    i = path.length
                } else R += path.substr(x, i - x + 1)
                x = i + 1, m++
            } else if (z == '?') {
                R += path.substr(x, path.length), m++, x = path.length
            } else {
                for (var i = path.length - 1; i >= 0; i--) if (path[i] == z) break
                if (i < 0) R += path.substr(x, path.length) + z, x = path.length, m++
                else R += path.substr(x, i - x), x = i + 1
            }
        } else if (ch == '.') {
            while (x < path.length) if (path[x++] == '.') break
            R += '.'
        }
    }
    while (R[R.length - 1] == '.') R = R.substr(0, R.length - 1)
}
exebook
quelle
0

Ich habe es geschafft, diesen Code in BASIC zu schreiben, um Platzhalter-Dateinamen zu maskieren:

REM inputs a filename and matches wildcards returning masked output filename.
FUNCTION maskNewName$ (path$, mask$)
IF path$ = "" THEN EXIT FUNCTION
IF INSTR(path$, "?") OR INSTR(path$, "*") THEN EXIT FUNCTION
x = 0
R$ = ""
FOR m = 0 TO LEN(mask$) - 1
    ch$ = MID$(mask$, m + 1, 1)
    q$ = MID$(path$, x + 1, 1)
    z$ = MID$(mask$, m + 2, 1)
    IF ch$ <> "." AND ch$ <> "*" AND ch$ <> "?" THEN
        IF LEN(q$) AND q$ <> "." THEN x = x + 1
        R$ = R$ + ch$
    ELSE
        IF ch$ = "?" THEN
            IF LEN(q$) AND q$ <> "." THEN R$ = R$ + q$: x = x + 1
        ELSE
            IF ch$ = "*" AND m = LEN(mask$) - 1 THEN
                WHILE x < LEN(path$)
                    R$ = R$ + MID$(path$, x + 1, 1)
                    x = x + 1
                WEND
            ELSE
                IF ch$ = "*" THEN
                    IF z$ = "." THEN
                        FOR i = LEN(path$) - 1 TO 0 STEP -1
                            IF MID$(path$, i + 1, 1) = "." THEN EXIT FOR
                        NEXT
                        IF i < 0 THEN
                            R$ = R$ + MID$(path$, x + 1) + "."
                            i = LEN(path$)
                        ELSE
                            R$ = R$ + MID$(path$, x + 1, i - x + 1)
                        END IF
                        x = i + 1
                        m = m + 1
                    ELSE
                        IF z$ = "?" THEN
                            R$ = R$ + MID$(path$, x + 1, LEN(path$))
                            m = m + 1
                            x = LEN(path$)
                        ELSE
                            FOR i = LEN(path$) - 1 TO 0 STEP -1
                                'IF MID$(path$, i + 1, 1) = z$ THEN EXIT FOR
                                IF UCASE$(MID$(path$, i + 1, 1)) = UCASE$(z$) THEN EXIT FOR
                            NEXT
                            IF i < 0 THEN
                                R$ = R$ + MID$(path$, x + 1, LEN(path$)) + z$
                                x = LEN(path$)
                                m = m + 1
                            ELSE
                                R$ = R$ + MID$(path$, x + 1, i - x)
                                x = i + 1
                            END IF
                        END IF
                    END IF
                ELSE
                    IF ch$ = "." THEN
                        DO WHILE x < LEN(path$)
                            IF MID$(path$, x + 1, 1) = "." THEN
                                x = x + 1
                                EXIT DO
                            END IF
                            x = x + 1
                        LOOP
                        R$ = R$ + "."
                    END IF
                END IF
            END IF
        END IF
    END IF
NEXT
DO WHILE RIGHT$(R$, 1) = "."
    R$ = LEFT$(R$, LEN(R$) - 1)
LOOP
R$ = RTRIM$(R$)
maskNewName$ = R$
END FUNCTION
eoredson
quelle
4
Können Sie erklären, wie dies die in der Frage gestellten Fragen beantwortet?
Fixer1234
Es repliziert die Funktion, die REN für den Wildcard-Abgleich verwendet, z. B. die Verarbeitung von REN * .TMP * .DOC, je nachdem, wie die Funktion vor dem Umbenennen der Dateinamen aufgerufen wird.
Eoredson