Use Dir to find file without "AAA" - excel

I have some files that are called Team-(Random Number).txt.
Dir("Team-" & "*" & ".txt")
But when there have been changes there could be text files called Team-(Random Number)-AAA.txt, Team-(Random Number)-AAB.txt and so but the most recent file is always called Team-(Random Number).txt.
Since Dir only returns 1 file and does this randomly is there a way to get the file Team-(Random Number).txt?
It should be no problem if dir returned the result in a normal order but apparently it does it randomly.
I've thought of excluding the -AAA part but don't know what the syntax should. Or in a less efficient way to get all files and sort it in an array but with 10 - 200 files it's not very efficient.
Now I'm hoping could give me the syntax of excluding the part or other workaround for my problem thanks!

I'd say go for Regular Expressions.
Private Sub TeamTxtExists()
Dim Path As String, Pattern As String, FileFound As String
Dim REGEX As Object, Matches As Object
Dim oFSO As Object, oFolder As Object, oFile As Object
Path = "D:\Personal\Stack Overflow\" 'Modify as necessary.
Pattern = "(Team-(\d+).txt)"
Set REGEX = CreateObject("VBScript.RegExp")
With REGEX
.Pattern = Pattern
.Global = True
.IgnoreCase = True
End With
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder(Path)
For Each oFile In oFolder.Files
Set Matches = REGEX.Execute(oFile.Name)
For Each Match In Matches
Debug.Print Match.Value
Next Match
Next oFile
End Sub
This will print in your immediate window (Ctrl-G in the VBE) all the names of text files that don't have AAA or the like in their filenames. Tried and tested.

In a similar vein to Loop through files in a folder using VBA?
Use Dir to efficiently find the first group of files that match team-xxxx.txt
Then zero in on the wanted match which could either be done with
Like for a simple match
Regexp for a harder match
Exit the Dir list on a successful match
I went with the regexp.
code
Sub LoopThroughFiles()
Dim objRegex As Object
Dim StrFile As String
Dim bFound As Boolean
bFound = False
Set objRegex = CreateObject("VBScript.RegExp")
objRegex.Pattern = "team-\d+"
StrFile = Dir("c:\temp\team-*.txt")
Do While Len(StrFile) > 0
If objRegex.test(StrFile) = False Then
StrFile = Dir
Else
bFound = True
MsgBox "Your file is " & StrFile
Exit Do
End If
Loop
If Not bFound Then MsgBox "No Match", vbCritical
End Sub

