คำสั่ง Windows RENAME ตีความสัญลักษณ์แทนได้อย่างไร


77

คำสั่ง Windows RENAME (REN) ตีความสัญลักษณ์แทนได้อย่างไร

สิ่งอำนวยความสะดวกช่วยเหลือในตัวไม่มีความช่วยเหลือ - ไม่ได้ใช้สัญลักษณ์แทนเลย

Microsoft TechNet XP ช่วยเหลือออนไลน์ไม่ได้ดีมาก นี่คือทั้งหมดที่จะต้องพูดเกี่ยวกับสัญลักษณ์แทน:

"คุณสามารถใช้อักขระตัวแทน ( *และ?) ในพารามิเตอร์ชื่อไฟล์ได้หากคุณใช้อักขระตัวแทนในชื่อไฟล์ 2 อักขระที่แสดงด้วยอักขระตัวแทนจะเหมือนกับอักขระที่สอดคล้องกันในชื่อไฟล์ 1"

ความช่วยเหลือไม่มาก - มีหลายวิธีที่คำสั่งนั้นสามารถตีความได้

ฉันจัดการเพื่อใช้สัญลักษณ์แทนในพารามิเตอร์filename2ได้สำเร็จในบางโอกาส แต่ก็มีการลองผิดลองถูกมาตลอด ฉันไม่สามารถคาดการณ์ได้ว่าอะไรจะได้ผลและอะไรที่ไม่ดี บ่อยครั้งที่ฉันต้องหันไปเขียนสคริปต์ชุดเล็ก ๆ ด้วยวงวนสำหรับ FOR ที่แยกแต่ละชื่อเพื่อที่ฉันจะสามารถสร้างชื่อใหม่แต่ละชื่อได้ตามต้องการ ไม่สะดวกมาก

หากฉันรู้ว่ากฎสำหรับวิธีการประมวลผลสัญลักษณ์ตัวแทนนั้นฉันคิดว่าฉันสามารถใช้คำสั่ง RENAME ได้อย่างมีประสิทธิภาพมากขึ้นโดยไม่ต้องเปลี่ยนแบตช์บ่อยครั้ง แน่นอนว่าการรู้กฎจะเป็นประโยชน์ต่อการพัฒนาแบทช์

(ใช่ - นี่เป็นกรณีที่ฉันโพสต์คำถามและคำตอบที่จับคู่ไว้ฉันเบื่อที่จะไม่รู้กฎและตัดสินใจทดลองด้วยตัวเองฉันคิดว่าคนอื่น ๆ อาจสนใจในสิ่งที่ฉันค้นพบ)


มีตัวอย่างมากมายของวิธีเปลี่ยนชื่อด้วย wildcard ที่นี่: lagmonster.org/docs/DOS7/z-ren1.html
Matthew Lock

5
@MatthewLock - ลิงก์ที่น่าสนใจ แต่กฎและตัวอย่างเหล่านั้นสำหรับ MSDOS 7 ไม่ใช่ Windows มีความแตกต่างอย่างมีนัยสำคัญ ตัวอย่างเช่น MSDOS ไม่อนุญาตให้ผนวกตัวอักษรเพิ่มเติมหลังจาก*Windows ทำ นั่นมีผลกระทบอย่างมาก ฉันหวังว่าฉันจะรู้เกี่ยวกับเว็บไซต์นั้น; มันอาจทำให้การสอบสวนของฉันง่ายขึ้น กฎ MSDOS7 นั้นแตกต่างอย่างมากจากนั้นกฎ DOS เก่าก่อนชื่อไฟล์แบบยาวและเป็นขั้นตอนในทิศทางที่ Windows จัดการกับมัน ฉันพบกฎไฟล์ DOS ที่มีชื่อยาวและพวกเขาก็ไร้ค่าสำหรับการตรวจสอบของฉัน
dbenham

ฉันไม่ทราบว่า;)
แมทธิวล็อค

คำตอบ:


116

กฎเหล่านี้ถูกค้นพบหลังจากการทดสอบอย่างละเอียดบนเครื่อง Vista ไม่มีการทดสอบกับ unicode ในชื่อไฟล์

RENAME ต้องการ 2 พารามิเตอร์ - sourceMask ตามด้วย targetMask ทั้ง sourceMask และ targetMask สามารถมี*และ / หรือ?ไวด์การ์ด พฤติกรรมของสัญลักษณ์แทนการเปลี่ยนแปลงเล็กน้อยระหว่างมาสก์แหล่งที่มาและเป้าหมาย

หมายเหตุ -สามารถใช้ REN เพื่อเปลี่ยนชื่อโฟลเดอร์ได้ แต่ไม่อนุญาตให้ใช้อักขระตัวแทนใน sourceMask หรือ targetMask เมื่อเปลี่ยนชื่อโฟลเดอร์ หาก sourceMask ตรงกับไฟล์อย่างน้อยหนึ่งไฟล์ไฟล์นั้นจะถูกเปลี่ยนชื่อและโฟลเดอร์จะถูกละเว้น หาก sourceMask จับคู่โฟลเดอร์เท่านั้นและไม่ใช่ไฟล์ดังนั้นจะเกิดข้อผิดพลาดทางไวยากรณ์ขึ้นหากสัญลักษณ์ตัวแทนปรากฏในแหล่งที่มาหรือเป้าหมาย หาก sourceMask ไม่ตรงกับสิ่งใดดังนั้นผลลัพธ์ข้อผิดพลาด "file not found"

