Excel VBA user defined function to find images in folder (match excel names to folder names of images) - vba

Currently i am using a function to match image names from excel sheet to image folder, but i want one more thing... that if i save image and forget to add its name in excel then it should show me that i forget to add name.
for example if i save 3 images in image folder
16095_1.jpg,16095_2.jpg,16095_3.jpg
and i add image names in excel sheet as
16095_1.jpg,16095_2.jpg
then it should warn me that i forget one image name in excel cell.
my image name format is - 16095_1.jpg,16095_2.jpg
function i am using is...
Function findimage(Path As String, ImageList As String)
Dim results
Dim x As Long
Dim dc 'double comma
results = Split(ImageList, ",")
If Not Right(Path, 1) = "\" Then Path = Path & "\"
For x = 0 To UBound(results)
results(x) = Len(Dir(Path & results(x))) > 0
Next
dc = InStr(ImageList, ",,")
If dc = 0 Then
findimage = Join(results, ",")
Else
findimage = ("Double_comma")
End If
End Function

This function takes a folder path and a variable number of patterns (See MSDN - Parameter Arrays (Visual Basic)). Using the MSDN - Dir Function to iterates over the file names in the folder path and compares them against the patterns with the MSDN - Like Operator (Visual Basic) to count the number of files that match the patterns.
Usage:
getFileCount("C:\Users\Owner\Pictures",".gif",".png")
getFileCount("C:\Users\Owner\Pictures","*.gif"
getFileCount("C:\Users\Owner\Pictures","apple_.gif","banana_.gif", "orange_##.*")
getFileCount("C:\Users\Owner\Pictures","#####_#.gif")
Function getFileCount(DirPath As String, ParamArray Patterns() As Variant) As Integer
Dim MyFile As String
Dim count As Integer, x As Long
If Not Right(DirPath, 1) = "\" Then DirPath = DirPath & "\"
MyFile = Dir(DirPath, vbDirectory)
Do While MyFile <> ""
For x = 0 To UBound(Patterns)
If MyFile Like Patterns(x) Then
count = count + 1
Exit For
End If
Next
MyFile = Dir()
Loop
getFileCount = count
End Function

Related

FileExist returns false

I have a folder with 700+ .jpgs. I also have a Textbox with one filename per line.
I want to check which file does not exist in the folder, but should be there.
This is my code:
Dim Counter As Integer = 0
For Each Line As String In tbFileNames.Lines
Counter = Counter + 1
If (IO.File.Exists(tbFolder.Text & "\" & tbFileNames.Lines(Counter - 1).ToString & ".jpg")) = False Then
tbNotExistingFiles.Text = tbNotExistingFiles.Text & vbNewLine & (tbFileNames.Lines(Counter - 1).ToString)
Else
End If
Next
Problem: I get more than 300 "missing" files, but there should be only 7. When I search for the output filenames, they are in the folder, so the FileExists functions returns false, but it shouldn't.
Where is the problem? Is it the amount of files?
According to this line:
If (IO.File.Exists(tbFolder.Text & "\" & tbFileNames.Lines(Counter - 1).ToString & ".jpg")) = False
Which can be interpreted as:
The tbFolder TextBox contains the directory's path where the images are located.
The tbFileNames TextBox contains the main and complete file names. One file name per line.
Appending the extension & ".jpg" means that the file names in the tbFileNames TextBox are without extensions. And,
You need to get a list of the missing files in that directory and show the result in the tbNotExistingFiles TextBox.
If my interpretation is correct, then you can achieve that using the extension methods like:
Imports System.IO
'...
Dim missingFiles = tbFileNames.Lines.
Select(Function(x) $"{x.ToLower}.jpg").
Except(Directory.GetFiles(tbFolder.Text).
Select(Function(x) Path.GetFileName(x.ToLower)))
tbNotExistingFiles.Text = String.Join(ControlChars.NewLine, missingFiles)
Or by a LINQ query:
Dim missingFiles = From x In tbFileNames.Lines
Where (
Aggregate y In Directory.EnumerateFiles(tbFolder.Text)
Where Path.GetFileName(y).ToLower.Equals($"{x.ToLower}.jpg")
Into Count()
) = 0
Select x
'Alternative to tbNotExistingFiles.Text = ...
tbNotExistingFiles.Lines = missingFiles.ToArray
Note that, there's no need nor use for the File.Exists(..) function in the preceding snippets. Just in case you prefer your approach using For..Loop and File.Exists(..) function, then you can do:
Dim missingFiles As New List(Of String)
For Each line In tbFileNames.Lines
If Not File.Exists(Path.Combine(tbFolder.Text, $"{line}.jpg")) Then
missingFiles.Add(line)
End If
Next
tbNotExistingFiles.Lines = missingFiles.ToArray

How can I format value between 2nd and 4th underscore in the file name?

I have VBA code to capture filenames to a table in an MS Access Database.
The values look like this:
FileName
----------------------------------------------------
WC1603992365_Michael_Cert_03-19-2019_858680723.csv
WC1603992365_John_Non-Cert_03-19-2019_858680722.csv
WC1703611403_Paul_Cert_03-27-2019_858679288.csv
Each filename has 4 _ underscores and the length of the filename varies.
I want to capture the value between the 2nd and the 3rd underscore, e.g.:
Cert
Non-Cert
Cert
I have another file downloading program, and it has "renaming" feature with a regular expression. And I set up the following:
Source file Name: (.*)\_(.*)\_(.*)\_(.*)\_\-(.*)\.(.*)
New File Name: \5.\6
In this example, I move the 5th section of the file name to the front, and add the file extension.
For example, WC1603992365_Michael_Cert_03-19-2019_858680723.csv would be saved as 858680723.csv in the folder.
Is there a way that I can use RegEx to capture 3rd section of the file name, and save the value in a field?
I tried VBA code, and searched SQL examples, but I did not find any.
Because the file name length is not fixed, I cannot use LEFT or RIGHT...
Thank you in advance.
One possible solution is to use the VBA Split function to split the string into an array of strings using the underscore as a delimiter, and then return the item at index 2 in this array.
For example, you could define a VBA function such as the following, residing in a public module:
Function StringElement(strStr, intIdx As Integer) As String
Dim strArr() As String
strArr = Split(Nz(strStr, ""), "_")
If intIdx <= UBound(strArr) Then StringElement = strArr(intIdx)
End Function
Here, I've defined the argument strStr as a Variant so that you may pass it Null values without error.
If supplied with a Null value or if the supplied index exceeds the bounds of the array returned by splitting the string using an underscore, the function will return an empty string.
You can then call the above function from a SQL statement:
select StringElement(t.Filename, 2) from Filenames t
Here I have assumed that your table is called Filenames - change this to suit.
This is the working code that I completed. Thank you for sharing your answers.
Public Function getSourceFiles()
Dim rs As Recordset
Dim strFile As String
Dim strPath As String
Dim newFileName As String
Dim FirstFileName As String
Dim newPathFileName As String
Dim RecSeq1 As Integer
Dim RecSeq2 As Integer
Dim FileName2 As String
Dim WrdArrat() As String
RecSeq1 = 0
Set rs = CurrentDb.OpenRecordset("tcsvFileNames", dbOpenDynaset) 'open a recordset
strPath = "c:\in\RegEx\"
strFile = Dir(strPath, vbNormal)
Do 'Loop through the balance of files
RecSeq1 = RecSeq1 + 1
If strFile = "" Then 'If no file, exit function
GoTo ExitHere
End If
FirstFileName = strPath & strFile
newFileName = strFile
newPathFileName = strPath & newFileName
FileName2 = strFile
Dim SubStrings() As String
SubStrings = Split(FileName2, "_")
Debug.Print SubStrings(2)
rs.AddNew
rs!FileName = strFile
rs!FileName68 = newFileName 'assign new files name max 68 characters
rs!Decision = SubStrings(2) 'extract the value after the 3rd underscore, and add it to Decision Field
rs.Update
Name FirstFileName As newPathFileName
strFile = Dir()
Loop
ExitHere:
Set rs = Nothing
MsgBox ("Directory list is complete.")
End Function

Select a File based on Expected Name

I am trying to open a file based on a name that is only partially complete in VBA. Here is an example:
I want to pull a file with the name: "MMM B2 06222018"
The file will always be located in "C:\Myfile\"
However, these two files are saved in "C:\Myfile\":
"MMM B2 06222018 Updated" - Updated at 10:00
"MMM B2 06222018" - Updated at 9:00
What I want is for the macro to pull the most recently updated file that has the name "MMM B2 06222018" within the file name, but that name may or maynot be the complete name of the file. In this case, the "MMM B2 06222018 Updated" is the file I want pulled because it includes all of the "MMM B2 06222018" name, AND it is the most recently saved file.
FileDateTime(file_path) 'I was thinking of using this to compare the file save times.
'But I can only use this if I have a file name.
What is a good way of analyzing the partial file name?
Thanks!
Function GET_LATEST_FILE(strFolder As String, strWildCardStart As String) As String
Dim d As Date, fld as Object, f As Object
d = DateSerial(1900, 1, 1)
With CreateObject("scripting.filesystemobject")
Set fld = .getfolder(strFolder)
For Each f In fld.Files
If f.Name Like strWildCardStart & "*" Then
If f.datelastmodified > d Then
d = f.datelastmodified
GET_LATEST_FILE = f.Name
End If
End If
Next f
End With
End Function
Use this like so
GET_LATEST_FILE("C:\Workspace\Dummy Data","Sample")
A less powerful way to do this, without FileSystemObject, is to use Dir:
Function GetLatestFile(ByVal FolderName As String, ByVal FileName As String) As String
GetLatestFile = "" 'Default to blank
'Declare variables
Dim sTestFile As String, dTestFile As Date, dLatest As Date
If Right(FolderName, 1) <> "\" Then FolderName = FolderName & "\" 'Add final slash if missing
If Len(FileName) = Len(Replace(FileName, "*", "")) Then FileName = FileName & "*" 'Add wildcard if missing
dLatest = DateSerial(1900, 0, 0) 'Default to 0
sTestFile = Dir(FolderName & sTestFile) 'First file that matches the filename, if any
While Len(sTestFile) > 1 'Loop through all files until we run out
dTestFile = FileDateTime(FolderName & sTestFile) 'Get Created/Modifed Date
If dTestFile > dLatest Then 'If new file is newer
GetLatestFile = sTestFile
dLatest = dTestFile 'Store date
End If
sTestFile = Dir() 'Calling Dir without any arguments gets the next file to match the last filename given
Wend
End Function

File name without extension name VBA

I need to get file name without extension name by VBA. I know ActiveWorkbook.Name property , but if user haves Windows property Hide extensions for known file types turn off, the result of my code will be [Name.Extension]. How can I return only name of Workbook independent of windows property?
I try even ActiveWorkbook.Application.Caption but I can't customize this property.
The answers given here already may work in limited situations, but are certainly not the best way to go about it. Don't reinvent the wheel. The File System Object in the Microsoft Scripting Runtime library already has a method to do exactly this. It's called GetBaseName. It handles periods in the file name as is.
Public Sub Test()
Dim fso As New Scripting.FileSystemObject
Debug.Print fso.GetBaseName(ActiveWorkbook.Name)
End Sub
Public Sub Test2()
Dim fso As New Scripting.FileSystemObject
Debug.Print fso.GetBaseName("MyFile.something.txt")
End Sub
Instructions for adding a reference to the Scripting Library
Simple but works well for me
FileName = ActiveWorkbook.Name
If InStr(FileName, ".") > 0 Then
FileName = Left(FileName, InStr(FileName, ".") - 1)
End If
Using the Split function seems more elegant than InStr and Left, in my opinion.
Private Sub CommandButton2_Click()
Dim ThisFileName As String
Dim BaseFileName As String
Dim FileNameArray() As String
ThisFileName = ThisWorkbook.Name
FileNameArray = Split(ThisFileName, ".")
BaseFileName = FileNameArray(0)
MsgBox "Base file name is " & BaseFileName
End Sub
This gets the file type as from the last character (so avoids the problem with dots in file names)
Function getFileType(fn As String) As String
''get last instance of "." (full stop) in a filename then returns the part of the filename starting at that dot to the end
Dim strIndex As Integer
Dim x As Integer
Dim myChar As String
strIndex = Len(fn)
For x = 1 To Len(fn)
myChar = Mid(fn, strIndex, 1)
If myChar = "." Then
Exit For
End If
strIndex = strIndex - 1
Next x
getFileType = UCase(Mid(fn, strIndex, Len(fn) - x + 1))
End Function
You could always use Replace() since you're performing this on the workbook's Name, which will almost certainly end with .xlsm by virtue of using VBA.
Using ActiveWorkbook per your example:
Replace(Application.ActiveWorkbook.Name, ".xlsm", "")
Using ThisWorkbook:
Replace(Application.ThisWorkbook.Name, ".xlsm", "")
This thread has been very helpful to me lately. Just to extend on the answer by #RubberDuck, the File System Object in the Microsoft Scripting Runtime library is already there to achieve this. Also if you define it as an Object as below, it will save you the hassle of having to enable 'Microsoft Scripting Runtime' in VBA Tools > References:
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Debug.Print fso.GetBaseName(ActiveWorkbook.Name)
In this way it will return name of the ActiveWorkbook without extension.
There is another way by using INSTRREV function as below:
Dim fname As String
fname = Left(ActiveWorkbook.Name, InStrRev(ActiveWorkbook.Name, ".") - 1)
MsgBox fname
Both will return the same result. Also in both of the methods above, they will retain any full-stops in the file name and only get rid of the last full-stop and the file extension.
To be verbose it the removal of extension is demonstrated for
workbooks.. which now have a variety of extensions .
. a new unsaved Book1 has no ext
. works the same for files
Function WorkbookIsOpen(FWNa$, Optional AnyExt As Boolean = False) As Boolean
Dim wWB As Workbook, WBNa$, PD%
FWNa = Trim(FWNa)
If FWNa <> "" Then
For Each wWB In Workbooks
WBNa = wWB.Name
If AnyExt Then
PD = InStr(WBNa, ".")
If PD > 0 Then WBNa = Left(WBNa, PD - 1)
PD = InStr(FWNa, ".")
If PD > 0 Then FWNa = Left(FWNa, PD - 1)
'
' the alternative of using split.. see commented out below
' looks neater but takes a bit longer then the pair of instr and left
' VBA does about 800,000 of these small splits/sec
' and about 20,000,000 Instr Lefts per sec
' of course if not checking for other extensions they do not matter
' and to any reasonable program
' THIS DISCUSSIONOF TIME TAKEN DOES NOT MATTER
' IN doing about doing 2000 of this routine per sec
' WBNa = Split(WBNa, ".")(0)
'FWNa = Split(FWNa, ".")(0)
End If
If WBNa = FWNa Then
WorkbookIsOpen = True
Exit Function
End If
Next wWB
End If
End Function
I use a macro from my personal.xlsb and run it on both xlsm and xlsx files so a variation on David Metcalfe's answer that I use is
Dim Wrkbook As String
Wrkbook = Replace(Application.ActiveWorkbook.Name, ".xlsx", ".pdf")
Wrkbook = Replace(Application.ActiveWorkbook.Name, ".xlsm", ".pdf")
Here is a solution if you do not want to use FSO.
There were some similar answers before, but here some checks are done to handle multiple dots in name and name without extension.
Function getFileNameWithoutExtension(FullFileName As String)
Dim a() As String
Dim ext_len As Integer, name_len As Integer
If InStr(FullFileName, ".") = 0 Then
getFileNameWithoutExtension = FullFileName
Exit Function
End If
a = Split(ActiveWorkbook.Name, ".")
ext_len = Len(a(UBound(a))) 'extension length (last element of array)
name_len = Len(FullFileName) - ext_len - 1 'length of name without extension and a dot before it
getFileNameWithoutExtension = Left(FullFileName, name_len)
End Function
Sub test1() 'testing the function
MsgBox (getFileNameWithoutExtension("test.xls.xlsx")) ' -> test.xls
MsgBox (getFileNameWithoutExtension("test")) ' -> test
MsgBox (getFileNameWithoutExtension("test.xlsx")) ' -> test
End Sub
strTestString = Left(ThisWorkbook.Name, (InStrRev(ThisWorkbook.Name, ".", -1, vbTextCompare) - 1))
full credit: http://mariaevert.dk/vba/?p=162

Reading text files with specific prefix

I have a folder with lots of text files each containing (but in random order) :
A = ...
B = ...
C = ...
Now I would like to import these text files into an excel-spreadsheet,
where each of the prefixes is organized in the colums, and the files are listed as rows
Example: 2 files
File 1:
A = 1
B = 2
C = 3
File 2:
A = 4
B = 5
C = 6
I would the excel to look like :
NR / A / B / C
1 / 1 /2 /3
2 / 4/ 5 /6
I am still learning VB, and this is just a bit over the top for me.
I have found a macro like this:
Sub Read_Text_Files()
Dim sPath As String, sLine As String
Dim oPath As Object, oFile As Object, oFSO As Object
Dim r As Long
'Files location
sPath = "C:\Test\"
r = 1
Set oFSO = CreateObject( _
"Scripting.FileSystemObject")
Set oPath = oFSO.GetFolder(sPath)
Application.ScreenUpdating = False
For Each oFile In oPath.Files
If LCase(Right(oFile.Name, 4)) = ".txt" Then
Open oFile For Input As #1 ' Open file for input.
Do While Not EOF(1) ' Loop until end of file.
Input #1, sLine ' Read data
If Left(sLine, 1) = "A=" Then 'Now i need to write this to the first column of that row
If Left(sLine, 1) = "B=" Then 'For the second column.
Range("A" & r).Formula = sLine ' Write data line
r = r + 1
Loop
Close #1 ' Close file.
End If
Next oFile
Application.ScreenUpdating = True
End Sub
Do you know how to open files in VBA for reading using syntax like Open and Line Input?
If not, read this: https://stackoverflow.com/a/11528932/2832561
I found this by googling for "VBA open file read"
Do you know how to work with and parse strings (and arrays) using functions like Mid, Left, Right, Split and Join?
If not, try reading this: http://www.exceltrick.com/formulas_macros/vba-split-function/
I found this by googling for "VBA String functions parse text"
Do you know how to work with Workbook and Worksheet objects and assign values to Range objects in Excel?
If not, try reading this: http://www.anthony-vba.kefra.com/vba/vbabasic2.htm
I found this by googling for "Workbook Worksheet Range VBA"
Once you have had a chance to try putting together a solution using these pieces, you can post specific questions on any issues you run into.