Is this helping?:
Dir("Your_folder_path_ending_with_a_\" & "Team-(*).txt")
Going a bit more in-depth and using the folder content shown in the picture:
This sub will return all the file names that only contain "Team-(Random Number).txt":
Sub showFileName()
Dim FolderPath As String: FolderPath = "C:\test\"
Dim Filter As String: Filter = "Team-(*).txt"
Dim dirTmp As String
dirTmp = Dir(FolderPath & Filter)
Do While Len(dirTmp) > 0
Debug.Print dirTmp
dirTmp = Dir
Loop
End Sub
The result is:
Team-(123).txt
Team-(14).txt
Team-(PI).txt

Related

Loop through all subfolders

I am trying to loop through all subfolders. This script works but then only pulls some folders and not others. I need it to pull all files in the folder. I did not create this full script but would like to modify it.
UPDATE:
I tried this alternate solution below and it works.
Sub loopAllSubFolderSelectStartDirectory()
Dim FSOLibrary As Object
Dim FSOFolder As Object
Dim folderName As String
'Set the folder name to a variable
folderName = "C:\Users\dreid_000\Desktop\PhaseII\"
'Set the reference to the FSO Library
Set FSOLibrary = CreateObject("Scripting.FileSystemObject")
'Another Macro must call LoopAllSubFolders Macro to start
LoopAllSubFolders FSOLibrary.GetFolder(folderName)
End Sub
Sub LoopAllSubFolders(FSOFolder As Object)
Dim FSOSubFolder As Object
Dim FSOFile As Object
Dim soldname As String
Dim sNewName As String
Dim sTempFile() As String
Dim sPath As String
Set Fso = CreateObject("Scripting.FileSystemObject")
'For each subfolder call the macro
For Each FSOSubFolder In FSOFolder.subfolders
LoopAllSubFolders FSOSubFolder
Next
'For each file, print the name
For Each FSOFile In FSOFolder.Files
'Insert the actions to be performed on each file
FSOFile.Name = "PhaseII.xlsx"
'This example will print the full file path to the immediate window
Debug.Print FSOFile.Path
Next
End Sub
Here is a good, generic solution that can meet basically any search folder and its subfolders requirements. It is a function that calls itself recursively and outputs a dictionary object containing the complete results, code commented for clarity:
Public Function SearchDirectory(ByVal arg_sFolderPath As String, _
Optional ByVal arg_sSearch As String = "*", _
Optional ByVal arg_bMatchCase As Boolean = False, _
Optional ByVal arg_bIncludeSubFolders As Boolean = True, _
Optional ByRef arg_hResults As Object) _
As Object
'Purpose of this function is to search a directory (and probably all of its subfolders)
'for files that match an optionally provided search string, and the match may or may
'not be case sensitive. It then collects all of the results in a dictionary object and
'returns that dictionary object as the function output
'
'Parameters:
' arg_sFolderPath: [Required][String] -The folder path of the original directory that will be searched
'
' arg_sSearch: [Optional][String] -A pattern that will be matched against.
' -For example, to find all Excel files you would use "*.xls*"
' -Default value is "*" which will return all files
'
' arg_bMatchCase: [Optional][Boolean] -Specifies whether or not to match arg_sSearch as case sensitive
' -For example, if this is set to True and the arg_sSearch is set to "*.xls*",
' then an Excel file named "EXCELFILE.XLSX" would NOT be found
' -Default value is False which will make matches not case sensitive
'
' arg_bIncludeSubFolders [Optional][Boolean] -Specifies whether or not to search all subfolders recursively
' -Default value is True which will include results in all subfolders
'
' arg_hResults [Optional][Dictionary Object] -An existing dictionary object to hold the results in
' -Typically this will not be provided on initial call, and is used
' during recursive calls to store all relevant results for output
'
'Author: tigeravatar on stackoverflow at https://stackoverflow.com/questions/56970854/loop-through-all-subfolders
'Created: July 10, 2019
'Static so that during recursive search it doesn't need to be recreated on every recursive call
Static oFSO As Object
If oFSO Is Nothing Then Set oFSO = CreateObject("Scripting.FileSystemObject")
'Check if arg_hResults was provided (typically not on first call, but will be on all subsequent recursive calls)
'This preserves all found matching file results throughout all recursive calls of the function
Dim hResults As Object
If arg_hResults Is Nothing Then Set hResults = CreateObject("Scripting.Dictionary") Else Set hResults = arg_hResults
'Variable used to store the folder path separator
Dim sPS As String
sPS = Application.PathSeparator
'Adjust so that even if folder path isn't passed with an ending Path Separator, the function can handle it appropriately
Dim sFolderPath As String
If Right(arg_sFolderPath, Len(sPS)) = sPS Then sFolderPath = arg_sFolderPath Else sFolderPath = arg_sFolderPath & sPS
'Verify the folder path provided is valid. Allow for hidden folders to be searched as well
If Len(Dir(sFolderPath, vbDirectory + vbHidden)) = 0 Then
MsgBox "Invalid directory path provided: " & Chr(10) & arg_sFolderPath, , "Search Directory Error"
Set SearchDirectory = Nothing
Exit Function
End If
'Using the FileSystemObject, work with the provided and validated folder path
Dim oFolder As Object
Set oFolder = oFSO.GetFolder(sFolderPath)
'Loop through all files in the folder
Dim oFile As Object
Dim bMatch As Boolean
For Each oFile In oFolder.Files
'Verify the file matches the provided search pattern (if any) according to case sensitivity
bMatch = False
If arg_bMatchCase = True Then
If oFile.Name Like arg_sSearch Then bMatch = True
Else
If LCase(oFile.Name) Like LCase(arg_sSearch) Then bMatch = True
End If
'If match found, add it to the hResults dictionary
If bMatch = True Then
If Not hResults.Exists(oFile.Path) Then hResults.Add oFile.Path, oFile.Path
End If
Next oFile
'If set to search subfolders (default behavior), have the function recursively call itself to search all subfolders
If arg_bIncludeSubFolders = True Then
Dim oSubFolder As Object
For Each oSubFolder In oFolder.SubFolders
Set hResults = SearchDirectory(oSubFolder.Path, arg_sSearch, arg_bMatchCase, arg_bIncludeSubFolders, hResults)
Next oSubFolder
End If
'Set function output to the hResults dictionary if it contains any matched file results
If hResults Is Nothing Then
Set SearchDirectory = Nothing
Else
If hResults.Count = 0 Then Set SearchDirectory = Nothing Else Set SearchDirectory = hResults
End If
End Function
This is an example of how to use the function and work with its results:
Sub tgr()
'Create an object variable and set it to the function SearchDirectory
'Provide SearchDirectory arguments as desired
Dim hFoundFiles As Object
Set hFoundFiles = SearchDirectory("C:\Test")
'Verify it actually found files matching your criteria in the folder specified
If hFoundFiles Is Nothing Then Exit Sub 'Didn't return any results
'Can output results to a worksheet
ActiveWorkbook.ActiveSheet.Range("A1").Resize(hFoundFiles.Count).Value = Application.Transpose(hFoundFiles.Keys)
'Can loop through each result if you need to do something with them individually
Dim vFile As Variant
For Each vFile In hFoundFiles.Keys
'Do something here
Debug.Print vFile
Next vFile
End Sub

Extract a folder name from file path

I have a file path (which is a connection path for the worksheet) in the following format:
C:\ExcelFiles\Data\20140522\File1_20140522.csv
I want to extract 20140522.
I tried using responses of How to extract groups of numbers from a string in vba, but they don't seem to work in my case.
please find below
Filename = "C:\ExcelFiles\Data\20140522\File1_20140522.csv"
a = Replace(Mid(Filename, InStrRev(Filename, "_") + 1, Len(Filename)), ".csv", "")
Try the following. Folder is selected.
Sub Folder_S()
Dim sFolder As FileDialog
On Error Resume Next
Set sFolder = Application.FileDialog(msoFileDialogFolderPicker)
If sFolder.Show = -1 Then
Folder_Select sFolder.SelectedItems(1), True
End If
End Sub
Sub Folder_Select(ByVal SourceFolderName As String, ByVal IncludeSubfolders As Boolean)
Dim FSO As Object
Dim SourceFolder As Object
Dim FileItem As Object
Dim strFile As String
Dim FileName As Variant
Dim pathParts() As String
Dim pathPart As String
Dim i As Long
Set FSO = CreateObject("Scripting.FileSystemObject")
Set SourceFolder = FSO.GetFolder(SourceFolderName)
pathParts = Split(SourceFolder.Path, Application.PathSeparator)
pathPart = SourceFolder.Path
For i = 0 To UBound(pathParts)
If pathParts(i) = "20140522" Then
pathPart = pathParts(i - 0)
Exit For
End If
Next i
Row = ActiveCell.Row
With CreateObject("Scripting.Dictionary")
For Each FileItem In SourceFolder.Files
strFile = FileItem.Name
.Item(strFile) = Array(FileItem.Name)
Next FileItem
If .Count > 0 Then
For Each FileName In .Items
Cells(Row, 2).Formula = pathPart
Next FileName
End If
End With
End Sub
I found your question by searching a solution how to get a folder path from a file that is inside this folder path. But your question doesn't match exactly what I need. For those who by your question title will find it for the same purpose as I found, below is my function:
Function getFolderPathFromFilePath(filePath As String) As String
Dim lastPathSeparatorPosition As Long
lastPathSeparatorPosition = InStrRev(filePath, Application.PathSeparator)
getFolderPathFromFilePath = Left(filePath, lastPathSeparatorPosition - 1)
End Function
In some solutions for this purpose, I used FSO, but it takes resources, and I think it isn't worthy to create FSO object if you need it only for this simple function.
the accepted answer is not accurate to read the folder name. here is more dynamic code.
use splitter which splits string based on delimeter and makes an array. now read the second last element in array, thats the folder name.
Dim fileName As String
fileName = "C:\ExcelFiles\Data\20140522\File1_20140522.csv"
Dim vPathSplitter As Variant
vPathSplitter = Split(fileName, "\")
MsgBox (vPathSplitter(UBound(vPathSplitter) - 1))
The below answer gets your file path from a range, rather than a fixed string. Much more handy if your planning on getting your filename from your sheets, which I imagine you are.
Sub GetFileDate()
Dim filename As String
filename = Sheets("Sheet1").Range("C9").Value 'Or Wherever your file path is
MsgBox Replace(Right(filename, 12), ".csv", "")
End Sub
This assumes the numbers your extracting will ALWAYS be dates in YYYYMMDD format and the file type is always .csv

Excel Macro to apend data from two notepad into a single notepad

I want to delete the last line contain '*' of two notepad and apend the reamining data into a new notepad by excel macro.
Please guys help me out. I can't find any suggestion.
Using #mehow's suggestion, here is some code that you can use:
' To get this to run, you'll need to reference Microsoft Scripting Runtime:
' Per http://stackoverflow.com/questions/3233203/how-do-i-use-filesystemobject-in-vba
' Within Excel you need to set a reference to the VB script run-time library. The relevant file is usually located at \Windows\System32\scrrun.dll
' To reference this file, load the Visual Basic Editor (ALT-F11)
' Select Tools - References from the drop-down menu
' A listbox of available references will be displayed
' Tick the check-box next to 'Microsoft Scripting Runtime'
' The full name and path of the scrrun.dll file will be displayed below the listbox
' Click on the OK button
Sub appFiles()
'File path and names for each file
Dim sFile1 As String
Dim sFile2 As String
Dim sFileLast As String
'Search string
Dim sSearchStr As String
'Delimiter used to separate/join lines
Dim sDL As String
'If the final file already exists, should it overwrite the previous _
contents (True) or append to the end of the file (False)
Dim doOverwrite As Boolean
'File contents
Dim sMsg1 As String
Dim sMsg2 As String
Dim sMsgFinal As String
sFile1 = "C:\Users\foobar\Desktop\foo.txt"
sFile2 = "C:\Users\foobar\Desktop\foo2.txt"
sFileLast = "C:\Users\foobar\Desktop\fooFinal.txt"
sSearchStr = "*"
sDL = Chr(13) & Chr(10)
doOverwrite = True
sMsg1 = appendLines(sFile1, sSearchStr, sDL)
sMsg2 = appendLines(sFile2, sSearchStr, sDL)
sMsgFinal = sMsg1 & sDL & sMsg2
Call writeToFile(sMsgFinal, sFileLast, doOverwrite)
End Sub
Function appendLines(sFileName As String, sSearchStr As String, Optional sDL As String = " ") As String
Dim oFSO As FileSystemObject
Set oFSO = New FileSystemObject
Dim oFS As TextStream
Dim sStr As String
Dim sMsg As String
If oFSO.fileexists(sFileName) Then 'Check if file exists
On Error GoTo Err
Set oFS = oFSO.openTextFile(sFileName)
'Read file
Do While Not oFS.AtEndOfStream
sStr = oFS.ReadLine
If InStr(sStr, sSearchStr) Then
appendLines = sMsg
Else
sMsg = sMsg & sStr & sDL
End If
Loop
oFS.Close
Else
Call MsgBox("The file path (" & sFileName & ") is invalid", vbCritical)
End If
Set oFS = Nothing
Set oFSO = Nothing
Exit Function
Err:
Call MsgBox("Error occurred while reading the file.", vbCritical)
oFS.Close
Set oFS = Nothing
Set oFSO = Nothing
End Function
Sub writeToFile(sMsg As String, sFileName As String, Optional doOverwrite As Boolean = False)
Dim oFSO As FileSystemObject
Set oFSO = New FileSystemObject
Dim oFS As TextStream
On Error GoTo Err
If oFSO.fileexists(sFileName) Then
If doOverwrite Then
Set oFS = oFSO.openTextFile(sFileName, ForWriting)
Else
Set oFS = oFSO.openTextFile(sFileName, ForAppending)
End If
Else
Set oFS = oFSO.CreateTextFile(sFileName, True)
End If
Call oFS.write(sMsg)
oFS.Close
Set oFS = Nothing
Set oFSO = Nothing
Exit Sub
Err:
Call MsgBox("Error occurred while writing to the file.", vbCritical)
oFS.Close
Set oFS = Nothing
Set oFSO = Nothing
End Sub
You'll need to customize the appFiles routine as needed, by providing file names to sFile1, sFile2, and sFileLast; your desired search string to sSearchStr (you mentioned using "*"); a delimiter to separate lines (it's currently written to use a carriage return and new line); and a parameter deciding whether or not to overwrite the final file (if you find yourself running this multiple times with the same final file).
Here's another link that I used while writing the code above: link - Explains how to write to a file from within a macro
Hope this helps.

VBA - Identifying whether a string is a file, a folder or a web url

I need to perform a number of actions, initiated by the passing of a string, with the course of actions depending on whether the string is a file, a folder or a web url.
FYI - for a file I copy the file to a repository, for a folder I am making a shortcut .lnk and copy that to a repository, and for a web url I am making a shortcut .url and copy that to a repository.
I developed a solution, but it isn't robust enough; I get the occasional error from misidentifying the string. The method I used was to count the dots in the string, and apply the rule:
If Dots = 1 Then... it's a file.
If Dots < 1 Then... it's a folder.
If Dots > 1 Then... it's a website.
I then improved this using a couple of functions I found on the web:
Dots = Len(TargetPath) - Len(Replace(TargetPath, ".", "")) ' Crude check for IsURL (by counting Dots)
If CheckFileExists(TargetPath) = True Then Dots = 1 ' Better check for IsFile
If CheckFolderExists(TargetPath) = True Then Dots = 0 ' Better check for IsFolder
Trouble is, I am still having problems with 2 circumstances:
When filenames contain additional dots, e.g. \Report.01.doc
When the string is a file or folder on a remote intranet location (I think this could be misidentifying as a web url).
Any pointers in the right direction would be much appreciated.
Tom H
This might solve your problem, or atleast lead you to one:
Function CheckPath(path) As String
Dim retval
retval = "I"
If (retval = "I") And FileExists(path) Then retval = "F"
If (retval = "I") And FolderExists(path) Then retval = "D"
If (retval = "I") And HttpExists(path) Then retval = "F"
' I => Invalid | F => File | D => Directory | U => Valid Url
CheckPath = retval
End Function
Function FileExists(ByVal strFile As String, Optional bFindFolders As Boolean) As Boolean
'Purpose: Return True if the file exists, even if it is hidden.
'Arguments: strFile: File name to look for. Current directory searched if no path included.
' bFindFolders. If strFile is a folder, FileExists() returns False unless this argument is True.
'Note: Does not look inside subdirectories for the file.
'Author: Allen Browne. http://allenbrowne.com June, 2006.
Dim lngAttributes As Long
'Include read-only files, hidden files, system files.
lngAttributes = (vbReadOnly Or vbHidden Or vbSystem)
If bFindFolders Then
lngAttributes = (lngAttributes Or vbDirectory) 'Include folders as well.
Else
'Strip any trailing slash, so Dir does not look inside the folder.
Do While Right$(strFile, 1) = "\"
strFile = Left$(strFile, Len(strFile) - 1)
Loop
End If
'If Dir() returns something, the file exists.
On Error Resume Next
FileExists = (Len(Dir(strFile, lngAttributes)) > 0)
End Function
Function FolderExists(ByVal strPath As String) As Boolean
On Error Resume Next
FolderExists = ((GetAttr(strPath) And vbDirectory) = vbDirectory)
End Function
Function TrailingSlash(varIn As Variant) As String
If Len(varIn) > 0 Then
If Right(varIn, 1) = "\" Then
TrailingSlash = varIn
Else
TrailingSlash = varIn & "\"
End If
End If
End Function
Function HttpExists(ByVal sURL As String) As Boolean
Dim oXHTTP As Object
Set oXHTTP = CreateObject("MSXML2.XMLHTTP")
If Not UCase(sURL) Like "HTTP:*" Then
sURL = "http://" & sURL
End If
On Error GoTo haveError
oXHTTP.Open "HEAD", sURL, False
oXHTTP.send
HttpExists = IIf(oXHTTP.Status = 200, True, False)
Exit Function
haveError:
Debug.Print Err.Description
HttpExists = False
End Function
Here's a simpler approach.
Sub whatAmI()
Dim s As String
Dim FSO As New FileSystemObject
s = "C:\FilePath\FolderName"
' s = "C:\FilePath\FolderName\FileName"
' s = "www.someURL.com"
If FSO.FolderExists(s) Then
Debug.Print "Dir"
ElseIf FSO.FileExists(s) Then
Debug.Print "File"
Else
' You can use HTTP library to check if existing URL
Debug.Print "Possible URL"
End If
End Sub
This requires selecting the Microsoft Scripting Runtime in the VBA Editor under Tools -> References.
You can use the previous code that uses the HTTP library to check if this is a valid URL rather then just random text.

read folders and any document properties from excel?

I am wanting to try something and I'm fairly sure it's possible, but not really sure!!
In MS Excel (2003) can I write a VBA script which will open a location (eg: s://public/marketing/documents/) and list all the documents located within there (filename)?
The ultimate goal would be to have the document name, date last modified, date created and modified by name.
Is this possible? I'd like to return any found values in rows on a sheet. eg: type: FOLDER, type: Word Doc etc.
Thanks for any info!
Done that recently. Use the DSOFile object. In Excel-VBA you first need to create a reference to Dsofile.dll ("DSO OLE Document Properties Reader 2.1" or similar). Also check you have a reference to the Office library
First you may want to select the file path which you want to examine
Sub MainGetProps()
Dim MyPath As String
MyPath = GetDirectoryDialog()
If MyPath = "" Then Exit Sub
GetFileProps MyPath, "*.*"
End Sub
Let's have a nice Path selection window
Function GetDirectoryDialog() As String
Dim MyFD As FileDialog
Set MyFD = Application.FileDialog(msoFileDialogFolderPicker)
With MyFD
.AllowMultiSelect = False
.Show
If .SelectedItems.Count <> 0 Then
GetDirectoryDialog = .SelectedItems(1)
End If
End With
End Function
Now let's use the DSO object to read out info ... I reduced the code to the bare necessary
Private Sub GetFileProps(MyPath As String, Arg As String)
Dim Idx As Integer, Jdx As Integer, MyFSO As FileSearch, MyRange As Range, MyRow As Integer
Dim DSOProp As DSOFile.OleDocumentProperties
Set DSOProp = New DSOFile.OleDocumentProperties
Set MyRange = ActiveSheet.[A2] ' your output is nailed here and overwrites anything
Set MyFSO = Application.FileSearch
With MyFSO
.NewSearch
.LookIn = MyPath
.SearchSubFolders = True ' or false as you like
.Filename = Arg
.FileType = msoFileTypeAllFiles
If .Execute() > 0 Then
MsgBox .FoundFiles.Count & " file(s) found." ' to see what you will get
For Idx = 1 To .FoundFiles.Count
DSOProp.Open .FoundFiles(Idx) ' examine the DSOProp element in debugger to find all summary property names; not all may be filled though
Debug.Print .FoundFiles(Idx)
Debug.Print "Title: "; DSOProp.SummaryProperties.Title
Debug.Print "Subject: "; DSOProp.SummaryProperties.Subject
' etc. etc. write it into MyRange(Idx,...) whatever
' now hunt down the custom properties
For Jdx = 0 To DSOProp.CustomProperties.Count - 1
Debug.Print "Custom #"; Jdx; " ";
Debug.Print " Name="; DSOProp.CustomProperties(Jdx).Name;
If DSOProp.CustomProperties(Jdx).Type <> dsoPropertyTypeUnknown Then
Debug.Print " Value="; DSOProp.CustomProperties(Jdx).Value
Else
Debug.Print " Type=unknowwn; don't know how to print";
End If
MyRow = MyRow + 1
Next Jdx
DSOProp.Close
Next Idx
Else
MsgBox "There were no files found."
End If
End With
End Sub
and that should be it
good luck MikeD

Resources