Windows RENAMEコマンドはどのようにワイルドカードを解釈しますか?


78

Windows RENAME(REN)コマンドはどのようにワイルドカードを解釈しますか?

組み込みのヘルプ機能は役に立ちません-ワイルドカードにはまったく対応していません。

マイクロソフトTechNetのXPのオンラインヘルプははるかに良いではありません。ワイルドカードに関して言う必要があるのは次のとおりです。

「どちらのファイル名パラメーターでもワイルドカード(*および?)を使用できます。filename2でワイルドカードを使用すると、ワイルドカードで表される文字はfilename1の対応する文字と同じになります。」

あまり助けにはなりません-ステートメントを解釈する方法はたくさんあります。

場合によってはfilename2パラメーターでワイルドカードを使用できましたが、試行錯誤を繰り返してきました。何が機能し、何が機能しないかを予測できませんでした。多くの場合、必要に応じて新しい名前を作成できるように、各名前を解析するFORループを使用して小さなバッチスクリプトを記述する必要がありました。あまり便利ではありません。

ワイルドカードの処理方法のルールを知っていれば、バッチ処理を頻繁に行う必要なく、RENAMEコマンドをより効果的に使用できると思います。もちろん、ルールを知っていればバッチ開発にも役立ちます。

(はい-これはペアの質問と回答を投稿している場合です。ルールを知らないことにうんざりして、自分で実験することに決めました。他の多くの人が私が発見したことに興味があるかもしれません)


ここでは、ワイルドカードで名前を変更する方法の良い例の山があります:lagmonster.org/docs/DOS7/z-ren1.html
マシュー・ロック

5
@MatthewLock-興味深いリンクですが、これらのルールと例はMSDOS 7用であり、 Windows用ではありません。大きな違いがあります。たとえば、MSDOSでは*、Windowsの後に追加の文字を追加できません。それは大きな結果をもたらします。私はそのサイトについて知っていたらよかったのに。調査が容易になったかもしれません。MSDOS7の規則は、長いファイル名の前の古いDOSの規則とは大きく異なり、Windowsでの処理方法の方向性を示しています。長いファイル名より前のDOSルールを見つけたので、調査に値しませんでした。
dbenham

私はそれを知りませんでした;)
マシューロック

回答:


117

これらのルールは、Vistaマシンでの広範なテストの後に発見されました。ファイル名にUnicodeを使用したテストは行われていません。

RENAMEには、sourceMaskとそれに続くtargetMaskという2つのパラメーターが必要です。sourceMaskとtargetMaskの両方が含まれていることができ*、および/または?ワイルドカード。ワイルドカードの動作は、ソースマスクとターゲットマスクの間でわずかに変化します。

注意 - RENは、フォルダの名前を変更するために使用することができますが、ワイルドカードはされていないフォルダ名を変更する場合sourceMaskまたはtargetMaskのいずれかで許可さ。sourceMaskが少なくとも1つのファイルと一致する場合、ファイルの名前が変更され、フォルダーは無視されます。sourceMaskがフォルダーではなくファイルのみに一致する場合、ワイルドカードがソースまたはターゲットに表示されると構文エラーが生成されます。sourceMaskが何にも一致しない場合、「ファイルが見つかりません」というエラーが発生します。

また、ファイルの名前を変更する場合、ワイルドカードはsourceMaskのファイル名部分でのみ許可されます。ファイル名に至るまでのパスにワイルドカードは使用できません。

sourceMask

sourceMaskは、名前を変更するファイルを決定するフィルターとして機能します。ワイルドカードは、ファイル名をフィルタリングする他のコマンドと同じように機能します。

  • ?- このワイルドカードを除く 任意の0または1文字と一致します。.貪欲です-それがaでない場合は常に次の文字を消費します。. ただし、名前の最後または次の文字がa.

  • *- 以下を含む 0個以上の文字に一致します.(以下の1つの例外を除く)。このワイルドカードは貪欲ではありません。それは、後続の文字を一致させるために必要なだけ、または必要に応じて一致します。

