File Name Extract From String works every other time - excel

I need help understanding why file name extraction from string only works every other time.
I've tried both right and mid. I've used before in other code with no problem. I'm useing msgbox for debugging.
Final outcome should be adding multiple file names to the bottom of a table
Private Sub ButtonAdd_Click()
Dim fd As FileDialog
Dim fName As String ' full path file name
Dim nextRow As Long
Dim filename As String ' extracted file name only
Set fd = Application.FileDialog(msoFileDialogFilePicker)
fd.Title = "Please select file to add"
fd.InitialFileName = ThisWorkbook.FullName
fd.AllowMultiSelect = True
fchosen = fd.Show
If fchosen = -1 Then
For i = 1 To fd.SelectedItems.Count
fName = fd.SelectedItems(i)
'filename = Right(fName, Len(fName) - InStrRev(filename, "\"))
filename = Mid(fName, InStrRev(filename, "\") + 1)
MsgBox (filename)
nextRow = Range("a" & Rows.Count).End(xlUp).row + 1
'Range("a" & nextRow) = filename
Next i
End If
End Sub

As Josh writes in the comments, you are using the wrong variable filename instead of fname. Another example of how nesting commands makes it difficult to find an error and how naming of variables matters.
Split the lines into two pieces and rename fname to something like fullFilename:
Dim fullFilename as string, filename as string, p as long
fullFilename = fd.SelectedItems(i)
p = InStrRev(fullFilename, "\")
if p > 0 then
filename = mid(fullFilename, p+1)
else
filename = fullFilename
End If
Now you can easily distinguish between the variable holding the full path and the one that has only the file name. And even if you mix something up, you can easily find the problem using the debugger

Take your pick
Get File Name From File Path
Option Explicit
Sub Sample()
MsgBox GetFilenameFromPath("C:\Temp\Myfile.Txt")
End Sub
Private Function GetFilenameFromPath(ByVal strPath As String) As String
If Right$(strPath, 1) <> "\" And Len(strPath) > 0 Then
GetFilenameFromPath = GetFilenameFromPath( _
Left$(strPath, Len(strPath) - 1)) + Right$(strPath, 1)
End If
End Function
Get File Name Without Extension
Option Explicit
Sub Sample()
Dim fName As String
fName = "C:\Temp\Myfile.Txt"
MsgBox GetFilenameFromPath(Left(fName, (InStrRev(fName, ".", -1, vbTextCompare) - 1)))
End Sub
Private Function GetFilenameFromPath(ByVal strPath As String) As String
If Right$(strPath, 1) <> "\" And Len(strPath) > 0 Then
GetFilenameFromPath = GetFilenameFromPath( _
Left$(strPath, Len(strPath) - 1)) + Right$(strPath, 1)
End If
End Function

Related

Save an embedded file to a location (export file from excel) with VBA

I have a little macro, that opens a Form where you can input details,
when you click on a button, you create a list with all the entries and save a selected pdf file thats embedded on another worksheet.
The Code works when you dont embed it as symbol. It basically creates a "screenshot" with the pdf. But i simply want to save the embedded object in a fixed path
`
Sub Schaltfläche6_Klicken()
Dim saveLocation As String
Dim sFolderPath As String
UserForm1.Show
sFolderPath = "C:\test\Excel"
saveLocation = "C:\test\Excel\Dummy.pdf"
If Dir(sFolderPath) <> "" Then
MkDir "C:\test\Excel"
End If
Worksheets("Dummy").ExportAsFixedFormat Type:=xlTypePDF, _
Filename:=saveLocation
End Sub
`
Hope you have a solution for the problem
i tried to find some solutions on the internet, but it didnt really helped. It looked a bit too complicated for what i really want
Please, use the next scenario. It cannot be a simple one, as I tried suggesting in my above comment:
Embed the pdf files but use 'Alt Text' to place there the pdf file name. It can be manually add by right clicking on the OLE object - Format Object... - Alt Text or in code, if you embed the files in this way. I can supply a code modification for such a case, if needed.
The workbook where from to be embedded pdf files extracted (WBPdf), must be closed.
Since, as stated above, WBPdf should be closed, the next code must be copied in a xlsm file and run it from there. Basically, it saves a copy of WBPdf with zip extension (in fact workbook type xlsx, xlsm, xlsa etc. are such archives containing many xml files and objects. The code firstly extracts the files from archive \xl\worksheets, processes them to extract a logical association between the bin files in \xl\embeddings and the pdf name extracted from worksheets xml files. Then, it binary open the found bin files, and process them to become correct pdf files. I placed a link to an answer where this process has very well explained some years before:
a. Create a Public variable on top of a standard module (in the declarations area):
Public ExpArr()
It will keep the correspondence between the bin file to pdf name to be saved as.
b. Copy the next code in a standard module:
Sub ExtractEmbeddedPDFs() 'it does NOT work if the workbook to be processed is Open!
Dim pdfFolder As String, embWB As String, zipName As String, oShell As Object, arrO, i As Long
pdfFolder = ThisWorkbook.Path & "\Extracted PDF"
embWB = ThisWorkbook.Path & "\Embedded pdf.xlsx"
zipName = left(embWB, InStrRev(embWB, ".")) & "zip"
If Dir(pdfFolder, vbDirectory) = "" Then 'if the folder where to save pdf files does not exist
MkDir pdfFolder 'it is created
End If
'Deleting any previously created files, if any:
On Error Resume Next
Kill zipName
Kill pdfFolder & "\*.*"
Kill pdfFolder & "\_rels\*.*"
RmDir pdfFolder & "\_rels\"
On Error GoTo 0
'Copy/rename the Excel file changing extension to zip:
On Error Resume Next
FileCopy embWB, zipName
If err.Number = 70 Then 'error in case of workbook being open:
err.Clear: On Error GoTo 0
MsgBox "Please, close the workbook where from the embedded pdf files should be extracted." & vbCrLf & _
"A zipped copy cannot be created...", vbInformation, "Need to close the workbook": Exit Sub
End If
On Error GoTo 0
Dim flsWsh As Object, fileNameInZip As Variant
Set oShell = CreateObject("Shell.Application")
Set flsWsh = oShell.NameSpace(oShell.NameSpace((zipName)).Items.Item(("xl\worksheets")))
For Each fileNameInZip In oShell.NameSpace(flsWsh).Items
oShell.NameSpace((pdfFolder)).CopyHere _
oShell.NameSpace(flsWsh).Items.Item(CStr(fileNameInZip))
Next
getOLEObjSheetsREL pdfFolder 'build the array which matches any .bin oleObject with the extracted pdf name
For i = 0 To UBound(ExpArr)
arrO = Split(ExpArr(i), "|") 'split the matching array elements by "|" to extract bin name in relation with pdf name
oShell.NameSpace((pdfFolder)).CopyHere oShell.NameSpace((zipName)).Items.Item("xl\embeddings\" & arrO(0))
ReadAndWriteExtractedBinFile pdfFolder & "\" & arrO(0), pdfFolder, CStr(arrO(1))
Next i
On Error Resume Next
Kill zipName
Kill pdfFolder & "\*.bin"
Kill pdfFolder & "\*.xml"
Kill pdfFolder & "\_rels\*.*"
RmDir pdfFolder & "\_rels\"
On Error GoTo 0
MsgBox "Ready..."
Shell "explorer.exe" & " " & pdfFolder, vbNormalFocus 'open the folder keeping extracted files
End Sub
'Eliminate specific characters from binary file to make it pdf compatible:
'see here a good process explanation:
'https://stackoverflow.com/questions/52778729/download-embedded-pdf-file
Sub ReadAndWriteExtractedBinFile(s As String, TmpPath, Optional pdfName As String = "")
Dim byteFile As Long, byt As Byte, fileName As String
Dim MyAr() As Byte, NewAr() As Byte, i As Long, j As Long, k As Long
byteFile = FreeFile: j = 1
Open s For Binary Access Read As byteFile 'Open the bin file
Do While Not EOF(byteFile) 'loop untill the last line (count the file bytes)
Get byteFile, , byt: j = j + 1
Loop
'create the (correct) pdf byte file, removing some bytes (characters) from the bin byte one:___
ReDim MyAr(1 To j - 1) 'initially reDim it to have the same dimension as byteFile
j = 1
If EOF(byteFile) Then Seek byteFile, 1 'set first byte position for the next iteration
Do While Not EOF(byteFile) 'place the content of bin byteFile in MyAr:
Get byteFile, , byt
MyAr(j) = byt: j = j + 1
Loop
Close byteFile
'build the correct byte array without bytes existing up to %PDF:
For i = LBound(MyAr) To UBound(MyAr)
If i = UBound(MyAr) - 4 Then Exit For 'eliminate the not necessary last 4 bytes
If val(MyAr(i)) = 37 And val(MyAr(i + 1)) = 80 And _
val(MyAr(i + 2)) = 68 And val(MyAr(i + 3)) = 70 Then 'when find %PDF
ReDim NewAr(1 To j - i + 1) 'reDim the array to eliminate everything before it
k = 1
For j = i To UBound(MyAr)
NewAr(k) = MyAr(j): k = k + 1
Next j
Exit For 'exits the loop (after finding %PDF bytes)
End If
Next i
byteFile = FreeFile
'Set the pdf to be saved name:
If pdfName = "" Then 'if no pdfName parameter, it builds a unique name:
fileName = TmpPath & "\" & Format(Now, "ddmmyyhhmmss") & ".pdf"
Else
fileName = TmpPath & "\" & pdfName 'this solution uses only the extracted (from OLEObject) name
End If
'Write the new (pdf) binary file:
If isArrLoaded(NewAr()) Then 'only for PDF (bin) embedded files:
Open fileName For Binary Lock Read Write As #byteFile
For i = LBound(NewAr) To UBound(NewAr)
Put #byteFile, , CByte(NewAr(i))
Next i
Close #byteFile
Else
'If by mistake a not appropriate bin file has been choosen:
Debug.Print "The object is not of pdf type..." 'theoretically, this line should never be reached
End If
End Sub
Private Sub getOLEObjSheetsREL(strPath As String)
Dim patt As String: patt = "oleObject\d{1,3}.bin"
Dim strFold As String, strFile As String, strText As String
Dim fso As Object, ts As Object, arrOLE, arrOLEC(1), arrTot, i As Long
strFold = strPath & "\_rels\" 'copied folder (from archive) keeping sheets keeping OLEObjects
ReDim arrTot(0)
strFile = Dir(strFold & "*.rels")
Do While strFile <> "" 'iterate between all existing files
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.getFile(strFold & strFile).OpenAsTextStream(1, -2)
strText = ts.ReadAll 'read their content
ts.Close
arrOLE = getOLEObj(strText, patt) 'extract an array linking OLEObject to pdf file name
If arrOLE(0) <> "" Then
arrOLEC(0) = left(strFile, Len(strFile) - 5): arrOLEC(1) = arrOLE
BubbleSort arrOLEC(1) 'sort the array
arrTot(i) = arrOLEC: i = i + 1: ReDim Preserve arrTot(i)
End If
strFile = Dir()
Loop
ReDim Preserve arrTot(i - 1)
getOLEObjects arrTot, strPath 'returning an array linking the bin object to pdf to be saved file name
End Sub
Private Sub BubbleSort(arr)
Dim i As Long, j As Long, temp
For i = LBound(arr) To UBound(arr) - 1
For j = i + 1 To UBound(arr)
If arr(i) > arr(j) Then
temp = arr(i): arr(i) = arr(j)
arr(j) = temp
End If
Next j
Next i
End Sub
Private Sub getOLEObjects(arrOLE As Variant, strPath As String)
Dim strFile As String, strText As String
Dim fso As Object, ts As Object, j As Long
Dim arr, frstTxt As String, El, i As Long, strName As String, PrID As String
Dim k As Long: ReDim ExpArr(100)
Const strObj As String = "oleObject"
For j = 0 To UBound(arrOLE)
strFile = strPath & "\" & arrOLE(j)(0)
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.getFile(strFile).OpenAsTextStream(1, -2)
strText = ts.ReadAll
ts.Close
arr = extractBetweenChars(strText, "<oleObject progId=", "<\/mc:Fallback>")
For Each El In arr
strName = "": PrID = ""
strName = extractBetweenChars(CStr(El), "altText=""", """ r:id")(0)
PrID = extractBetweenChars(CStr(El), """", """")(0)
If PrID = "Acrobat Document" Or PrID = "Packager Shell Object" Then i = i + 1
If strName <> "" Then
If InStr(strName, ".pdf") > 0 Then
ExpArr(k) = strObj & i & ".bin" & "|" & strName: k = k + 1
End If
End If
Next
Next j
'keep only the elements keeping values:
If k > 0 Then
ReDim Preserve ExpArr(k - 1)
Else
Erase ExpArr
End If
End Sub
The workbook keeping embedded pdf files, can also contain embedded csv, xls, txt, jpg files. The code is able to distinguish between them and use for extraction only the appropriate bin files.
Please, send some feedback after testing it.

How to grab a new line in Word by Excel VBA code

I have code that takes the contents of a Word file into a variable, so I parse the contents and handle it by VBA code.
My question is: I'm currently scanning letters by a loop, but I don't know how to find a line break.
Any help is appreciated
Thank you
This is the code I'm currently using
Sub open_word_find_text()
Dim book1 As Word.Application
Dim sheet1 As Word.Document
Set book1 = CreateObject("word.application")
book1.Visible = True
GetFilePath = Application.GetOpenFilename 'Select a file'
Filename = Mid(GetFilePath, InStrRev(GetFilePath, "\") + 1, 999):
FilePath = Mid(GetFilePath, 1, InStrRev(GetFilePath, Filename) - 2)
find_text = InputBox("Type the text you are looking for:")
file = Dir(FilePath & "\")
While (file <> "") 'loop over all the files in the folder
If InStr(file, ".docx") > 0 Then
Filename = Mid(file, InStrRev(file, "\") + 1, 999):
Set sheet1 = book1.Documents.Open(FilePath & "\" & file)
ff = sheet1.Content 'Save the contents of the file in a variable
count_result = 0
For i = 1 To Len(ff)
ff2 = Mid(ff, i, Len(find_text))
If ff2 = find_text Then
count_result = count_result + 1
MsgBox "Number result: " & count_result & vbNewLine & Mid(ff, i - 150, i + 200), vbOKCancel + vbMsgBoxRight + vbAbortRetryIgnore, Filename
End If
DoEvents
Next
b:
End If
sheet1.Close
file = Dir
DoEvents
Wend
book1.Quit
MsgBox " end!"
End Sub
I don't understand what you are trying to do, but the following will store the contents of the document in a string array by splitting on the vbCr-constant representing the line break:
Dim sLinesArr() As String
sLinesArr = Split(sheet1.Content.Text, vbCr)
The array can then be used in a loop:
Dim vLine As Variant
For Each vLine In sLinesArr
MsgBox vLine
Next

Loop Through Folders & Subfolders for files with Wildcard Search

Good Morning,
I have some code that is doing the basis of what I want, BUT I just need to take it one step further for it to work perfectly. Currently I'm pulling every file in the folders and subfolders which is proper, but now I need to select only files that have a particular version label: "v5.3.xlsx" so I can open them, make adjustments and close. I've tried every version of a wild card search I can think of but I'm not terribly well versed in VBA so I was hoping someone could kick this over the finish line for me. Any help is very much appreciated!
Sub loopAllSubFolderSelectStartDirectory()
'Another Macro must call LoopAllSubFolders Macro to start to procedure
Call LoopAllSubFolders("C:\Users\smith\Desktop\Excel Test File\Client\")
End Sub
'List all files in sub folders
Sub LoopAllSubFolders(ByVal folderPath As String)
Dim fileName As String
Dim myFile As String
Dim fullFilePath As String
Dim numFolders As Long
Dim folders() As String
Dim i As Long
Application.ScreenUpdating = False
If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
fileName = Dir(folderPath & ".", vbDirectory)
While Len(fileName) <> 0
If Left(fileName, 1) <> "." Then
fullFilePath = folderPath & fileName
If (GetAttr(fullFilePath) And vbDirectory) = vbDirectory Then
ReDim Preserve folders(0 To numFolders) As String
folders(numFolders) = fullFilePath
numFolders = numFolders + 1
Else
'Insert the actions to be performed on each file
'This example will print the full file path to the immediate window
Debug.Print fullFilePath
End If
End If
fileName = Dir()
Wend
For i = 0 To numFolders - 1
LoopAllSubFolders folders(i)
Next i
Application.ScreenUpdating = True
End Sub

Excel VBA check and display number of file in folder

I have the following module to check the number of files contained in a folder and display a messagebox with the with the number of files:
Sub CheckFiles(strDir As String, strType As String)
Dim file As Variant, i As Integer
strDir = ThisWorkbook.Path & "\Source\"
If Right(strDir, 1) <> "\" Then strDir = strDir & "\"
file = Dir(strDir & strType)
While (file <> "")
i = i + 1
file = Dir
Wend
MsgBox i
End Sub
Files to look for (in separate module):
Call CheckFiles("", "File1*.xlsx")
Call CheckFiles("", "File2*.xlsx")
What I want to do is to only display messagebox if the number of files for File1 is not excaly 3 and the number of files for File2 is not excaly 2. This is what I'm having trouble doing? How can this be acheived?
Add the ChckNum as Third Parameter in the Subject and pass it in the Call Statement
Try:
Sub CheckFiles(strDir As String, strType As String, chknum As Integer)
Dim file As Variant, i As Integer
strDir = ThisWorkbook.path & "\Source\"
If Right(strDir, 1) <> "\" Then strDir = strDir & "\"
file = Dir(strDir & strType)
While (file <> "")
i = i + 1
file = Dir
Wend
If i <> chknum Then MsgBox i
End Sub
And
Call CheckFiles("", "File1*.xlsx", 3)
Call CheckFiles("", "File2*.xlsx", 2)

Excel vba: Import multiple text files, and move files after import?

I really hope someone can help with this. At the moment I am using vba to import each line of text from a text file into a new column on one row. And each time I run the function a new row of data is created below the previous.
Results:
Row 1 (Showing Data from TextFile 1)
Column A Column B Column C
Data Data Data
Row 2 (Showing Data from TextFile 2)
Column A Column B Column C
Data Data Data
So this all works fine and after I have imported the text from the file, the file is moved from my directory 'unactioned' to a directory called 'actioned'.
So at the moment my code is not quite there yet, I am currently having to define the text file name so that I can import the data from the text file into my spreadsheet and again i am defining the text file name i want to move, this code will only currently work for 1 text file. However what i want to be able to do is if there are several text files in my folder 'unactioned', then i want to import each of these text files into a new row, and move all the text files we have just imported the data from to my folder 'actioned' at the same time
Here is my code:
Sub ImportFile()
Dim rowCount As Long
rowCount = ActiveSheet.UsedRange.Rows.Count + 1
If Cells(1, 1).Value = "" Then rowCount = 1
Close #1
Open "Y:\Incident Logs\Unactioned\INSC89JH.txt" For Input As #1
A = 1
Do While Not EOF(1)
Line Input #1, TextLine
Cells(rowCount, A) = TextLine
A = A + 1
Loop
Close #1
Dim d As String, ext, x
Dim srcPath As String, destPath As String, srcFile As String
srcPath = "Y:\Incident Logs\Unactioned\"
destPath = "Y:\Incident Logs\Actioned\"
ext = Array("*.txt", "*.xls")
For Each x In ext
d = Dir(srcPath & x)
Do While d <> ""
srcFile = srcPath & d
FileCopy srcFile, destPath & d
Kill srcFile
d = Dir
Loop
Next
End Sub
please can someone show me how i would amend this code to do what i need it to do? Thanks in advance
I would suggest breaking your code into multiple functions.
You can change the ImportFile method to not kill ALL files, but just the file it operates on, and then have it take a specific file to operate on one at a time. E.g.:
Sub ImportFile(directory As String, filename As String)
Dim rowCount As Long
rowCount = ActiveSheet.UsedRange.Rows.Count + 1
If Cells(1, 1).Value = "" Then rowCount = 1
Close #1
Open directory & filename For Input As #1
A = 1
Do While Not EOF(1)
Line Input #1, TextLine
Cells(rowCount, A) = TextLine
A = A + 1
Loop
Close #1
'Move the file and delete it
Dim srcPath As String, destPath As String
srcPath = directory & filename
destPath = "C:\Incident Logs\Actioned\" & filename
FileCopy srcPath, destPath
Kill srcPath
End Sub
Then, here is another stackoverflow post on how to iterate files in a folder
So with a little adaptation you could have something like:
Sub ImportAllFiles()
ImportFilesWithExtension "*.txt"
ImportFilesWithExtension "*.xls*"
End Sub
Sub ImportFilesWithExtension(extension As String)
Dim StrFile As String, myDir As String
myDir = "C:\Incident Logs\Unactioned\"
StrFile = Dir(myDir & extension)
Do While Len(StrFile) > 0
ImportFile myDir, StrFile
StrFile = Dir
Loop
End Sub
I'd also break it down into functions:
Sub ImportFile()
Dim rLastCell As Range
Dim vFolder As Variant
Dim vFile As Variant
Dim colFiles As Collection
With ThisWorkbook.Worksheets("Sheet1") 'Note - update sheet name.
'First find the last cell on the named sheet.
Set rLastCell = .Cells.Find( _
What:="*", _
LookIn:=xlValues, _
SearchDirection:=xlPrevious)
If rLastCell Is Nothing Then
'Set LastCell to A2.
Set rLastCell = .Cells(2, 1)
Else
'Set LastCell to column A, last row + 1
Set rLastCell = .Range(rLastCell.Row + 1, 1)
End If
vFolder = GetFolder()
Set colFiles = New Collection
EnumerateFiles vFolder, "\*.txt", colFiles
For Each vFile In colFiles
'Do stuff with the file.
'Close the file and move it.
MoveFile CStr(vFile), "S:\Bartrup-CookD\Text 1\" & Mid(vFile, InStrRev(vFile, "\") + 1, Len(vFile)) 'Note - update folder name.
Next vFile
End With
End Sub
This will place all files into a collection:
Sub EnumerateFiles(ByVal sDirectory As String, _
ByVal sFileSpec As String, _
ByRef cCollection As Collection)
Dim sTemp As String
sTemp = Dir$(sDirectory & sFileSpec)
Do While Len(sTemp) > 0
cCollection.Add sDirectory & "\" & sTemp
sTemp = Dir$
Loop
End Sub
This will ask you to select a folder:
' To Use : vFolder = GetFolder()
' : vFolder = GetFolder("S:\Bartrup-CookD\Customer Services Phone Reports")
Function GetFolder(Optional startFolder As Variant = -1) As Variant
Dim fldr As FileDialog
Dim vItem As Variant
Set fldr = Application.FileDialog(msoFileDialogFolderPicker)
With fldr
.Title = "Select a Folder"
.AllowMultiSelect = False
If startFolder = -1 Then
.InitialFileName = Application.DefaultFilePath
Else
If Right(startFolder, 1) <> "\" Then
.InitialFileName = startFolder & "\"
Else
.InitialFileName = startFolder
End If
End If
If .Show <> -1 Then GoTo NextCode
vItem = .SelectedItems(1)
End With
NextCode:
GetFolder = vItem
Set fldr = Nothing
End Function
This will move a file from folder A to folder B:
'----------------------------------------------------------------------
' MoveFile
'
' Moves the file from FromFile to ToFile.
' Returns True if it was successful.
'----------------------------------------------------------------------
Public Function MoveFile(FromFile As String, ToFile As String) As Boolean
Dim objFSO As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
objFSO.MoveFile FromFile, ToFile
MoveFile = (Err.Number = 0)
Err.Clear
End Function

Resources