นอกจากนี้เมื่อเปลี่ยนชื่อไฟล์ไวด์การ์ดจะได้รับอนุญาตเฉพาะในส่วนชื่อไฟล์ของ sourceMask ไม่อนุญาตให้ใช้สัญลักษณ์แทนในเส้นทางที่นำไปสู่ชื่อไฟล์

sourceMask

sourceMask ทำงานเป็นตัวกรองเพื่อกำหนดว่าจะเปลี่ยนชื่อไฟล์ใด อักขระตัวแทนทำงานที่นี่เหมือนกับคำสั่งอื่น ๆ ที่กรองชื่อไฟล์

  • ?- จับคู่อักขระใด ๆ 0 หรือ 1 ยกเว้น . อักขระตัวแทนนี้เป็นโลภ - มันใช้อักขระตัวถัดไปเสมอหากไม่ใช่ตัวอักษร A . อย่างไรก็ตามมันจะจับคู่กับสิ่งใด ๆ โดยไม่ล้มเหลวหากอยู่ที่ชื่อท้ายหรืออักขระถัดไปคือ.

  • *- จับคู่อักขระ 0 ตัวหรือมากกว่ารวมถึง . (ยกเว้นหนึ่งข้อยกเว้นด้านล่าง) อักขระตัวแทนนี้ไม่โลภ มันจะจับคู่น้อยหรือมากเท่าที่จำเป็นเพื่อเปิดใช้งานอักขระที่ตามมาให้ตรงกัน

อักขระที่ไม่ใช่อักขระตัวแทนทั้งหมดต้องจับคู่กันยกเว้นสองสามกรณีพิเศษ

  • .- จับคู่ตัวเองหรือตรงกับส่วนท้ายของชื่อ (ไม่มีอะไร) หากไม่มีตัวอักษรเหลืออยู่อีก (หมายเหตุ - ชื่อ Windows ที่ถูกต้องไม่สามารถลงท้ายด้วย.)

  • {space}- จับคู่ตัวเองหรือตรงกับส่วนท้ายของชื่อ (ไม่มีอะไร) หากไม่มีตัวอักษรเหลืออยู่อีก (หมายเหตุ - ชื่อ Windows ที่ถูกต้องไม่สามารถลงท้ายด้วย{space})

  • *.ในตอนท้าย - จับคู่อักขระ 0 ตัวหรือมากกว่านั้นยกเว้น . การยกเลิก.อาจเป็นการรวมกันของ.และ{space}ตราบใดที่อักขระตัวสุดท้ายในรูปแบบคือ. นี่คือข้อยกเว้นเพียงข้อเดียวเท่านั้นที่*ไม่ตรงกับชุดอักขระใด ๆ

กฎข้างต้นนั้นไม่ซับซ้อน แต่มีกฎสำคัญอีกข้อหนึ่งที่ทำให้สถานการณ์สับสน: ซอร์สโค้ดถูกเปรียบเทียบกับชื่อแบบยาวและชื่อแบบย่อ 8.3 (ถ้ามี) กฎข้อสุดท้ายนี้สามารถตีความผลลัพธ์ได้ยากมากเนื่องจากไม่ชัดเจนเสมอเมื่อมาสก์จับคู่ผ่านชื่อย่อ

เป็นไปได้ที่จะใช้ RegEdit เพื่อปิดใช้งานการสร้างชื่อสั้น ๆ 8.3 บนไดรฟ์ข้อมูล NTFS ซึ่งการแปลความหมายของผลลัพธ์ไฟล์มาส์กตรงไปตรงมามากขึ้น ชื่อย่อใด ๆ ที่สร้างขึ้นก่อนปิดใช้งานชื่อย่อจะยังคงอยู่

targetMask

หมายเหตุ - ฉันยังไม่ได้ทำการทดสอบอย่างเข้มงวด แต่ปรากฏว่ากฎเดียวกันนี้ยังใช้งานได้กับชื่อเป้าหมายของคำสั่ง COPY

targetMask ระบุชื่อใหม่ มันมักจะใช้กับชื่อเต็มยาว targetMask ไม่เคยใช้กับชื่อแบบย่อ 8.3 แม้ว่า sourceMask จะตรงกับชื่อแบบย่อ 8.3

