Làm thế nào để lệnh Windows RENAME diễn giải các ký tự đại diện?


77

Làm thế nào để lệnh Windows RENAME (REN) diễn giải các ký tự đại diện?

Công cụ HELP được xây dựng không giúp ích được gì - nó hoàn toàn không đề cập đến các ký tự đại diện.

Trợ giúp trực tuyến của Microsoft Technet XP không tốt hơn nhiều. Đây là tất cả những gì nó phải nói về ký tự đại diện:

"Bạn có thể sử dụng ký tự đại diện ( *?) trong một trong hai tham số tên tệp. Nếu bạn sử dụng ký tự đại diện trong filename2, các ký tự được đại diện bởi các ký tự đại diện sẽ giống hệt với các ký tự tương ứng trong tên tệp1."

Không có nhiều trợ giúp - có nhiều cách mà tuyên bố có thể được diễn giải.

Tôi đã quản lý để sử dụng thành công các ký tự đại diện trong tham số filename2 trong một số trường hợp, nhưng nó luôn bị thử và sai. Tôi đã không thể lường trước những gì hoạt động và những gì không. Thường thì tôi đã phải dùng đến việc viết một tập lệnh bó nhỏ với vòng lặp FOR để phân tích từng tên để tôi có thể tạo mỗi tên mới khi cần. Không thuận tiện lắm.

Nếu tôi biết các quy tắc về cách xử lý ký tự đại diện thì tôi cho rằng tôi có thể sử dụng lệnh RENAME hiệu quả hơn mà không phải dùng đến lô hàng thường xuyên. Tất nhiên biết các quy tắc cũng sẽ có lợi cho sự phát triển hàng loạt.

(Có - đây là trường hợp tôi đăng một câu hỏi và câu trả lời được ghép nối. Tôi cảm thấy mệt mỏi vì không biết các quy tắc và quyết định tự mình thử nghiệm. Tôi cho rằng nhiều người khác có thể quan tâm đến những gì tôi đã khám phá)


Có rất nhiều ví dụ hay về cách đổi tên bằng ký tự đại diện ở đây: lagmonster.org/docs/DOS7/z-ren1.html
Matthew Lock

5
@MatthewLock - Liên kết thú vị, nhưng các quy tắc và ví dụ này dành cho MSDOS 7, không phải cho Windows. Có sự khác biệt đáng kể. Ví dụ: MSDOS không cho phép nối thêm các ký tự bổ sung sau *, Windows thì có. Điều đó có hậu quả rất lớn. Tôi ước tôi đã biết về trang web đó mặc dù; nó có thể đã làm cho cuộc điều tra của tôi dễ dàng hơn. Các quy tắc MSDOS7 khác biệt đáng kể so với các quy tắc DOS cũ trước các tên tệp dài và chúng là một bước theo hướng Windows xử lý nó. Tôi đã tìm thấy các quy tắc tên tệp DOS dài và chúng vô dụng đối với cuộc điều tra của tôi.
dbenham

Tôi không biết điều đó;)
Matthew Lock

Câu trả lời:


116

Các quy tắc này đã được phát hiện sau khi thử nghiệm rộng rãi trên máy Vista. Không có bài kiểm tra nào được thực hiện với unicode trong tên tệp.

RENAME yêu cầu 2 tham số - một sourceMask, theo sau là targetMask. Cả sourceMask và targetMask đều có thể chứa *và / hoặc ?ký tự đại diện. Hành vi của các ký tự đại diện thay đổi một chút giữa các mặt nạ nguồn và đích.

Lưu ý - REN có thể được sử dụng để đổi tên thư mục, nhưng ký tự đại diện không được phép trong cả sourceMask hoặc targetMask khi đổi tên thư mục. Nếu sourceMask khớp với ít nhất một tệp, thì (các) tệp sẽ được đổi tên và các thư mục sẽ bị bỏ qua. Nếu sourceMask chỉ khớp với các thư mục và không phải tệp, thì sẽ xảy ra lỗi cú pháp nếu ký tự đại diện xuất hiện trong nguồn hoặc đích. Nếu sourceMask không khớp với bất cứ điều gì, thì kết quả lỗi "không tìm thấy tệp".

Ngoài ra, khi đổi tên tệp, ký tự đại diện chỉ được phép trong phần tên tệp của sourceMask. Ký tự đại diện không được phép trong đường dẫn đến tên tệp.

