I am trying to sort the files of a folder and perform action on them in a specific order (by name or by date modified for example)
I have tried the following method:
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(source_folder.Path)
Set colFiles = objFolder.Files
Set outputLines = CreateObject("System.Collections.ArrayList")
For Each objFile in colFiles
outputLines.Add(objFile.Name)
next
outputLines.Sort()
For Each outputLine in outputLines
set objFile = colFiles.item (outputLine&"")
*rest of the code*
the problem is if the file name of the file contains a char with an ASCII value which is less the ".", for example, "test!.exe" will be before "test.exe", which is not good for me.
I had an idea to make a class which contains the "extension" and "file name without extension" as field and sort by the "file name without extension" but I found out I cannot sort an Arraylist with custom objects as elements.
I have two questions on the same subject:
1) Is there a way to sort the files of a folder and perform action on them in a specific order which handles such cases (like test!.exe and test.exe)
2) Is there a default order to the files in a folder? I tested it a bit and it seems like the order is by the files name, but I saw in an official Microsoft documentation that there is no order to the files, so I am a bit confused.
Here's one idea. You could substitute a control character (or some other invalid filename char) between the file's base name and its extension. For example, put a vbTab in as a delimiter.
For Each objFile in colFiles
strBaseName = objFSO.GetBaseName(objFile.Path)
strExtension = objFSO.GetExtensionName(objFile.Path)
outputLines.Add strBaseName & vbTab & strExtension
Next
After sorting, you'll just have to remember to swap it back out before doing anything with the file.
For Each outputLine in outputLines
strFile = Replace(outputLine, vbTab, ".")
...
Next
Related
I have some XML files in a folder \\demo.US\Modified\. The files in the folder are:
USA.xml
Canada.xml
Mexico.xml
The code below is changing the encoding from UTF-8 to windows-1252 and is creating a modified file mod.xml.
This mod.xml file have data from all three XML files concatenated.
I need help so I can save files separately.
If value of objFile.Name is USA.xml then it should save modified file name as USA_mod.xml. the output for \\demo.US\Modified\ folder after execution is complete should have mod files in it as below.
USA.xml
Canada.xml
Mexico.xml
USA_mod.xml
Canada_mod.xml
Mexico_mod.xml
The code I used is as follows.
Set objFSO = CreateObject("Scripting.FileSystemObject")
objStartFolder = "\\demo.US\Modified\"
Set objFolder = objFSO.GetFolder(objStartFolder)
Set colFiles = objFolder.Files
For Each objFile In colFiles
WScript.Echo objFile.Name
Set objFile = objFSO.OpenTextFile(objStartFolder & objFile.Name, 1)
Set outFile = objFSO.OpenTextFile(objStartFolder & "mod.xml", 2, True)
Do Until objFile.AtEndOfStream
strContent = strContent & objFile.ReadLine
Loop
MsgBox strContent
strContent = Replace(strContent, "encoding=""UTF-8""", "encoding=""windows-1252""")
outFile.WriteLine strContent
outFile.Close
objFile.Close
Next
As others have already pointed out, you shouldn't do what you're attempting to do here, because it is very likely to create more problems down the road. Find the cause of the issue and fix that instead of trying to handle symptoms. You have been warned.
With that said, the reason why the content of all input files is written to the same output file is because you always specify the same output file. That file should contain only the content of the last input file, though, because you open the file for writing (thus erasing previous content) rather than for appending.
Replace these lines:
Set objFile = objFSO.OpenTextFile(objStartFolder & objFile.Name, 1)
Set outFile = objFSO.OpenTextFile(objStartFolder & "mod.xml", 2, True)
with this:
Set inFile = objFile.OpenAsTextStream
outFilename = objFSO.BuildPath(objStartFolder, objFSO.GetBaseName(objFile) & "_mod.xml")
Set outFile = objFSO.OpenTextFile(outFilename, 2, True)
and also replace the other occurrences of objFile after that with inFile (always avoid changing the value of a loop variable), and the code should do what you expect it to do. But again, be warned that the output may not be valid XML.
I managed to made it working, below is the code I used
Dim objFSO, filePath, objFile, colFiles, s , FName
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set filePath = objFSO.GetFolder("\\demo.US\Modified\")
Set colFiles = filePath.Files
For Each FName in colFiles
set objFile = objFSO.OpenTextFile(FName.Path,1)
set outFile = objFSO.OpenTextFile(LEFT(FName.Path,instr(FName.Path,".xml")-1) &"_mod.xml",2,True)
do until objFile.AtEndOfStream
strContent=objFile.ReadLine
Loop
strContent = Replace(strContent, "encoding=""UTF-8""", "encoding=""windows-1252""")
outFile.WriteLine strContent
outFile.Close
objFile.Close
Next
This script collects all files in a folder and renames the files by appending the number of lines to the file name. All files are .txt files. The method (since fso.MoveFile and fso.DeleteFile are too particular, generating permissions errors) is to
create the text files,
then create a collection of the files in the folder,
then copy each file into the same folder with a new name, and
finally to delete the original file that was copied.
The script works ok, unless there are no empty text files in the collection. What happens is, the collection gets rebuilt with the new files and the script once again renames the files. I know I can prevent this by checking each file for the existence of certain repeating character strings, but I'd like to know what's happening? Why does the script rebuild the file collection and run through them again renaming each one? This continues on until I kill the process.
Another interesting factoid is, if I happen to trap an empty text file, my message is displayed and the script stops there, but has still reprocessed the first file in the collection a second time. Note that the empty file just happens to be the last one in the collection, but the first filed is once again processed.
So, by design a created text file named 'ab0.txt' gets renamed to 'ab0-15.txt' since it has 15 lines of text in it. What happens is this newly renamed file looks like 'ab0-15-15-15-15-15-15-15-15-15-15.txt'
Questions: What's going on? And is there a better and more efficient way to accomplish this objective?
Here's the code pertinent to the issue:
Set fso = CreateObject("Scripting.FileSystemObject")
Set oFolder = fso.GetFolder(strSaveTo)
Set colFiles = oFolder.Files
' Call Sub to copy and rename
ChangeFileName colFiles
MsgBox("File renaming complete.")
' Exit code
Sub ChangeFileName(collectionSet)
Const ForReading = 1
Dim oFile
For Each oFile In collectionSet
Set LineCnt = fso.OpenTextFile(oFile, ForReading)
If oFile.Size = 0 then
'if this msg is not included, weird things happen
MsgBox("The file named " & oFile & _
" is empty.You may want to verify and manually delete it.")
'[I had some code in here to delete the empty file, but nothing worked]
Else
Do While LineCnt.AtEndOfStream <> True
LineCnt.SkipLine
Loop
lineVar = lineCnt.Line-1
strNewFile = strSaveTo & Left(oFile.name, Len(oFile.name)-4) & _
"-" & lineVar & ".txt"
fso.CopyFile oFile, strNewFile
LineCnt.Close
fso.DeleteFile oFile, True
End If
Next
End Sub
I've heard anecdotal evidence that the Files collection is "live", meaning that newly created files will be added to the collection and iterated over, but I can't find any documentation that says one way or the other. In any case, it's probably a good idea to copy the File objects in the collection to an array first before processing them:
Dim oFile
Dim fileArray()
Dim i
ReDim fileArray(collectionSet - 1)
i = 0
For Each oFile in collectionSet
Set fileArray(i) = oFile
i = i + 1
Next
For Each oFile In fileArray
' Count lines and rename
Next
It seems that collectionSet is the collection of files in the folder that you are trying to modify. The problem is that with each pass through the for-each loop you are adding files to this folder, some of which are fed back into the loop. What you need to do is the find a way to take a snapshot of the folder before you try to iterate over it. The way to do this would be to replace the folder collectionSet by a collection of strings which are the names of the files before you iterate over it, and modify your code to open the files by their name (instead of via a file object). That way the collection won't be expanding while you iterate over it.
You should create your vars in the scope they are used (e.g. your
file/folder objects are used in the sub.
Always explicit(ly) declare your vars.
You don't need to copy the file and rename it then do the delete.
Just rename it with the FileObject.Name property.
Here is an example:
Option Explicit 'always declare your vars!
Dim strFolder: strFolder = "c:\temp\Rename Test"
Dim strExtension: strExtension = "txt"
' Call Sub to rename the files in the folder
ChangeFileName strFolder, strExtension
Sub ChangeFileName(strFolder, strExtension)
Const ForReading = 1
Dim FSO: set FSO = CreateObject("Scripting.FileSystemObject")
Dim objFolder: set objFolder = FSO.GetFolder(strFolder)
Dim colFiles: set colFiles = objFolder.Files
Dim objFile
Dim intCount
Dim strFileName
Dim objTextStream
For Each objFile In colFiles
msgbox "File: " & objfile.path & vbcrlf & FSO.GetExtensionName(objFile.path)
if UCase(FSO.GetExtensionName(objFile.Path)) = UCase(strExtension) and _
objFile.Size > 0 then
'set LineCnt = FSO.OpenTextFile(objFile, ForReading)
set objTextStream = objFile.OpenAsTextStream(ForReading,-2)
intCount = 0
strFileName = objFile.Name
Do While objTextStream.AtEndOfStream <> True
intCount = intCount + 1
objTextStream.ReadLine
Loop
objTextStream.Close
objFile.Name = FSO.GetBaseName(objFile.Path) & "-" & _
intCount & "." & FSO.GetExtensionName(objFile.Path)
end if
Next
End Sub
I am redesigning our department website and our IT department does not support the intranet development. The server runs ASP Classic and is able to run VB scripts and Javascript to an extent (somethings work others don't).
So here is my problem:
I modified a simple code that I got from http://www.brainjar.com/asp/dirlist/
to list all the PDF Files in a directory including sub directories but I am not sure how to sort it.
As of now it sorts it in alphabetical order per each folder it reads. I would like it to sort every file of every sub directory by the item.DateLastModified property I do not know if this possible.
I am thinking I would need to store the items in an array and then sort the array and print the data but I have no idea how to do that it has been 10 years since I took a programming course.
Any help would be greatly appreciated!
Current code I am using :
====>
<% sub ListFolderContents(path)
dim fs, folder, file, item, url
set fs = CreateObject("Scripting.FileSystemObject")
set folder = fs.GetFolder(path)
for each item in folder.SubFolders
ListFolderContents(item.Path)
next
'Display a list of files
for each item in folder.Files
url = MapURL(item.path)
if item.type = "PDF File" then
Response.Write("<dt>" & item.Name & "" _
& vbCrLf)
end if
next
Response.Write("</dt>" & vbCrLf)
end sub
function MapURL(path)
dim rootPath, url
'Convert a physical file path to a URL for hypertext links.
rootPath = Server.MapPath("/")
url = Right(path, Len(path) - Len(rootPath))
MapURL = Replace(url, "\", "/")
end function %>
The original is at http://www.brainjar.com/asp/dirlist/
Well, it's your lucky day! I happen to have old code that I wrote for personal use ~10 years ago, so with little tweaking it can fit your case almost perfectly. The key is using a disconnected recordset to hold all the data, then sort by date last modified. The crawling itself is similar to what you already have, by recursion. Note that no need to create new folder instance in each iteration - it's waste of resources, since you already have the folder object in the loop.
Anyway, here it is:
Const adVarChar = 200
Const adInteger = 3
Const adDate = 7
Dim objFSO, oFolder, objRS
Sub ExtractAllFiles(oFolder)
Dim oSubFolder, oFile
'recurse all sub folders
For Each oSubFolder In oFolder.SubFolders
Call ExtractAllFiles(oSubFolder)
Next
'loop through all the files found, add to the recordset
For Each oFile in oFolder.Files
objRS.AddNew
objRS.Fields("Name").Value = oFile.Name
objRS.Fields("Url").Value = MapURL(oFile.Path)
objRS.Fields("Type") = oFile.Type
objRS.Fields("DateLastModified").Value = oFile.DateLastModified
Next
End Sub
Sub ListFolderContents(sPath, sTypeToShow)
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set oFolder = objFSO.GetFolder(sPath)
'create a disconnected recordset
Set objRS = Server.CreateObject("ADODB.Recordset")
'append proper fields
objRS.Fields.Append "Name", adVarChar, 255
objRS.Fields.Append "Url", adVarChar, 255
objRS.Fields.Append "Type", adVarChar, 255
objRS.Fields.Append "DateLastModified", adDate
objRS.Open
'extract all files in given path:
Call ExtractAllFiles(oFolder)
'sort and apply:
If Not(objRS.EOF) Then
objRS.Sort = "DateLastModified DESC"
objRS.MoveFirst
End If
'loop through all the records:
Do Until objRS.EOF
If (Len(sTypeToShow)=0) Or (LCase(sTypeToShow)=LCase(objRS("Type"))) Then
Response.Write("<dt>" & objRS("Name") & " (Type: " & objRS("Type") & ", Last modified: " & objRS("DateLastModified") & ")</dt>" _
& vbCrLf)
End If
objRS.MoveNext()
Loop
'clean up resources
Set oFolder = Nothing
Set objFSO = Nothing
objRS.Close
Set objRS = Nothing
End Sub
To use it in your code, have such line in the HTML body:
<% Call ListFolderContents(Server.MapPath("."), "PDF File") %>
You can of course use different path and change the display to show only what you need.
I wrote a simple vbscript to rename files in a particular folder. Specifically to remove particular content from the filname.
The Script I wrote (listed below) runs fine but the highlighted part (second IF-THEN statement) doesn't run. I can't figure out whats wrong with the code. I plan to add more IF-THEN statement to remove particular content from file names.
I'm a novice at this so please be patient with me. Can anyone help?
Set objFS = CreateObject("Scripting.FileSystemObject")
strFolder="C:\Users\Admin2\Downloads\Compressed"
Set objFolder = objFS.GetFolder(strFolder)
For Each strFile In objFolder.Files
strFileName = strFile.Name
If InStr(strFileName,"(2014)") > 0 Then
strNewFileName = Replace(strFileName,"(2014)","")
strFile.Name = strNewFileName
End If
**If InStr(strFileName,"(digital)") > 0 Then
strNewFileName = Replace(strFileName,"(digital)","")
strFile.Name = strNewFileName
End If**
Next
Type prefix fraud detected:
For Each strFile In objFolder.Files
"strFile" should be "objFile". Dangerous extra variable in:
strFileName = strFile.Name
The variable "strFileName" will get stale if you change "objFile.Name". Use a variable to hold the new/desired name instead.
strNewFileName = objFile.Name
Renaming the file twice will loose changes on the way. Modify "strNewFileName" (in steps or all at once:
strNewFileName = Replace(Replace(strNewFileName, "(2014)", ""), "(digital)", "")
; you don't really need the If guard, because Replace won't change strings that don't contain the target).
Check for .FileExists(strNewFileName) before you do the rename.
Can you prove that there are file names that contain "(digita1)" <-- mark the digit 1) exactly? Lower vs. upper case? A nasty blank?
I hope the following code helps
Set objFS = CreateObject("Scripting.FileSystemObject")
strFolder="pathtofolder"
Set objFolder = objFS.GetFolder(strFolder)
For Each objFile In objFolder.Files
ObjFileName = ObjFile.Name
NewFileName = Replace(Replace(ObjFileName,"(2014)",""),"(digital)","")
Set fileSystemObject = CreateObject("Scripting.FileSystemObject")
If fileSystemObject.FileExists(NewFileName) Then
Else
ObjFile.Name = Trim(NewFileName)
End If
Next
Essentially, I want to get the names of all .zip files in a given directory and unzip them. Right now, I'm working on just getting the names of the files into some sort of array or list. It's grabbing the files correctly, but I don't know a whole lot about VBScript, could someone point me in the right direction?
Dim fileList
Set objFSO = CreateObject("Scripting.FileSystemObject")
objStartFolder = "C:\Test"
Set objFolder = objFSO.GetFolder(objStartFolder)
Wscript.Echo objFolder.Path
Set colFiles = objFolder.Files
For Each objFile in colFiles
If UCase(objFSO.GetExtensionName(objFile.name)) = "ZIP" Then
Wscript.Echo objFile.Name
'Add file names to fileList variable
End If
Next
There is...
' using vbArray
ReDim fileArray(-1)
Set objFSO = CreateObject("Scripting.FileSystemObject")
objStartFolder = "C:\Test"
Set objFolder = objFSO.GetFolder(objStartFolder)
For Each objFile In objFolder.Files
If UCase(objFSO.GetExtensionName(objFile.Name)) = "ZIP" Then
ReDim Preserve fileArray(UBound(fileArray) + 1)
fileArray(UBound(fileArray)) = objFile.Name
End If
Next
WScript.Echo Join(fileArray, vbNewLine)
' using .NET ArrayList (as no biult-in Lists in VBScript)
Dim fileList
Set fileList = CreateObject("System.Collections.ArrayList")
For Each objFile In objFolder.Files
If UCase(objFSO.GetExtensionName(objFile.Name)) = "ZIP" Then
fileList.Add objFile.Name
End If
Next
WScript.Echo Join(fileList.ToArray, vbNewLine)
P.P.S:
Is Files collection dynamic:
Set oFSO = CreateObject("Scripting.FileSystemObject")
curDir = CreateObject("WScript.Shell").CurrentDirectory
Set oFolder = oFSO.GetFolder(curDir)
Set oFiles = oFolder.Files
WScript.Echo "Files count: " & oFiles.Count
Set oFile = oFSO.CreateTextFile(oFSO.GetTempName)
oFile.Close
WScript.Echo "Files count: " & oFiles.Count
[EDIT] But looks like then iterate VBS use snapshot of Files collection, so we can presume that unzip may go safety without need of file list.
As you have the files in a collection already (colFiles), the first reason to have a collection of items - process them in turn - does not make you put them in a second list. Why not just unzip each file instead of putting it in a list? To unzip a file A you don't need info about/access to the other files X in the folder. So the second reason for a collection - needing all elements at the same time/when processing one item - doesn't apply either.
If you insist on a second list, all depends on what you want to do with it/the items. If you just want a list of names, the easiest way would be to put the names/pathes as keys in a Dictionary. If you want the names sorted, a System.Collections.ArrayList/SortedList will be more convenient. If you want to work with more properties/attributes of the files - size, dates, access rights, ... - a Disconnected ADODB Recordset will allow you to store all those properties in a (SQL) Table.
Last, but not least, a simple native VBScript Array (dimensioned for the attributes for each file) could be used: As you know the possible number of elements (colFiles.Count), you can define the size of the array before the loop, assign the items in the loop, and ReDim Preserve it after looping.
Take your pick, and I'll add sample code to this posting.
P.S.:
As #Panayot gave you sample code for Arrays and ArrayLists, it would make sense to ask for an ADO Recordset.
P.P.S:
For worriers:
Dim oFS : Set oFS = CreateObject( "Scripting.FileSystemObject" )
Dim sDir : sDir = "..\testdata\testFilesCollection"
Dim nFile, sFile, oFile, i
If oFS.FolderExists( sDir ) Then oFS.DeleteFolder sDir
oFS.CreateFolder sDir
WScript.Echo "----- Creating", cnMax, "files"
For nFile = 1 To cnMax
sFile = nFile & ".txt"
oFS.CreateTextFile oFS.BuildPath( sDir, sFile )
WScript.Echo sFile, "created"
Next
WScript.Echo "----- Looping over", cnMax, "files and creating", cnMax, "more"
i = 0
For Each oFile In oFS.GetFolder( sDir ).Files
If i <= cnMax Then
sFile = Chr(65 + i) & ".txt"
oFS.CreateTextFile oFS.BuildPath( sDir, sFile )
End If
i = i + 1
WScript.Echo oFile.Name, "seen", sFile, "created"
Next
WScript.Echo "----- Looping over", 2 * cnMax, "files"
For Each oFile In oFS.GetFolder( sDir ).Files
WScript.Echo oFile.Name, "seen"
Next
output:
----- Creating 3 files
1.txt created
2.txt created
3.txt created
----- Looping over 3 files and creating 3 more
2.txt seen A.txt created
3.txt seen B.txt created
1.txt seen C.txt created
----- Looping over 6 files
B.txt seen
2.txt seen
C.txt seen
A.txt seen
3.txt seen
1.txt seen
[The files collection is a snapshot] == #Panayot's argument/evidence ==> The For Each loops over a snapshot of the Files collection - at least wrt additions. Testing for deletions is left as an exercise.