การมีหรือไม่มี wildcards ใน sourceMask ไม่มีผลกระทบต่อวิธีการประมวลผล wildcard ใน 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 ตัวอักษรเพื่อไม่ให้ถูกตัดทอน (เห็นได้ชัดว่าสามารถเพิ่มเพิ่มเติม?ที่ปลายทั้งสองของ targetMask เพื่อรักษาชื่อและส่วนขยายยาวได้ถึง 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

หากเปิดใช้งานชื่อแบบย่อดังนั้น sourceMask ที่มีอย่างน้อย 8 ?สำหรับชื่อและอย่างน้อย 3 ?สำหรับส่วนขยายจะจับคู่ไฟล์ทั้งหมดเพราะจะตรงกับชื่อแบบย่อ 8.3 เสมอ

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


การเล่นโวหาร / บั๊กที่มีประโยชน์? สำหรับการลบคำนำหน้าชื่อ

โพสต์ SuperUser นี้อธิบายถึงวิธีการใช้ชุดสแลช ( /) เพื่อลบอักขระนำจากชื่อไฟล์ ต้องใช้เครื่องหมายทับหนึ่งตัวเพื่อลบอักขระแต่ละตัว ฉันยืนยันพฤติกรรมบนเครื่อง 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 ที่เป็นไปได้ - คำสั่งเดียวอาจเปลี่ยนชื่อไฟล์เดียวกันสองครั้ง!

เริ่มต้นในโฟลเดอร์ทดสอบที่ว่างเปล่า:

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เปลี่ยนชื่อแล้วยังคงมองหาไฟล์อื่น ๆ 223456~1.Xอีกมากมายในการประมวลผลและพบว่าไฟล์ชื่อใหม่ผ่านทางชื่อสั้นใหม่ 223456789.123.xxแฟ้มดังกล่าวจะแล้วเปลี่ยนชื่ออีกครั้งให้ผลสุดท้ายของการ

หากฉันปิดใช้งานการสร้างชื่อ 8.3 ชื่อ RENAME จะให้ผลลัพธ์ที่คาดหวัง

ฉันยังไม่ได้ทำงานอย่างเต็มที่จากเงื่อนไขทั้งหมดที่จะต้องมีอยู่เพื่อชักนำให้เกิดพฤติกรรมแปลก ๆ นี้ ฉันกังวลว่ามันอาจเป็นไปได้ที่จะสร้าง RENAME แบบเรียกซ้ำไม่สิ้นสุด แต่ฉันไม่สามารถชักนำให้เกิดซ้ำได้

ฉันเชื่อว่าทั้งหมดต่อไปนี้จะต้องเป็นจริงเพื่อก่อให้เกิดข้อผิดพลาด ทุกกรณีที่เกิดข้อบกพร่องฉันเห็นมีเงื่อนไขต่อไปนี้ แต่ไม่ใช่ทุกกรณีที่ตรงตามเงื่อนไขต่อไปนี้

  • ต้องเปิดใช้งานชื่อย่อ 8.3
  • sourceMask ต้องตรงกับชื่อแบบยาว
  • การเปลี่ยนชื่อเริ่มต้นจะต้องสร้างชื่อย่อที่ตรงกับ sourceMask
  • ชื่อย่อที่เปลี่ยนชื่อเริ่มต้นจะต้องจัดเรียงในภายหลังกว่าชื่อย่อดั้งเดิม (หากมีอยู่?)

6
ช่างเป็นคำตอบที่ละเอียดมาก .. +1
meder omuraliev

วิจิตรบรรจง!
Andriy M

13
จากนี้ไมโครซอฟท์ก็ควรจะเพิ่ม "สำหรับการใช้งานดูsuperuser.com/a/475875 " REN /?ใน
efotinis

4
@CAD - คำตอบนี้เป็นเนื้อหาต้นฉบับ 100% ที่ Simon รวมอยู่ในเว็บไซต์ของเขาตามคำขอของฉัน ดูที่ด้านล่างของหน้า SS64 และคุณจะเห็นว่า Simon ให้เครดิตฉันสำหรับงานนี้
dbenham

2
@ JacksOnF1re - เพิ่มข้อมูล / เทคนิคใหม่ในคำตอบของฉัน คุณสามารถลบCopy of คำนำหน้าของคุณโดยใช้เทคนิคเครื่องหมายทับหน้าที่ไม่ชัดเจน:ren "Copy of *.txt" "////////*"
dbenham

4

คล้ายกับ exebook นี่คือการติดตั้ง C # เพื่อรับชื่อไฟล์เป้าหมายจากซอร์สไฟล์

ฉันพบข้อผิดพลาดเล็กน้อย 1 ข้อในตัวอย่างของ dbenham:

 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

1

บางทีบางคนอาจพบว่ามีประโยชน์ รหัส JavaScript นี้ยึดตามคำตอบของ dbenham ด้านบน

ฉันไม่ได้ทดสอบsourceMaskมาก แต่targetMaskจับคู่ตัวอย่างทั้งหมดที่ได้รับจาก 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

ฉันได้เขียนโค้ดนี้ในภาษาเบสิกเพื่อปกปิดชื่อไฟล์ไวด์การ์ด:

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 ที่ใช้สำหรับการจับคู่ไวด์การ์ดเช่นการประมวลผล REN * .TMP * .DOC ขึ้นอยู่กับวิธีการเรียกใช้ฟังก์ชั่นก่อนที่จะเปลี่ยนชื่อไฟล์
eoredson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.