ワイルドカード以外のすべての文字は、いくつかの特殊なケースの例外を除き、自分自身と一致する必要があります。

  • .-それ自体と一致するか、文字が残っていない場合は名前の末尾と一致します(何もない)。(注-有効なWindows名はで終わることはできません.

  • {space}-それ自体と一致するか、文字が残っていない場合は名前の末尾と一致します(何もない)。(注-有効なWindows名はで終わることはできません{space}

  • *.終わりに-任意の0個以上の文字にマッチ除く . 終端は.、実際の任意の組み合わせを使用できます.し、{space}限りマスクの非常に最後の文字があるとして. これが唯一の例外で*、単に任意の文字セットと一致していません。

上記の規則はそれほど複雑ではありません。しかし、状況を混乱させるもう1つの非常に重要なルールがあります。sourceMaskは、長い名前と短い8.3名(存在する場合)の両方と比較されます。この最後のルールにより、マスクが短い名前で一致する場合は常に明らかではないため、結果の解釈が非常に難しくなります。

RegEditを使用して、NTFSボリュームでの短い8.3形式の名前の生成を無効にすることができます。この時点では、ファイルマスクの結果の解釈ははるかに簡単です。ショートネームを無効にする前に生成されたショートネームは残ります。

targetMask

注-厳密なテストは行っていませんが、COPYコマンドのターゲット名にも同じルールが適用されるようです

targetMaskは新しい名前を指定します。これは常に完全な長い名前に適用されます。sourceMaskが短い8.3名に一致した場合でも、targetMaskが短い8.3名に適用されることはありません。

sourceMaskでのワイルドカードの有無は、targetMaskでのワイルドカードの処理方法に影響を与えません。

以下の議論では- cではない任意の文字を表し*?または.

targetMaskはソース名に対して厳密に左から右に処理され、バックトラッキングは行われません。

  • c-次の文字がターゲット名にない限り、ソース名内の位置を進め、ターゲット名に.追加cします。(ソースにあった文字をで置き換えますcが、決して置き換えません.

  • ?-ソースの長い名前の次の文字に一致し、次の文字がそうでない限りターゲット名に追加します。次の文字がある. 場合、.またはソース名の最後にある場合、結果と現在のソース名内の位置は変更されません。

  • *targetMaskの最後-残りのすべての文字をソースからターゲットに追加します。すでにソースの最後にある場合、何もしません。

  • *c-現在の位置からc(大文字と小文字を区別する貪欲な一致)の最後の出現までのすべてのソース文字に一致し、一致した文字セットをターゲット名に追加します。cが見つからない場合、ソースからの残りのすべての文字が追加され、その後にc これがWindowsファイルのパターンマッチングで大文字と小文字が区別される唯一の状況です。

  • *.-現在の位置から最後の出現.(貪欲な一致)までのすべてのソース文字に一致し、一致した文字セットをターゲット名に追加します。.が見つからない場合、ソースからの残りのすべての文字が追加され、その後に.

  • *?-残りのすべての文字をソースからターゲットに追加します。すでにソースの最後にある場合は何もしません。

  • .*最前面なし- 文字をコピーせずに最初の出現までソース内の位置を進め、ターゲット名に.追加.します。.ソースで見つからない場合は、ソースの最後に進み.、ターゲット名に追加します。

targetMaskが枯渇した後、任意の末尾.とは{space}、Windowsのファイル名はで終わることができないので、結果としてターゲット名の末尾をオフにトリミングされています.{space}

いくつかの実用的な例

拡張子の1番目と3番目の位置にある文字を置き換えます(まだ存在しない場合は2番目または3番目の文字を追加します)

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

すべてのファイルの(最終)拡張子を変更する

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

すべてのファイルに拡張子を追加します

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

最初の拡張子の後に余分な拡張子を削除します。?完全な既存の名前と初期拡張子を保持するには、十分なものを使用する必要があることに注意してください。

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)

上記と同じですが、切り捨てられないように、5文字より長い初期名または拡張子を持つファイルを除外します。(明らかに?、ターゲットマスクの両端に追加を追加して、最大6文字までの名前と拡張子を保持できます)

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)

_名前の最後の文字を変更し、拡張子を保持しようとします。(_拡張機能に表示されている場合、正常に動作しません)

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)

任意の名前は、文字で区切られたコンポーネントに分割でき. 、各コンポーネントの末尾からのみ追加または削除できます。文字をコンポーネントの先頭または中央から削除したり追加したりすることはできませんが、残りはワイルドカードで保持されます。置換はどこでも許可されています。

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

