Bagaimana perintah Windows RENAME menginterpretasikan wildcard?

77

Bagaimana perintah Windows RENAME (REN) mengartikan wildcard?

Fasilitas BANTUAN bawaan tidak membantu - tidak menangani wildcard sama sekali.

The Microsoft TechNet XP bantuan online tidak jauh lebih baik. Inilah yang bisa dikatakan tentang wildcard:

"Anda dapat menggunakan wildcard ( *dan ?) di salah satu parameter nama file. Jika Anda menggunakan wildcard di nama file2, karakter yang diwakili oleh wildcard akan identik dengan karakter terkait di namafile1."

Tidak banyak membantu - ada banyak cara pernyataan itu dapat diinterpretasikan.

Saya telah berhasil menggunakan wildcard dalam parameter filename2 pada beberapa kesempatan, tetapi selalu ada trial and error. Saya belum dapat mengantisipasi apa yang berhasil dan yang tidak. Seringkali saya harus menggunakan skrip batch kecil dengan loop FOR yang mem-parsing setiap nama sehingga saya dapat membangun setiap nama baru sesuai kebutuhan. Sangat tidak nyaman.

Jika saya tahu aturan untuk bagaimana wildcard diproses maka saya pikir saya bisa menggunakan perintah RENAME lebih efektif tanpa harus menggunakan batch sesering mungkin. Tentu saja mengetahui aturan juga akan menguntungkan pengembangan batch.

(Ya - ini adalah kasus di mana saya memposting pertanyaan dan jawaban berpasangan. Saya bosan tidak mengetahui aturan dan memutuskan untuk bereksperimen sendiri. Saya pikir banyak orang lain mungkin tertarik pada apa yang saya temukan)

dbenham
sumber
Ada banyak contoh bagus tentang cara mengganti nama dengan wildcard di sini: lagmonster.org/docs/DOS7/z-ren1.html
Matthew Lock
5
@ MatthewLock - Tautan yang menarik, tetapi aturan dan contoh itu untuk MSDOS 7, bukan Windows. Ada perbedaan yang signifikan. Misalnya, MSDOS tidak mengizinkan menambahkan karakter tambahan setelahnya *, Windows tidak. Itu memiliki konsekuensi besar. Saya berharap saya tahu tentang situs itu; mungkin membuat penyelidikan saya lebih mudah. Aturan MSDOS7 sangat berbeda dari aturan DOS lama sebelum nama file lama, dan mereka adalah langkah ke arah bagaimana Windows menanganinya. Saya telah menemukan aturan nama file DOS nama lama, dan mereka tidak berharga untuk penyelidikan saya.
dbenham
Saya tidak tahu itu;)
Matthew Lock

Jawaban:

116

Aturan-aturan ini ditemukan setelah pengujian ekstensif pada mesin Vista. Tidak ada tes yang dilakukan dengan unicode dalam nama file.

RENAME membutuhkan 2 parameter - sourceMask, diikuti oleh targetMask. SourceMask dan targetMask dapat berisi *dan / atau ?wildcard. Perilaku wildcard sedikit berubah antara masker sumber dan target.

Catatan - REN dapat digunakan untuk mengganti nama folder, tetapi wildcard tidak diperbolehkan di sourceMask atau targetMask saat mengganti nama folder. Jika sourceMask cocok dengan setidaknya satu file, maka file tersebut akan diganti namanya dan folder akan diabaikan. Jika sourceMask hanya cocok dengan folder dan bukan file, maka kesalahan sintaksis dihasilkan jika wildcard muncul di sumber atau target. Jika sourceMask tidak cocok dengan apa pun, maka hasil kesalahan "file tidak ditemukan".

Juga, ketika mengganti nama file, wildcard hanya diperbolehkan di bagian nama file dari sourceMask. Wildcard tidak diperbolehkan di jalur yang mengarah ke nama file.

sourceMask

SourceMask berfungsi sebagai filter untuk menentukan file mana yang diganti namanya. Wildcard bekerja di sini sama dengan perintah lain yang memfilter nama file.

  • ?- Cocok dengan 0 atau 1 karakter apa pun kecuali . wildcard ini rakus - selalu mengkonsumsi karakter berikutnya jika bukan karakter . Namun tidak akan cocok dengan apa pun tanpa kegagalan jika di akhir nama atau jika karakter berikutnya adalah.

  • *- Cocok dengan 0 karakter atau lebih termasuk . (dengan satu pengecualian di bawah). Kartu liar ini tidak serakah. Itu akan cocok sesedikit atau sebanyak yang diperlukan untuk memungkinkan karakter berikutnya untuk cocok.

Semua karakter non-wildcard harus cocok dengan diri mereka sendiri, dengan beberapa pengecualian kasus khusus.

  • .- Cocokkan sendiri atau dapat cocok dengan akhir nama (tidak ada) jika tidak ada lagi karakter yang tersisa. (Catatan - nama Windows yang valid tidak dapat diakhiri dengan .)

  • {space}- Cocokkan sendiri atau dapat cocok dengan akhir nama (tidak ada) jika tidak ada lagi karakter yang tersisa. (Catatan - nama Windows yang valid tidak dapat diakhiri dengan {space})

  • *.pada akhirnya - Mencocokkan 0 karakter atau lebih kecuali . Pengakhiran .sebenarnya dapat berupa kombinasi .dan {space}selama karakter terakhir dalam mask adalah . Ini adalah satu-satunya pengecualian di mana *tidak hanya cocok dengan set karakter apa pun.

