0

I have some files with long filenames

long filename with spaces 1.jpg
long filename with spaces 1.bmp
long filename with spaces 2.jpg
long filename with spaces 2.bmp
long filename with spaces 3.jpg
long filename with spaces 3.bmp
...
long filename with spaces 10.jpg
long filename with spaces 10.bmp
long filename with spaces 11.jpg
long filename with spaces 11.bmp
...
long filename with spaces 124.jpg
long filename with spaces 124.bmp
long filename with spaces 125.jpg
long filename with spaces 125.bmp

that I would like to pad with zeroes so they look like

long filename with spaces 0001.jpg
long filename with spaces 0001.bmp
long filename with spaces 0002.jpg
long filename with spaces 0002.bmp
long filename with spaces 0003.jpg
long filename with spaces 0003.bmp
...
long filename with spaces 0010.jpg
long filename with spaces 0010.bmp
long filename with spaces 0011.jpg
long filename with spaces 0011.bmp
...
long filename with spaces 0124.jpg
long filename with spaces 0124.bmp
long filename with spaces 0125.jpg
long filename with spaces 0125.bmp

and be able to use wild cards for the file names.

I've been using this script but it only adds the zeros I put in and doesn't accept wildcards:

Set objFso = CreateObject("Scripting.FileSystemObject")
Set Folder = objFSO.GetFolder("C:\MyPictures\")

For Each File In Folder.Files
    sNewFile = File.Name
    sNewFile = Replace(sNewFile, "long filename with spaces ", "long filename with spaces 000")
    If (sNewFile <> File.Name) Then 
        File.Move(File.ParentFolder + "\" + sNewFile)
    End If
Next

So with that script, long filename with spaces 1.jpg becomes long filename with spaces 0001.jpg, which is what I want, but long filename with spaces 125.jpg becomes long filename with spaces 000125.jpg, which isn't what I'm looking for.

I'm using Windows 10 and I would also accept batch files.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
joetex72
  • 5
  • 4

2 Answers2

1

Use a regular expression replacement function to invoke a custom padding function:

Function LPad(s, l, c)
  Dim n : n = 0
  If l > Len(s) Then n = l - Len(s)
  LPad = String(n, c) & s
End Function

Function PadIndex(m, m1, m2, pos, src)
  PadIndex = m1 & LPad(m2, 4, "0")
End Function

Set re = New RegExp
re.Pattern = "^(.*?)(\d+)$"

Set fso = CreateObject("Scripting.FileSystemObject")

For Each f In fso.GetFolder("C:\MyPictures").Files
  newName = re.Replace(fso.GetBaseName(f), GetRef("PadIndex")) & "." & _
            fso.GetExtensionName(f)
  If newName <> f.Name Then f.Name = newName
Next

The regular expression ^(.*?)(\d+)$ matches any string ending with one or more digits. The replacement function pads the value of the second capturing group ((\d+)) and appends it to the value of the first capturing group ((.*?), non-greedy match).

Community
  • 1
  • 1
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • This answer is short and sweet. Can I accept both? They both work. – joetex72 Feb 14 '17 at 00:23
  • Can this be modified to grab specific extensions instead of all @ansgar-wiechers ? Thant's one thing I liked about the other script. – joetex72 Feb 17 '17 at 00:33
  • Sure it can. Just add an `If` condition around the body of the loop. – Ansgar Wiechers Feb 17 '17 at 09:59
  • @joetex72 It would be sufficient if you decided on one answer and stuck with it (yes, I saw that you had accepted MC ND's answer first and then changed your mind twice). If you prefer another person's answer that's fine with me, but I certainly do not appreciate having my answer accepted and then un-accepted again. Think first. Then act. – Ansgar Wiechers Feb 19 '17 at 22:40
0
Option Explicit

' Required object to iterate filesystem
Dim fso
    Set fso = WScript.CreateObject("Scripting.FileSystemObject")

' Regular expression to match and tokenize the names of files to process
Dim re
    Set re = New RegExp 
    re.IgnoreCase = True 
    re.Pattern = "(long filename with spaces )0*([0-9]+)\.(jpg|bmp)"
    ' submatches:  ^0                            ^1        ^2

Dim file, match, newName
    ' For each file in the indicated folder
    For Each file In fso.GetFolder("w:\42198563").Files

        ' If the file name matches the regular expression
        For Each match In re.Execute( file.Name )
            ' Determine the new name for the file by joining the submatches
            ' (capture groups) retrieved by the regular expression
            newName = fso.BuildPath( _ 
                file.ParentFolder.Path _ 
                , match.SubMatches(0) _ 
                  & LeftZeroPad( 4, match.SubMatches(1) ) _ 
                  & "." _ 
                  & match.SubMatches(2) _ 
            )

            ' If the file name changes and there is not name collision, rename
            If file.Path <> newName Then 
                If Not fso.FileExists( newName ) Then
                    WScript.Echo file.Path & " => " & newName
                    file.Move newName
                Else 
                    WScript.Echo "SKIPPED " & file.Path
                End If 
            End If 
        Next ' match 
    Next ' file

' Helper function to pad the ending digits in the file name    
Function LeftZeroPad( length, data )
    If Len(data) < length Then 
        LeftZeroPad = Right( String(length, "0") & data, length )
    Else
        LeftZeroPad = data 
    End If 
End Function
MC ND
  • 69,615
  • 8
  • 84
  • 126
  • I would love if this script didn't prompt every time so I could call it from a batch file @mc-nd . – joetex72 Feb 17 '17 at 00:49
  • @joetex72, If you don't need the output, remove the `WScript.Echo` lines, or if you are going to use it from a batch file run it as `cscript.exe //nologo myScript.vbs`. If don't want to remove the `Echo`s, but don't want the output use `cscript.exe //nologo //b myScript.vbs` (console mode) or `wscript //b myScript.vbs` (windows mode). The `//b` will run the script in *"batch"* mode (in the sense of batch processing, not batch file), supressing all output. – MC ND Feb 17 '17 at 06:30