Question Comment la commande Windows RENAME interprète-t-elle les caractères génériques?


Comment la commande Windows RENAME (REN) interprète-t-elle les caractères génériques?

La fonction HELP intégrée n'est d'aucune aide - elle n'aborde pas du tout les caractères génériques.

le Aide en ligne Microsoft technet XP n'est pas beaucoup mieux. Voici tout ce qu’il a à dire concernant les caractères génériques:

"Vous pouvez utiliser des caractères génériques (* et ?) dans les deux paramètres de nom de fichier. Si vous utilisez des caractères génériques dans nomfichier2, les caractères représentés par les caractères génériques seront identiques aux caractères correspondants dans nomfichier1. "

Pas beaucoup d'aide - il y a plusieurs façons d'interpréter cette déclaration.

J'ai réussi à utiliser avec succès des caractères génériques dans le filename2 paramètre à certaines occasions, mais il a toujours été un essai et une erreur. Je n'ai pas pu anticiper ce qui fonctionne et ce qui ne fonctionne pas. Fréquemment, j'ai dû écrire un petit script batch avec une boucle FOR qui analyse chaque nom pour que je puisse créer chaque nouveau nom si nécessaire. Pas très pratique.

Si je connaissais les règles de traitement des caractères génériques, je pense que je pourrais utiliser la commande RENAME plus efficacement sans avoir à recourir au traitement par lots aussi souvent. Bien entendu, la connaissance des règles serait également bénéfique pour le développement des lots.

(Oui, c'est un cas où je publie une question et une réponse appariées. J'en ai eu assez de ne pas connaître les règles et j'ai décidé d'expérimenter par moi-même. Je pense que beaucoup d'autres pourraient être intéressés par ce que j'ai découvert)


66
2017-09-16 13:59


origine


Il y a des tas de bons exemples de comment renommer avec des caractères génériques ici: lagmonster.org/docs/DOS7/z-ren1.html - Matthew Lock
@MatthewLock - Lien intéressant, mais ces règles et exemples concernent MSDOS 7, ne pas Les fenêtres. Il y a des différences significatives. Par exemple, MSDOS ne permet pas d'ajouter des caractères supplémentaires après *, Windows le fait. Cela a des conséquences énormes. J'aurais bien aimé connaître ce site; cela a peut-être facilité mon enquête. Les règles MSDOS7 sont significativement différentes des anciennes règles DOS avant les noms de fichiers longs et constituent une étape dans la manière dont Windows les gère. J'avais trouvé le nom de fichier pré-long DOS règles, et ils étaient sans valeur pour mon enquête. - dbenham
Je ne le savais pas;) - Matthew Lock


Réponses:


Ces règles ont été découvertes après des tests approfondis sur une machine Vista. Aucun test n'a été effectué avec Unicode dans les noms de fichiers.

RENAME nécessite 2 paramètres - un masque source, suivi d'un masque cible. Le sourceMask et le targetMask peuvent tous deux contenir * et / ou ? jokers Le comportement des caractères génériques change légèrement entre les masques source et cible.

Remarque - REN peut être utilisé pour renommer un dossier, mais les caractères génériques sont ne pas autorisé dans le sourceMask ou targetMask lors du renommage d'un dossier. Si le masque source correspond à au moins un fichier, le ou les fichiers seront renommés et les dossiers seront ignorés. Si le masque source correspond uniquement aux dossiers et non aux fichiers, une erreur de syntaxe est générée si des caractères génériques apparaissent dans la source ou la cible. Si le masque source ne correspond à rien, une erreur "fichier introuvable" est générée.

En outre, lors de la modification du nom des fichiers, les caractères génériques ne sont autorisés que dans la partie nom de fichier du masque source. Les caractères génériques ne sont pas autorisés dans le chemin d'accès au nom du fichier.

sourceMask

Le masque source fonctionne comme un filtre pour déterminer quels fichiers sont renommés. Les caractères génériques fonctionnent ici comme avec toute autre commande qui filtre les noms de fichiers.

  • ? - Correspond à n'importe quel caractère 0 ou 1 sauf  .  Ce joker est gourmand - il consomme toujours le prochain caractère s'il ne s'agit pas d'un .  Cependant, il ne correspondra à rien sans échec si à la fin du nom ou si le prochain caractère est un .

  • * - Correspond à tous les caractères 0 ou plus comprenant  . (avec une exception ci-dessous). Ce joker n'est pas gourmand. Il correspondra aussi peu ou autant que nécessaire pour permettre aux personnages suivants de correspondre.