Aturan di atas tidak serumit itu. Tetapi ada satu lagi aturan yang sangat penting yang membuat situasi membingungkan: SourceMask dibandingkan dengan nama panjang dan nama pendek 8.3 (jika ada). Aturan terakhir ini dapat membuat interpretasi hasil sangat rumit, karena tidak selalu jelas ketika topeng dicocokkan melalui nama pendek.

Dimungkinkan untuk menggunakan RegEdit untuk menonaktifkan pembuatan nama-nama pendek 8.3 pada volume NTFS, di mana titik interpretasi hasil mask file jauh lebih mudah. Setiap nama pendek yang dibuat sebelum menonaktifkan nama pendek akan tetap ada.

targetMask

Catatan - Saya belum melakukan pengujian yang ketat, tetapi tampaknya aturan yang sama ini juga berfungsi untuk nama target dari perintah COPY

TargetMask menentukan nama baru. Itu selalu diterapkan pada nama panjang penuh; TargetMask tidak pernah diterapkan ke nama 8.3 pendek, bahkan jika sourceMask cocok dengan nama 8.3 pendek.

Ada atau tidaknya wildcard di sourceMask tidak berdampak pada bagaimana wildcard diproses di targetMask.

Dalam diskusi berikut - cmewakili karakter apapun yang tidak *, ?atau.

TargetMask diproses berdasarkan nama sumber ketat dari kiri ke kanan tanpa pelacakan-kembali.

  • c- Meningkatkan posisi dalam nama sumber selama karakter berikutnya tidak .dan ditambahkan cke nama target. (Mengganti karakter dengan sumber c, tetapi tidak pernah menggantikan .)

  • ?- Cocokkan karakter berikutnya dari nama panjang sumber dan menambahkannya ke nama target selama karakter berikutnya tidak . Jika karakter berikutnya adalah .atau jika pada akhir nama sumber maka tidak ada karakter yang ditambahkan ke hasil dan saat ini posisi dalam nama sumber tidak berubah.

  • *di akhir targetMask - Menambahkan semua karakter yang tersisa dari sumber ke target. Jika sudah di akhir sumber, maka tidak melakukan apa-apa.

  • *c- Mencocokkan semua karakter sumber dari posisi saat ini melalui kejadian terakhir c(pertandingan serakah yang peka terhadap huruf besar-kecil) dan menambahkan rangkaian karakter yang cocok ke nama target. Jika ctidak ditemukan, maka semua karakter yang tersisa dari sumber ditambahkan, diikuti oleh c Ini adalah satu-satunya situasi yang saya tahu di mana pencocokan pola file Windows sensitif huruf.

  • *.- Mencocokkan semua karakter sumber dari posisi saat ini melalui kejadian terakhir. (pertandingan serakah) dan menambahkan set karakter yang cocok ke nama target. Jika .tidak ditemukan, maka semua karakter yang tersisa dari sumber ditambahkan, diikuti oleh.

  • *?- Menambahkan semua karakter yang tersisa dari sumber ke target. Jika sudah di akhir sumber maka tidak melakukan apa-apa.

  • .without *in front - Meningkatkan posisi dalam sumber melalui kejadian pertama. tanpa menyalin karakter apa pun, dan menambahkan .ke nama target. Jika .tidak ditemukan dalam sumber, maka maju ke akhir sumber dan tambahkan .ke nama target.

Setelah targetMask telah habis, semua yang tertinggal .dan {space}dipangkas dari akhir nama target yang dihasilkan karena nama file Windows tidak dapat diakhiri dengan .atau{space}

Beberapa contoh praktis

Ganti karakter di posisi 1 dan 3 sebelum ekstensi apa pun (tambahkan karakter 2 atau 3 jika belum ada)

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

Ubah ekstensi (final) dari setiap file

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

Tambahkan ekstensi ke setiap file

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

Hapus ekstensi tambahan apa pun setelah ekstensi awal. Perhatikan bahwa memadai ?harus digunakan untuk mempertahankan nama lengkap yang ada dan ekstensi awal.

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)

Sama seperti di atas, tetapi saring file dengan nama awal dan / atau ekstensi lebih dari 5 karakter sehingga tidak terpotong. (Jelas bisa menambahkan tambahan ?di salah satu ujung targetMask untuk mempertahankan nama dan ekstensi hingga 6 karakter)

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)

Ubah karakter setelah _nama terakhir dan coba pertahankan ekstensi. (Tidak berfungsi dengan baik jika _muncul dalam ekstensi)

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)