nguồnMask

SourceMask hoạt động như một bộ lọc để xác định tệp nào được đổi tên. Các ký tự đại diện hoạt động ở đây giống như với bất kỳ lệnh nào khác lọc tên tệp.

  • ?- Khớp với bất kỳ 0 hoặc 1 ký tự nào ngoại trừ Ký . tự đại diện này là tham lam - nó luôn tiêu thụ ký tự tiếp theo nếu không phải là . Tuy nhiên, nó sẽ không khớp với bất cứ thứ gì nếu không có lỗi nếu ở cuối tên hoặc nếu ký tự tiếp theo là.

  • *- Khớp bất kỳ 0 hoặc nhiều ký tự bao gồm . (với một ngoại lệ bên dưới). Ký tự đại diện này không tham lam. Nó sẽ khớp ít nhất hoặc nhiều như cần thiết để cho phép các ký tự tiếp theo khớp.

Tất cả các ký tự không phải ký tự đại diện phải khớp với chính chúng, với một vài trường hợp ngoại lệ đặc biệt.

  • .- Khớp chính nó hoặc nó có thể khớp với cuối tên (không có gì) nếu không còn ký tự nào nữa. (Lưu ý - tên Windows hợp lệ không thể kết thúc bằng .)

  • {space}- Khớp chính nó hoặc nó có thể khớp với cuối tên (không có gì) nếu không còn ký tự nào nữa. (Lưu ý - tên Windows hợp lệ không thể kết thúc bằng {space})

  • *.ở cuối - Khớp 0 hoặc nhiều ký tự ngoại trừ . Kết thúc .thực sự có thể là bất kỳ sự kết hợp nào .{space}miễn là ký tự cuối cùng trong mặt nạ là . Đây là ngoại lệ duy nhất *không chỉ khớp với bất kỳ bộ ký tự nào.

Các quy tắc trên không phức tạp. Nhưng có một quy tắc rất quan trọng nữa khiến tình huống trở nên khó hiểu: sourceMask được so sánh với cả tên dài và tên ngắn 8.3 (nếu nó tồn tại). Quy tắc cuối cùng này có thể làm cho việc giải thích các kết quả rất khó khăn, bởi vì nó không phải lúc nào cũng rõ ràng khi mặt nạ được khớp thông qua tên ngắn.

Có thể sử dụng RegEdit để vô hiệu hóa việc tạo các tên ngắn 8.3 trên các khối NTFS, tại đó việc giải thích kết quả mặt nạ tệp sẽ dễ dàng hơn nhiều. Bất kỳ tên ngắn nào được tạo trước khi tắt tên ngắn sẽ vẫn còn.

mục tiêu

Lưu ý - Tôi chưa thực hiện bất kỳ thử nghiệm nghiêm ngặt nào, nhưng có vẻ như các quy tắc này cũng hoạt động với tên mục tiêu của ủy ban COPY

TargetMask chỉ định tên mới. Nó luôn được áp dụng cho tên dài đầy đủ; TargetMask không bao giờ được áp dụng cho tên 8.3 ngắn, ngay cả khi sourceMask khớp với tên 8.3 ngắn.

Sự hiện diện hay vắng mặt của các ký tự đại diện trong sourceMask không ảnh hưởng đến cách xử lý các ký tự đại diện trong targetMask.

Trong các cuộc thảo luận sau - cđại diện cho bất kỳ nhân vật đó không phải là *, ?hoặc.