短い名前が有効になっている場合、少なくとも8つ?の名前と3つ?の拡張子を持つsourceMaskは、常に短い8.3名に一致するため、すべてのファイルに一致します。

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


役に立つ癖/バグ?名前のプレフィックスを削除する

このSuperUserの投稿では、スラッシュ(/)のセットを使用してファイル名から先頭の文字を削除する方法について説明しています。削除する文字ごとに1つのスラッシュが必要です。Windows 10マシンでの動作を確認しました。

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

この手法は、ソースマスクとターゲットマスクの両方が二重引用符で囲まれている場合にのみ機能します。必要な引用符がない次のすべてのフォームは、このエラーで失敗します。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

/使用して、ファイル名の中央または末尾の文字を削除することはできません。先行(プレフィックス)文字のみを削除できます。

技術的に/は、ワイルドカードとして機能していません。むしろ、単純な文字置換を行っていますが、置換後、RENコマンド/はファイル名で無効であることを認識し/、名前から先頭のスラッシュを取り除きます。RENは、/ターゲット名の途中で検出すると構文エラーを返します。


RENAMEの可能性のあるバグ-1つのコマンドで同じファイルの名前を2回変更することがあります!

空のテストフォルダーから開始:

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

sourceMaskは*1*最初に長いファイル名と一致し、ファイルの名前はの期待される名前に変更されると思います223456789.123.x。その後、RENAMEは処理するファイルを探し続け、新しい短い名前のを介して新しい名前のファイルを見つけます223456~1.X。その後、ファイルの名前が再び変更され、最終的な結果が得られ223456789.123.xxます。

8.3形式の名前の生成を無効にすると、RENAMEで期待される結果が得られます。

この奇妙な振る舞いを誘発するために存在しなければならないトリガー条件のすべてを完全に解決したわけではありません。終わりのない再帰的なRENAMEを作成できるかもしれないと心配していましたが、それを誘導することはできませんでした。

バグを引き起こすには、以下のすべてが真実でなければならないと思います。私が見たバグのあるケースにはすべて以下の条件がありましたが、以下の条件を満たすすべてのケースがバグだったわけではありません。

  • 短い8.3名を有効にする必要があります
  • sourceMaskは元の長い名前と一致する必要があります。
  • 最初の名前変更では、sourceMaskにも一致する短い名前を生成する必要があります
  • 名前が変更された最初の短縮名は、元の短縮名よりも後でソートする必要があります(存在する場合)。

6
なんて徹底的な答え.. +1。
メダーオムラリエフ

ものすごく手の込んだ!
アンドリーM

13
これに基づき、Microsoftはちょうど「を参照してください、使用方法については追加する必要がありsuperuser.com/a/475875の中で」REN /?
efotinis

4
@CAD-この答えは、サイモンが私のリクエストに応じて彼のサイトに含めた100%オリジナルのコンテンツです。そのSS64ページの下部を見ると、Simonが私にその功績を称えてくれていることがわかります。
-dbenham

2
@ JacksOnF1re-私の答えに新しい情報/技術が追加されました。Copy of あいまいなスラッシュテクニックを使用して、実際にプレフィックスを削除できますren "Copy of *.txt" "////////*"
。– dbenham

4

exebookと同様に、ソースファイルからターゲットファイル名を取得するC#の実装を次に示します。

dbenhamの例で1つの小さなエラーが見つかりました。

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

コードは次のとおりです。

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

そして、例をテストするためのNUnitテストメソッドがあります。

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

    }

私の例の間違いについて頭を上げてくれてありがとう。回答を編集して修正しました。
dbenham 14

1

私はワイルドカードのファイル名をマスクするためにBASICでこのコードを書くことに成功しました:

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

4
これが質問で尋ねられたものにどのように答えるかを明確にできますか?
fixer1234

ファイル名の名前を変更する前に関数がどのように呼び出されるかに応じて、REN * .TMP * .DOCの処理など、RENがワイルドカードマッチングに使用する関数を複製します。
eoredson

1

たぶん誰かがこれを役に立つと思うかもしれません。このJavaScriptコードは、上記のdbenhamによる回答に基づいています。

私はあまりテストしませんでしsourceMasktargetMaskが、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)
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.