Nama apa pun dapat dipecah menjadi komponen yang dibatasi oleh . Karakter hanya dapat ditambahkan atau dihapus dari akhir setiap komponen. Karakter tidak dapat dihapus dari atau ditambahkan ke awal atau tengah komponen sambil mempertahankan sisanya dengan wildcard. Pergantian diperbolehkan di mana saja.

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

Jika nama pendek diaktifkan, maka sourceMask dengan setidaknya 8 ?untuk nama dan setidaknya 3 ?untuk ekstensi akan cocok dengan semua file karena akan selalu cocok dengan nama pendek 8.3.

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


Keunikan / bug yang berguna? untuk menghapus awalan nama

Posting SuperUser ini menjelaskan bagaimana satu set garis miring ( /) dapat digunakan untuk menghapus karakter utama dari nama file. Diperlukan satu tebasan untuk setiap karakter yang akan dihapus. Saya telah mengkonfirmasi perilaku di mesin Windows 10.

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

Teknik ini hanya berfungsi jika topeng sumber dan target dilampirkan dalam tanda kutip ganda. Semua formulir berikut tanpa tanda kutip yang diperlukan gagal dengan kesalahan ini: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

Tidak /dapat digunakan untuk menghapus karakter apa pun di tengah atau akhir nama file. Itu hanya dapat menghapus karakter utama (awalan).

Secara teknis /tidak berfungsi sebagai wildcard. Alih-alih melakukan substitusi karakter sederhana, tetapi kemudian setelah substitusi, perintah REN mengakui bahwa /tidak valid dalam nama file, dan menghapus /garis miring dari namanya. REN memberikan kesalahan sintaks jika mendeteksi /di tengah nama target.


Kemungkinan bug RENAME - satu perintah dapat mengganti nama file yang sama dua kali!

Mulai di folder tes kosong:

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

Saya percaya sourceMask *1*pertama kali cocok dengan nama file yang panjang, dan file tersebut diubah namanya menjadi hasil yang diharapkan 223456789.123.x. RENAME kemudian terus mencari lebih banyak file untuk diproses dan menemukan file yang baru dinamai melalui nama pendek baru 223456~1.X. File tersebut kemudian diganti lagi memberikan hasil akhir dari 223456789.123.xx.

Jika saya menonaktifkan generasi nama 8,3 maka RENAME memberikan hasil yang diharapkan.

Saya belum sepenuhnya menyelesaikan semua kondisi pemicu yang harus ada untuk memicu perilaku aneh ini. Saya khawatir bahwa mungkin saja membuat RENAME rekursif yang tidak pernah berakhir, tetapi saya tidak pernah bisa memunculkannya.

Saya percaya semua hal berikut harus benar untuk menginduksi bug. Setiap kasing yang saya lihat memiliki kondisi berikut, tetapi tidak semua kasing yang memenuhi ketentuan berikut disadap.

  • Nama 8,3 pendek harus diaktifkan
  • SourceMask harus cocok dengan nama panjang asli.
  • Ganti nama awal harus menghasilkan nama pendek yang juga cocok dengan sourceMask
  • Nama pendek berganti nama awal harus diurutkan lebih lambat dari nama pendek asli (jika ada?)
dbenham
sumber
6
Apa jawaban yang menyeluruh .. +1.
meder omuraliev
Sangat rumit!
Andriy M
13
Berdasarkan ini, Microsoft seharusnya menambahkan "Untuk penggunaan, lihat superuser.com/a/475875 " di REN /?.
efotinis
4
@CAD - Jawaban ini adalah 100% konten asli yang Simon sertakan di situsnya atas permintaan saya. Lihatlah bagian bawah halaman SS64 dan Anda akan melihat bahwa Simon memberi saya penghargaan untuk pekerjaan itu.
dbenham
2
@ JacksOnF1re - Informasi / teknik baru ditambahkan ke jawaban saya. Anda benar-benar dapat menghapus Copy of awalan menggunakan teknik garis miring yang tidak jelas:ren "Copy of *.txt" "////////*"
dbenham
4

Mirip dengan exebook, inilah implementasi C # untuk mendapatkan nama file target dari sourcefile.

Saya menemukan 1 kesalahan kecil dalam contoh dbenham:

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

Ini kodenya:

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

Dan inilah metode pengujian NUnit untuk menguji contoh:

    [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
sumber
Terima kasih atas kesalahannya dalam contoh saya. Saya telah mengedit jawaban saya untuk memperbaikinya.
dbenham
1

Mungkin seseorang dapat menemukan ini berguna. Kode JavaScript ini didasarkan pada jawaban oleh dbenham di atas.

Saya tidak sourceMaskbanyak menguji , tetapi targetMaskcocok dengan semua contoh yang diberikan oleh 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
sumber
0

Saya telah berhasil menulis kode ini dalam BASIC untuk menutupi nama file wildcard:

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
sumber
4
Bisakah Anda mengklarifikasi bagaimana ini menjawab apa yang ditanyakan dalam pertanyaan?
fixer1234
Ini mereplikasi fungsi yang digunakan REN untuk pencocokan wildcard seperti memproses REN * .TMP * .DOC tergantung pada bagaimana fungsi dipanggil sebelum mengganti nama nama file.
eoredson