TargetMask được xử lý theo tên nguồn từ trái sang phải mà không theo dõi ngược.

  • c- Tiến lên vị trí trong tên nguồn miễn là ký tự tiếp theo không có .và thêm cvào tên đích. (Thay thế ký tự trong nguồn c, nhưng không bao giờ thay thế .)

  • ?- Ghép ký tự tiếp theo từ tên dài nguồn và nối nó với tên đích miễn là ký tự tiếp theo không có . Nếu ký tự tiếp theo là .hoặc nếu ở cuối tên nguồn thì không có ký tự nào được thêm vào kết quả và hiện tại vị trí trong tên nguồn không thay đổi.

  • *ở cuối targetMask - Nối tất cả các ký tự còn lại từ nguồn vào đích. Nếu đã ở cuối nguồn, thì không làm gì cả.

  • *c- Khớp tất cả các ký tự nguồn từ vị trí hiện tại thông qua lần xuất hiện cuối cùng của c(trường hợp tham lam nhạy cảm trường hợp) và nối thêm bộ ký tự trùng khớp với tên đích. Nếu ckhông được tìm thấy, thì tất cả các ký tự còn lại từ nguồn sẽ được nối thêm, tiếp theo c Đây là tình huống duy nhất tôi biết về việc khớp mẫu tệp Windows có phân biệt chữ hoa chữ thường.

  • *.- Ghép tất cả các ký tự nguồn từ vị trí hiện tại thông qua lần xuất hiện cuối cùng của .(khớp tham lam) và nối thêm bộ ký tự trùng khớp với tên đích. Nếu .không được tìm thấy, thì tất cả các ký tự còn lại từ nguồn sẽ được nối thêm, theo sau là.

  • *?- Nối tất cả các ký tự còn lại từ nguồn vào mục tiêu. Nếu đã hết nguồn thì không làm gì cả.

  • .không *ở phía trước - Nâng cao vị trí trong nguồn thông qua lần xuất hiện đầu tiên. mà không sao chép bất kỳ ký tự nào và gắn .vào tên đích. Nếu .không được tìm thấy trong nguồn, sau đó tiến đến cuối nguồn và nối .vào tên đích.

Sau khi mục tiêu đã hết, mọi dấu vết .{space}được cắt bớt phần cuối của tên mục tiêu kết quả vì tên tệp Windows không thể kết thúc bằng .hoặc{space}

Một số ví dụ thực tế

Thay thế một ký tự ở vị trí thứ 1 và thứ 3 trước bất kỳ phần mở rộng nào (thêm ký tự thứ 2 hoặc thứ 3 nếu nó chưa tồn tại)

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

Thay đổi phần mở rộng (cuối cùng) của mỗi tệp

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

Nối một phần mở rộng cho mỗi tệp

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

Loại bỏ bất kỳ phần mở rộng bổ sung sau phần mở rộng ban đầu. Lưu ý rằng ?phải sử dụng đầy đủ để giữ nguyên tên đầy đủ và phần mở rộng ban đầu.

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)

Tương tự như trên, nhưng lọc ra các tệp có tên ban đầu và / hoặc phần mở rộng dài hơn 5 ký tự để chúng không bị cắt ngắn. (Rõ ràng có thể thêm một bổ sung ?vào một trong hai đầu của TargetMask để giữ tên và tiện ích mở rộng dài tới 6 ký tự)

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)

Thay đổi ký tự sau _tên cuối cùng và cố gắng duy trì phần mở rộng. (Không hoạt động đúng nếu _xuất hiện trong phần mở rộng)

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)

Bất kỳ tên nào cũng có thể được chia thành các thành phần được phân cách bằng . Ký tự chỉ có thể được thêm vào hoặc xóa từ cuối mỗi thành phần. Các ký tự không thể bị xóa khỏi hoặc thêm vào đầu hoặc giữa của một thành phần trong khi vẫn giữ phần còn lại bằng các ký tự đại diện. Thay thế được cho phép bất cứ nơi nào.

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

Nếu tên ngắn được bật, thì sourceMask có ít nhất 8 ?cho tên và ít nhất 3 ?cho tiện ích mở rộng sẽ khớp với tất cả các tệp vì nó sẽ luôn khớp với tên ngắn 8.3.

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


Quirk / bug hữu ích? để xóa tiền tố tên

Bài đăng SuperUser này mô tả cách sử dụng một bộ dấu gạch chéo ( /) để xóa các ký tự hàng đầu khỏi tên tệp. Một dấu gạch chéo được yêu cầu cho mỗi ký tự sẽ bị xóa. Tôi đã xác nhận hành vi trên máy Windows 10.

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

Kỹ thuật này chỉ hoạt động nếu cả mặt nạ nguồn và mặt nạ đích được đặt trong dấu ngoặc kép. Tất cả các hình thức sau đây mà không có trích dẫn cần thiết đều thất bại với lỗi này: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

Không /thể được sử dụng để xóa bất kỳ ký tự nào ở giữa hoặc cuối tên tệp. Nó chỉ có thể loại bỏ các ký tự hàng đầu (tiền tố).