Tous les caractères non joker doivent correspondre eux-mêmes, avec quelques exceptions de cas particuliers.

  • . - Correspond à lui-même ou il peut correspondre à la fin du nom (rien) s'il ne reste plus de caractères. (Remarque - un nom Windows valide ne peut pas se terminer par .)

  • {space} - Correspond à lui-même ou il peut correspondre à la fin du nom (rien) s'il ne reste plus de caractères. (Remarque - un nom Windows valide ne peut pas se terminer par {space})

  • *. à la fin - Correspond à tous les caractères 0 ou plus sauf  .  Le terminateur . peut effectivement être une combinaison de . et {space} tant que le dernier personnage du masque est .  C'est la seule et unique exception où * ne correspond pas simplement à un ensemble de caractères.

Les règles ci-dessus ne sont pas si complexes. Mais il y a une autre règle très importante qui rend la situation confuse: Le masque source est comparé à la fois au nom long et au nom abrégé 8.3 (s'il existe). Cette dernière règle peut rendre l'interprétation des résultats très délicate, car elle n'est pas toujours évidente lorsque le masque correspond au nom abrégé.

Il est possible d'utiliser RegEdit pour désactiver la génération de noms 8.3 courts sur des volumes NTFS. À ce stade, l'interprétation des résultats des masques de fichiers est beaucoup plus simple. Tous les noms abrégés générés avant la désactivation des noms abrégés resteront.

targetMask

Remarque - Je n'ai pas effectué de test rigoureux, mais il semble que ces mêmes règles fonctionnent également pour le nom cible de la commande COPY.

Le targetMask spécifie le nouveau nom. Il est toujours appliqué au nom complet complet; Le targetMask n'est jamais appliqué au nom court 8.3, même si le masque source correspond au nom abrégé 8.3.

La présence ou l'absence de caractères génériques dans le masque source n'a aucun impact sur le traitement des caractères génériques dans le masque cible.

Dans la discussion suivante - c représente n'importe quel personnage qui n'est pas *, ?, ou .

Le targetMask est traité contre le nom de source strictement de gauche à droite, sans retour en arrière.

  • c - Avance la position dans le nom de la source tant que le caractère suivant n'est pas . et ajoute c au nom de la cible. (Remplace le caractère qui était en source par c, mais ne remplace jamais .)

  • ? - Correspond au caractère suivant du nom long source et l'ajoute au nom de la cible tant que le caractère suivant n'est pas .  Si le prochain caractère est . ou si à la fin du nom de la source, aucun caractère n'est ajouté au résultat et la position actuelle dans le nom de la source est inchangée.

  • * à la fin de targetMask - Ajoute tous les caractères restants de la source à la cible. Si déjà à la fin de la source, alors ne fait rien.

  • *c - Correspond à tous les caractères sources depuis la position actuelle jusqu'à la dernière occurrence de c (correspondance gourmande sensible à la casse) et ajoute le jeu de caractères correspondant au nom de la cible. Si c est introuvable, alors tous les caractères restants de la source sont ajoutés, suivis par cC'est la seule situation que je connaisse où la correspondance des modèles de fichiers Windows est sensible à la casse.

  • *. - Correspond à tous les caractères source de la position actuelle à travers le dernier apparition de . (correspondance gourmande) et ajoute l’ensemble des caractères correspondants au nom de la cible. Si . est introuvable, alors tous les caractères restants de la source sont ajoutés, suivis par .

  • *? - Ajoute tous les caractères restants de la source à la cible. Si déjà à la fin de la source ne fait alors rien.

  • . sans pour autant * devant - Avance la position dans la source à travers le premier apparition de . sans copier aucun caractère, et ajoute . au nom de la cible. Si . ne se trouve pas dans la source, puis avance à la fin de la source et ajoute . au nom de la cible.

Une fois le targetMask épuisé, tout suivi . et {space} sont supprimés à la fin du nom de la cible résultante, car les noms de fichiers Windows ne peuvent pas se terminer par . ou {space}

Quelques exemples pratiques

Remplacer un personnage en 1ère et 3ème positions avant toute extension (ajoute un 2ème ou 3ème caractère s'il n'existe pas encore)

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

Changer l'extension (finale) de chaque fichier

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

Ajouter une extension à chaque fichier

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

Supprimez toute extension supplémentaire après l'extension initiale. Notez que adéquate ? doit être utilisé pour conserver le nom existant complet et l’extension initiale.

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)

Comme ci-dessus, mais filtrez les fichiers avec un nom initial et / ou une extension supérieure à 5 caractères afin qu'ils ne soient pas tronqués. (Evidemment pourrait ajouter un autre ? à chaque extrémité de targetMask pour préserver les noms et les extensions jusqu'à 6 caractères de long)

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)

Changer les caractères après le dernier _ dans le nom et tenter de préserver l'extension. (Ne fonctionne pas correctement si _ apparaît en extension)

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)

