Get file from folder based on pattern criteria and exceptions - vba

Cannot figure out how to implement the following use case.
I pass a folder and criterias (seperated by a ";") to a function and retrieve a file if criterias were matched.
This is working quite well, but I have an exception:
If the criteria pattern in the first position is "ABC", there are multiple files which could match the search pattern.
My goal is to prioritize the finding in a sense that a file containing "RD_" should have a higher prioritization than a file which contains the string "SD_".
Hint:
If a file has the string "RD_" inside and matches also the other criterias, then the function can stop.
If a file has the string "SD_" inside and matches also the other criterias, then the function should not stop and still loop all files within the folder and try to find a file matching all criterias AND "RD_".
Hope you can help me further.
Sub getTargetFile()
Debug.Print getInputFilePath("D:\", "ABC;11111")
End Sub
Function getInputFilePath(inputDirectoryToScanForFile, filenameCriteria) As String
Dim vSplitCriteria: vSplitCriteria = Split(filenameCriteria, ";") ' Split the criteria into pieces and put them into an array
Dim oFSO As Object
Dim oFolder As Object
Dim oFile As Object
Dim i As Integer
Dim sFoundFile As String
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder(inputDirectoryToScanForFile)
Dim blnCorrectFiles As Boolean
For Each oFile In oFolder.Files ' Loop through each file in folder
blnCorrectFiles = True ' assume the current file is correct
'Debug.Print oFile.Name
For i = LBound(vSplitCriteria) To UBound(vSplitCriteria) ' Loop through all file name criteria
If Not InStr(1, UCase(oFile.Name), UCase(vSplitCriteria(i))) > 0 Then ' check whether criteia matched
blnCorrectFiles = False
End If
If blnCorrectFiles = False Then Exit For
Next i
If blnCorrectFiles = True Then ' if all criteria are matched, otherwise it was set to false inbetween the above loop
sFoundFile = oFile.Name
Exit For
End If
Next oFile
If blnCorrectFiles = True Then
getInputFilePath = inputDirectoryToScanForFile & sFoundFile
Else
getInputFilePath = ""
End If
End Function

It sounds like you want to check for RD_ first and then if nothing is found, check for SD_. Which sounds like you just need to run the function twice. Trying to mix and match inside the function would be difficult to generalize and only make things messy.
Sub Prioritize()
Dim Directory As String
Directory = "D:\"
Dim Criteria As String
Criteria = "ABC;11111"
Dim fPath As String
fPath = getInputFilePath(Directory, "RD_;" & Criteria)
If fPath = "" Then fPath = getInputFilePath(Directory, "SD_;" & Criteria)
End Sub

Related

Vba check if file starts with values from list and if not kill it

In my never ending story to learn VBA I am trying to create a macro that deletes files based on the files starting characters and unsure how to proceed.
I have an excel file with numbers in column a ,these numbers are either 4,5 or 6 digits.
I have a file folder with files which may or may not start with these digits from a range from excel file. These files in folders are of different types
But I reckon this may not be an issue still,the naming convention is as follows : ie. 4563_listofitems.pdf,65475_skusdec.doc etc.
My goal is to loop through files and check if the starting characters of the file are on included in the A range of the excel sheet,if so (there may be up to 6 files starting with such number) create a folder named with the found starting characters and move the files starting with these characters into the folder,else if file doesn't start with fixed characters from the list then just delete (kill) that file. My issue is idk how to check the files names against the list.
My code as now for looping trough
Sub loopf
Dim filen as variant
Filen =dir("c:\test\")
While filen <>""
If instr(1,filen,10000)=1 then
'Here I want check against the values from range but unsure how ,should I somehow loop through the range ?
Filen=dir
End if
Wend
End sub
To check if a value is contained within a known list, I like using the Dictionary Object. It has the function Exists which checks if a value is listed within the Dictionary.
So before you loop through the files, you just need to add every one of your accepted numbers into the dictionary. Then while looping though the files check if Dictionary.Exists(Value). If it exists, then the value is good, if not then Kill.
Here's how I would set that up:
Sub loopf()
Dim AcceptedPrefixes As Object
Set AcceptedPrefixes = CreateObject("Scripting.Dictionary")
Dim PrefixRange As Range
Set PrefixRange = ThisWorkbook.Sheets(1).Range("A1:A5")
Dim Cell As Range
For Each Cell In PrefixRange.Cells
If Cell <> "" And Not AcceptedPrefixes.exists(Cell.Value) Then
AcceptedPrefixes.Add CStr(Cell.Value), 0
End If
Next
Dim Directory As String
Directory = "c:\test\"
Dim filen As Variant
filen = Dir(Directory)
While filen <> ""
Dim FilePrefix As String
FilePrefix = Split(filen, "_")(0)
If Not AcceptedPrefixes.exists(FilePrefix) Then
Kill Directory & filen
End If
filen = Dir
Wend
End Sub
Sub Files()
Dim oFSO As Object
Dim oFolder As Object
Dim oFile As Object
Dim i As Integer
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder("C:\test")
For Each oFile In oFolder.Files
'do somthing
Next oFile
End Sub

Check if an outlook folder exists; if not create it

Im trying to check if a folder exists; if it does not then create it. The below is just throwing a run-time error.
Sub AddClose()
Dim myNameSpace As Outlook.NameSpace
Dim myFolder As Outlook.Folder
Dim myNewFolder As Outlook.Folder
Set myNameSpace = Application.GetNamespace("MAPI")
Set myFolder = myNameSpace.GetDefaultFolder(olFolderInbox)
If myFolder.Folders("Close") = 0 Then
myFolder.Folders.Add("Close").Folders.Add ("EID1")
myFolder.Folders("Close").Folders.Add ("EID2")
myFolder.Folders("Close").Folders.Add ("EID3")
End If
End Sub
However, If the folder exists then the below works...
If myFolder.Folders("Close") > 0 Then
MsgBox "Yay!"
End If
Why? What can I do to correct the problem?
Firstly, you are comparing the result of the myFolder.Folders("Close") call (which is supposed to return a MAPIFolder object) with an integer (0). You need to use Is Nothing or Is Not Nothing operator.
Secondly, MAPIFolder.Folders.Item() raises an exception if the folder with a given name is not found. You need to trap that exception (as ugly as it is in VBA) and either check the Err.Number value or check that the return object is set:
On Error Resume Next
set subFolder = myFolder.Folders.Item("Close")
if subFolder Is Nothing Then
set subFolder = myFolder.Folders.Add("Close")
End If
I do not understand: If myFolder.Folders("Close") = 0 Then. myFolder.Folders("Close") is a folder and I would not have thought of comparing it against zero. Do you have a reference to a site where this functionality is explained because I would like to understand it?
I wish to create a folder if it does not exist often enough to have written a function. My function does not have ideal parameters for your requirement but it works. I offer it as tested code that does what you want or as a source of ideas for your own code.
Sub DemoGetCreateFldr shows how to use the function GetCreateFldr to achieve the effect I believe you seek.
I do not use GetDefaultFolder because, on my system, it returns a reference to a store I do not use. “Outlook Data File” is Outlook’s default store but the wizard created a separate store for each of my two email addresses. In Set Store = Session.Folders("Outlook Data File"), replace "Outlook Data File" with the name of the store holding the Inbox for which you want to create subfolders.
The first call of GetCreateFldr creates folder “Close” if it does not exist and then creates folder “EID1”. I save the reference to the folder, and use Debug.Print to demonstrate it returns the correct reference.
For folders “EID2” and “EID3”, I do not save the reference which matches your code.
If folders “Close”, “EID1”, “EID2” and “EID3” exist, GetCreateFldr does not attempt to create them although it still returns a reference.
Hope this helps.
Sub DemoGetCreateFldr()
Dim FldrEID1 As Folder
Dim FldrNameFull(1 To 3) As String
Dim Store As Folder
Set Store = Session.Folders("Outlook Data File")
FldrNameFull(1) = "Inbox"
FldrNameFull(2) = "Close"
FldrNameFull(3) = "EID1"
Set FldrEID1 = GetCreateFldr(Store, FldrNameFull)
Debug.Print FldrEID1.Parent.Parent.Parent.Name & "|" & _
FldrEID1.Parent.Parent.Name & "|" & _
FldrEID1.Parent.Name & "|" & _
FldrEID1.Name
FldrNameFull(3) = "EID2"
Call GetCreateFldr(Store, FldrNameFull)
FldrNameFull(3) = "EID3"
Call GetCreateFldr(Store, FldrNameFull)
End Sub
Public Function GetCreateFldr(ByRef Store As Folder, _
ByRef FldrNameFull() As String) As Folder
' * Store identifies the store, which must exist, in which the folder is
' wanted.
' * FldrNameFull identifies a folder which is or is wanted within Store.
' Find the folder if it exists otherwise create it. Either way, return
' a reference to it.
' * If LB is the lower bound of FldrNameFull:
' * FldrNameFull(LB) is the name of a folder that is wanted within Store.
' * FldrNameFull(LB+1) is the name of a folder that is wanted within
' FldrNameFull(LB).
' * FldrNameFull(LB+2) is the name of a folder that is wanted within
' FldrNameFull(LB+1).
' * And so on until the full name of the wanted folder is specified.
' 17Oct16 Date coded not recorded but must be before this date
Dim FldrChld As Folder
Dim FldrCrnt As Folder
Dim ChildExists As Boolean
Dim InxC As Long
Dim InxFN As Long
Set FldrCrnt = Store
For InxFN = LBound(FldrNameFull) To UBound(FldrNameFull)
ChildExists = True
' Is FldrNameFull(InxFN) a child of FldrCrnt?
On Error Resume Next
Set FldrChld = Nothing ' Ensure value is Nothing if following statement fails
Set FldrChld = FldrCrnt.Folders(FldrNameFull(InxFN))
On Error GoTo 0
If FldrChld Is Nothing Then
' Child does not exist
ChildExists = False
Exit For
End If
Set FldrCrnt = FldrChld
Next
If ChildExists Then
' Folder already exists
Else
' Folder does not exist. Create it and any children
Set FldrCrnt = FldrCrnt.Folders.Add(FldrNameFull(InxFN))
For InxFN = InxFN + 1 To UBound(FldrNameFull)
Set FldrCrnt = FldrCrnt.Folders.Add(FldrNameFull(InxFN))
Next
End If
Set GetCreateFldr = FldrCrnt
End Function
Its not a good coding practice to user on error.
I would recommend you to traverse through the folders.
Then if a certain name is not found create it.
The code below part of my macro I use.
It looks for a "Duplicates" under inbox.
It intentionally doesn't do this recursively.
Sub createDuplicatesFolder()
Dim folderObj, rootfolderObj, newfolderObj As Outlook.folder
Dim NameSpaceObj As Outlook.NameSpace
duplicatefolder = False
For Each folderObj In Application.Session.Folders
If folderObj.Name = "Duplicates" Then duplicatefolder = True
Next
If duplicatefolder = False Then
Set rootfolderObj = NameSpaceObj.GetDefaultFolder(olFolderInbox)
Set newfolderObj = rootfolderObj.Folders.Add("Duplicates")
End Sub
A slow way. Depends on number of folders.
Sub checkFolder()
Dim folderObj As folder
Dim rootfolderObj As folder
Dim newfolderObj As folder
Dim checkFolderName As String
' Check and add in the same location
Set rootfolderObj = Session.GetDefaultFolder(olFolderInbox)
' Check and add the same folder name
checkFolderName = "checkedFolder"
For Each folderObj In rootfolderObj.folders
If folderObj.name = checkFolderName Then
Set newfolderObj = rootfolderObj.folders(checkFolderName)
'Reduces the search time, if the folder exists
Exit For
End If
Next
If newfolderObj Is Nothing Then
Set newfolderObj = rootfolderObj.folders.add(checkFolderName)
End If
Debug.Print newfolderObj.name
End Sub
A fast way. Add without checking existing folders.
Sub addFolder_OnErrorResumeNext()
Dim rootFolder As folder
Dim addFolder As folder
Dim addFolderName As String
Set rootFolder = Session.GetDefaultFolder(olFolderInbox)
addFolderName = "addFolder"
On Error Resume Next
' Bypass expected error if folder exists
Set addFolder = rootFolder.folders.add(addFolderName)
' Return to normal error handling for unexpected errors
' Consider mandatory after On Error Resume Next
On Error GoTo 0
' In other cases the expected error should be handled.
' For this case it can be ignored.
Set addFolder = rootFolder.folders(addFolderName)
Debug.Print addFolder.name
End Sub

VBA Error 52 on a function that tests if a file exists

I'm trying to pull text from a bunch of XML files into Word. I'm working from a list of files and have found that some of them don't actually exist in the folder. So, I'm using this function to check whether the files actually exist before opening them. But I'm still getting error 52 (Bad file name or number).
This is the function:
Function FileThere(FileName As String) As Boolean
FileThere = (Dir(FileName) > "")
End Function
And this is the code I'm calling it from:
Sub PullContent()
Dim docList As Document
Dim docCombinedFile As Document
Dim objFileListTable As Table
Dim objRow As Row
Dim strContent As String
Dim strFileCode As String
'Code # for the current file. (Pulled in temporarily, output to the Word doc.)
Dim strFilename As String
'Name of XML file. Created based on strFileCode
Set docCombinedFile = Documents.Add
'The new doc which will list all warnings
Dim strXml As String
'String variable that holds the entire content of the data module
Dim strInvalidCodes
'String listing any invalid file codes. Displayed at the end.
Dim FSO As Object: Set FSO = CreateObject("Scripting.FileSystemObject")
Documents.Open FileName:="C:\Users\kelly.keck\Documents\Triton MTS\IETMs - Test\IETMList.docx"
Set docList = Documents("IETMList.docx")
Set objFileListTable = docList.Tables(1)
For Each objRow In objFileListTable.Rows
strFileCode = objRow.Cells(4).Range.Text
strFileCode = Left(strFileCode, Len(strFileCode) - 2)
strFilename = strFileCode & ".xml"
strPath = "C:\Applications\xml\"
If FileThere(strPath & strFileCode) = True Then
'MsgBox (strPath & strFilename)
strXml = FSO.OpenTextFile(strPath & strFilename).ReadAll
Else
strInvalidCodes = strInvalidCodes & vbCr & strFileCode
End If
Next
MsgBox ("The following filenames were invalid: " & vbCr & strInvalidCodes)
End Sub
Getting this error seems to defeat the purpose of having a function to check if a file exists, but I'm not sure what's wrong with the function.
A bit late to the party, but this hasn't had an accepted answer yet.
I generally use this method to test if a file exists, and your code uses FileSystemObject already so could use that reference.
Public Function FileExists(ByVal FileName As String) As Boolean
Dim oFSO As Object
Set oFSO = CreateObject("Scripting.FileSystemObject")
FileExists = oFSO.FileExists(FileName)
End Function
I believe that you need to be sure that FileThere is actually returning the Boolean value you intend. It would be more reliable if you checked the Len property (the number of characters) or checked whether it actually returns the empty string.
The following is more verbose than absolutely necessary in order to make the logic clear. If you were to use Len, instead, then you'd check Len(Dir(FileName)) > 0
Function FileThere(FileName as String) as Boolean
Dim bFileExists as Boolean
If Dir(FileName) = "" Then
bFileExists = False
Else
bFileExists = True
End If
FileThere = bFileExists
End Function

FileSystemObject.CreateFolder to create directory and subdirectories

I would like to create a directory and a subdirectory with the following code:
Public fso As Scripting.FileSystemObject
Set fso = New Scripting.FileSystemObject
fso.CreateFolder ("C:\Users\<my_username>\DataEntry\logs")
I am trying to create nested directories. In this case, the DataEntry directory would not exist, so essentially I would like to create 2 directories, DataEntry\logs under C:\Users\<username>
If I enter command prompt, I can create that directory with mkdir without any issues. However, I simply cannot get VBA to create that folder and I get:
Run-time error '76':
Path not found
I am using Excel VBA 2007/2010
tigeravatar's looping answer might work, but it's a bit hard to read. Instead of micromanaging the string handling yourself, the FileSystemObject has path manipulation functions available, and recursion is slightly easier to read than a loop.
Here is the function I use:
Function CreateFolderRecursive(path As String) As Boolean
Dim FSO As New FileSystemObject
'If the path exists as a file, the function fails.
If FSO.FileExists(path) Then
CreateFolderRecursive = False
Exit Function
End If
'If the path already exists as a folder, don't do anything and return success.
If FSO.FolderExists(path) Then
CreateFolderRecursive = True
Exit Function
End If
'recursively create the parent folder, then if successful create the top folder.
If CreateFolderRecursive(FSO.GetParentFolderName(path)) Then
If FSO.CreateFolder(path) Is Nothing Then
CreateFolderRecursive = False
Else
CreateFolderRecursive = True
End If
Else
CreateFolderRecursive = False
End If
End Function
Need to create each folder one at a time. You can use code like this to do so:
Sub tgr()
Dim strFolderPath As String
Dim strBuildPath As String
Dim varFolder As Variant
strFolderPath = "C:\Users\<my_username>\DataEntry\logs"
If Right(strFolderPath, 1) = "\" Then strFolderPath = Left(strFolderPath, Len(strFolderPath) - 1)
For Each varFolder In Split(strFolderPath, "\")
If Len(strBuildPath) = 0 Then
strBuildPath = varFolder & "\"
Else
strBuildPath = strBuildPath & varFolder & "\"
End If
If Len(Dir(strBuildPath, vbDirectory)) = 0 Then MkDir strBuildPath
Next varFolder
'The full folder path has been created regardless of nested subdirectories
'Continue with your code here
End Sub
Agree with MarkD's suggestion to utilize recursion, it was the code I came here looking to find. In a scenario where the path provided uses a nonexistent root folder it will result in an infinite loop. Adding to MarkD's solution to check for zero length path.
Function CreateFolderRecursive(path As String) As Boolean
Static FSO As FileSystemObject
'Initialize FSO variable if not already setup
If FSO Is Nothing Then Set lFSO = New FileSystemObject
'Is the path paramater populated
If Len(path) = 0 Then
CreateFolderRecursive = False
Exit Function
End If
'If the path exists as a file, the function fails.
If FSO.FileExists(path) Then
CreateFolderRecursive = False
Exit Function
End If
'If the path already exists as a folder, don't do anything and return success.
If FSO.FolderExists(path) Then
CreateFolderRecursive = True
Exit Function
End If
'recursively create the parent folder, then if successful create the top folder.
If CreateFolderRecursive(FSO.GetParentFolderName(path)) Then
If FSO.CreateFolder(path) Is Nothing Then
CreateFolderRecursive = False
Else
CreateFolderRecursive = True
End If
Else
CreateFolderRecursive = False
End If
End Function

SQL "%" equivalent in VBA

Is there any SQL equivalent of "%" sign in VBA?
I need to return a few files just with some characters in the middle.
Help really appreciated!
For instance here is my code: I need to download all file that has in the name 2013 from that webpage and save and call them differently. Is this mission possible?
Sub Sample()
Dim strURL As String
Dim strPath As String
Dim i As Integer
strURL = "http://cetatenie.just.ro/wp-content/uploads/Ordin-********.2013.pdf"
strPath = "C:\Documents and Settings\ee28118\Desktop\178.pdf"
Ret = URLDownloadToFile(0, strURL, strPath, 0, 0)
If Ret = 0 Then
MsgBox "File successfully downloaded"
Else
MsgBox "Unable to download the file"
End If
End Sub
You can use the Like Operator.
Characters in pattern Matches in string
? Any single character.
* Zero or more characters.
# Any single digit (0–9).
[charlist] Any single character in charlist.
[!charlist] Any single character not in charlist
Example :
Dim MyCheck
MyCheck = "aBBBa" Like "a*a" ' Returns True.
MyCheck = "F" Like "[A-Z]" ' Returns True.
MyCheck = "F" Like "[!A-Z]" ' Returns False.
MyCheck = "a2a" Like "a#a" ' Returns True.
MyCheck = "aM5b" Like "a[L-P]#[!c-e]" ' Returns True.
MyCheck = "BAT123khg" Like "B?T*" ' Returns True.
MyCheck = "CAT123khg" Like "B?T*" ' Returns False.
When you navigate to the uploads folder, you get a directory listing of all the files in it. You can loop through the hyperlinks on that listing and test each to see if it meets your criterion and, if so, download it. You need a reference to MSXML and MSHTML. Here's an example.
Sub Sample()
Dim sUrl As String
Dim xHttp As MSXML2.XMLHTTP
Dim hDoc As MSHTML.HTMLDocument
Dim hAnchor As MSHTML.HTMLAnchorElement
Dim Ret As Long
Dim sPath As String
Dim i As Long
sPath = "C:\Documents and Settings\ee28118\Desktop\"
sUrl = "http://cetatenie.just.ro/wp-content/uploads/"
'Get the directory listing
Set xHttp = New MSXML2.XMLHTTP
xHttp.Open "GET", sUrl
xHttp.send
'Wait for the page to load
Do Until xHttp.readyState = 4
DoEvents
Loop
'Put the page in an HTML document
Set hDoc = New MSHTML.HTMLDocument
hDoc.body.innerHTML = xHttp.responseText
'Loop through the hyperlinks on the directory listing
For i = 0 To hDoc.getElementsByTagName("a").Length - 1
Set hAnchor = hDoc.getElementsByTagName("a").Item(i)
'test the pathname to see if it matches your pattern
If hAnchor.pathname Like "Ordin-*.2013.pdf" Then
Ret = UrlDownloadToFile(0, sUrl & hAnchor.pathname, sPath, 0, 0)
If Ret = 0 Then
Debug.Print sUrl & hAnchor.pathname & " downloaded to " & sPath
Else
Debug.Print sUrl & hAnchor.pathname & " not downloaded"
End If
End If
Next i
End Sub
Edit
I assumed that URLDownloadToFile was already written. I didn't write one, I just used the below function to test the code that iterates through the files. You can use it to make sure the above code works for you, but you'll need to write the actual code to download the file eventually. With all the arguments to URLDownloadToFile, I'm surprised it doesn't exist already.
Function UrlDownloadToFile(lNum As Long, sUrl As String, sPath As String, lNum1 As Long, lNum2 As Long) As Long
UrlDownloadToFile = 0
End Function
Try below code : The boolean function would return true if the string has the string 2013 in it.
Sub Sample()
Dim result As Boolean
result = has2013("http://cetatenie.just.ro/wp-content/uploads/Ordin-********.2013.pdf")
Debug.Print result
result = has2013("http://cetatenie.just.ro/wp-content/uploads/Ordin-********.2014.pdf")
Debug.Print result
End Sub
Function has2013(lnk As String) As Boolean
has2013 = lnk Like "*2013*"
End Function
in VBA use the LIKE function with wildcard characters:
here is an example (copied from Ozgrid Forums)
Dim sht As Worksheet
For Each sht In ActiveWorkbook.Worksheets
If sht.Name Like "FRI*" Then
'Add code for Friday sheets
Else
If sht.Name Like "MON*" Then
'Add code for Monday sheets
End If
End If
Next
The multiplication character * takes the place of zero or more characters, whereas ? takes the place of exactly 1 character, and # takes the place of 1 number. There are other more specific char. matching strategies if you only want to match certain characters.
so there you go!
Also, you could take a look at Ozgrid Forums: Using Regular Expressions in VBA
To get a list of the files on the server, read up on FTP (using DIR) at Mr Excel - List files using FTP