Về mặt kỹ thuật /không hoạt động như một ký tự đại diện. Thay vào đó, nó đang thực hiện thay thế ký tự đơn giản, nhưng sau khi thay thế, lệnh REN nhận ra rằng /nó không hợp lệ trong tên tệp và loại bỏ các /dấu gạch chéo hàng đầu khỏi tên. REN đưa ra lỗi cú pháp nếu phát hiện /ở giữa tên đích.


Lỗi RENAME có thể xảy ra - một lệnh có thể đổi tên cùng một tệp hai lần!

Bắt đầu trong một thư mục thử nghiệm trống:

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

Tôi tin rằng sourceMask *1*trước tiên khớp với tên tệp dài và tệp được đổi tên thành kết quả mong đợi 223456789.123.x. RENAME sau đó tiếp tục tìm kiếm thêm các tệp để xử lý và tìm tệp mới được đặt tên thông qua tên viết tắt mới của 223456~1.X. Các tập tin sau đó được đổi tên một lần nữa cho kết quả cuối cùng của 223456789.123.xx.

Nếu tôi vô hiệu hóa việc tạo tên 8.3 thì RENAME cho kết quả như mong đợi.

Tôi đã không hoàn toàn tìm ra tất cả các điều kiện kích hoạt phải tồn tại để gây ra hành vi kỳ quặc này. Tôi đã lo ngại rằng có thể tạo ra một RENAME đệ quy không bao giờ kết thúc, nhưng tôi không bao giờ có thể tạo ra một.

Tôi tin rằng tất cả những điều sau đây phải là đúng để gây ra lỗi. Mọi trường hợp lỗi tôi thấy đều có các điều kiện sau, nhưng không phải tất cả các trường hợp đáp ứng các điều kiện sau đều bị lỗi.

  • Tên 8.3 ngắn phải được bật
  • SourceMask phải khớp với tên dài ban đầu.
  • Việc đổi tên ban đầu phải tạo một tên ngắn cũng khớp với sourceMask
  • Tên ngắn được đổi tên ban đầu phải sắp xếp muộn hơn tên ngắn ban đầu (nếu nó tồn tại?)

6
Thật là một câu trả lời thấu đáo .. +1.
meder omuraliev

Rất công phu!
Andriy M

13
Dựa trên điều này, Microsoft chỉ cần thêm "Để sử dụng, xem superuser.com/a/475875 " trong REN /?.
efotinis

4
@CAD - Câu trả lời này là 100% nội dung gốc mà Simon đưa vào trang web của anh ấy theo yêu cầu của tôi. Nhìn vào cuối trang SS64 đó và bạn sẽ thấy Simon cho tôi tín dụng cho công việc.
dbenham

2
@ JacksOnF1re - Thông tin / kỹ thuật mới được thêm vào câu trả lời của tôi. Bạn thực sự có thể xóa Copy of tiền tố của mình bằng cách sử dụng kỹ thuật gạch chéo khó hiểu:ren "Copy of *.txt" "////////*"
dbenham

4

Tương tự như exebook, đây là một triển khai C # để lấy tên tệp đích từ một tệp nguồn.

Tôi đã tìm thấy 1 lỗi nhỏ trong các ví dụ của dbenham:

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

Đây là mã:

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

Và đây là một phương pháp thử nghiệm NUnit để kiểm tra các ví dụ:

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

    }

Cảm ơn vì đã đứng đầu về sai lầm trong ví dụ của tôi. Tôi đã chỉnh sửa câu trả lời của mình để sửa nó.
dbenham

1

Có lẽ ai đó có thể tìm thấy điều này hữu ích. Mã JavaScript này dựa trên câu trả lời của dbenham ở trên.

Tôi đã không kiểm tra sourceMaskrất nhiều, nhưng targetMaskkhông khớp với tất cả các ví dụ được đưa ra bởi 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)
}

0

Tôi đã quản lý để viết mã này trong BASIC để che giấu tên tệp ký tự đại diện:

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
Bạn có thể làm rõ làm thế nào điều này trả lời những gì đã được hỏi trong câu hỏi?
fixer1234

Nó sao chép chức năng mà REN sử dụng để khớp ký tự đại diện, chẳng hạn như xử lý REN * .TMP * .DOC tùy thuộc vào cách gọi hàm trước khi đổi tên tệp.
eoredson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.