Tout nom peut être divisé en composants délimités par .  Les caractères ne peuvent être ajoutés ou supprimés qu'à la fin de chaque composant. Les caractères ne peuvent pas être supprimés ou ajoutés au début ou au milieu d'un composant tout en conservant le reste avec des caractères génériques. Les substitutions sont autorisées n'importe où.
MODIFIER  - Cet exemple a été corrigé. La version antérieure contenait des informations incorrectes sur les masques source incompatibles

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

Si les noms courts sont activés, alors un masque source contenant au moins 8 ? pour le nom et au moins 3 ? car l'extension correspondra à tous les fichiers car elle correspondra toujours au nom court 8.3.

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


Quirk / bug utile? pour supprimer les préfixes de nom

Ce message SuperUser décrit comment un ensemble de barres obliques (/) peut être utilisé pour supprimer des caractères principaux d’un nom de fichier. Une barre oblique est requise pour chaque caractère à supprimer. J'ai confirmé le comportement sur une machine Windows 10.

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

Cette technique ne fonctionne que si les masques source et cible sont placés entre guillemets. Tous les formulaires suivants, sans les devis requis, échouent avec cette erreur: 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

le / ne peut pas être utilisé pour supprimer des caractères au milieu ou à la fin d'un nom de fichier. Il ne peut supprimer que les caractères (préfixes) en tête.

Techniquement le / ne fonctionne pas comme un joker. Au lieu de cela, il effectue une simple substitution de caractères, mais après la substitution, la commande REN reconnaît que / n'est pas valide dans un nom de fichier et supprime le début / des barres obliques du nom. REN donne une erreur de syntaxe si elle détecte / au milieu d'un nom de cible.


Bug RENAME possible - une seule commande peut renommer le même fichier deux fois!

En commençant dans un dossier de test vide:

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

Je crois le sourceMask *1* correspond d'abord au nom de fichier long et le fichier est renommé pour le résultat attendu de 223456789.123.x. RENAME continue alors à rechercher plus de fichiers à traiter et trouve le fichier nouvellement nommé via le nouveau nom abrégé de 223456~1.X. Le fichier est ensuite renommé, donnant le résultat final de 223456789.123.xx.

Si je désactive la génération de nom 8.3, le RENAME donne le résultat attendu.

Je n'ai pas entièrement élaboré toutes les conditions de déclenchement qui doivent exister pour induire ce comportement étrange. Je craignais qu'il soit possible de créer un RENAME récursif sans fin, mais je n'ai jamais pu en créer un.

Je crois que tout ce qui suit doit être vrai pour provoquer le bogue. Chaque cas détecté que j'ai vu avait les conditions suivantes, mais tous les cas rencontrant les conditions suivantes n'ont pas été corrigés.

  • Les noms courts 8.3 doivent être activés
  • Le masque source doit correspondre au nom long d'origine.
  • Le renommage initial doit générer un nom abrégé correspondant au masque source
  • Le nom abrégé initial renommé doit être trié plus tard que le nom abrégé d'origine (s'il existait?)

103
2017-09-16 14:00



Quelle réponse complète .. +1. - meder omuraliev
Extrêmement élaboré! - Andriy M
Sur cette base, Microsoft devrait simplement ajouter "Pour usage, voir superuser.com/a/475875 " dans REN /?. - efotinis
@CAD - Cette réponse est 100% du contenu original que Simon a inclus sur son site à ma demande. Regardez en bas de cette page SS64 et vous verrez que Simon me crédite pour le travail. - dbenham
@ JacksOnF1re - Nouvelles informations / techniques ajoutées à ma réponse. Vous pouvez réellement supprimer votre Copy of  préfixe utilisant une technique obscure de barre oblique: ren "Copy of *.txt" "////////*" - dbenham


Semblable à exebook, voici une implémentation C # pour obtenir le nom du fichier cible à partir d'un fichier source.

J'ai trouvé 1 petite erreur dans les exemples de dbenham:

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

Voici le 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('.', ' ');
    }

Et voici une méthode de test NUnit pour tester les exemples:

    [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));

    }

3
2017-12-16 10:13



Merci pour la tête au sujet de l'erreur dans mon exemple. J'ai édité ma réponse pour la réparer. - dbenham


Peut-être que quelqu'un peut trouver cela utile. Ce code JavaScript est basé sur la réponse de dbenham ci-dessus.

Je n'ai pas testé sourceMask beaucoup, mais targetMask ne correspond à tous les exemples donnés par 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)
}

1
2018-04-09 17:07





J'ai réussi à écrire ce code en BASIC pour masquer les noms de fichiers génériques:

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

0
2017-10-13 01:27



Pouvez-vous préciser comment cela répond à ce qui a été demandé dans la question? - fixer1234
Il réplique la fonction utilisée par REN pour les correspondances de caractères génériques, telles que le traitement de REN * .TMP * .DOC en fonction de la façon dont la fonction est appelée avant de renommer les noms de fichiers. - eoredson