I have a list of text files (all different names.txt, but small files).
I want to rename them based on the first line of text in the file.
Some files have a few enters before the text. So the code returns with a (blank).txt.
Sub RenameTextFile()
Const SpecialCharacters As String = "\,/,:,*,?,<,>,|,""," ' Modify this as neccesary
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso, MyFile, FileName, TextLine
Dim char As Variant
Set fso = CreateObject("Scripting.FileSystemObject")
Set fol = fso.GetFolder("C:\Research syntheses - Meta analysis\Txt files ECS\out\")
For Each fil In fol.Files
FileName = fil
Set MyFile = fso.OpenTextFile(FileName, ForReading)
Do While MyFile.AtEndOfStream <> True
Do
Dim tmpLine As String
TextLine = MyFile.ReadLine
tmpLine = RemoveWhiteSpace(TextLine)
If Len(tmpLine) = 0 Then
TextLine = tmpLine
End If
Loop Until Len(TextLine) > 0
MyFile.Close
For Each char In Split(SpecialCharacters, ",")
TextLine = Replace(TextLine, char, "")
Next
fil.Name = TextLine & ".txt"
Exit Do
Loop
MyFile.Close
Next fil
End Sub
You could add another loop to your code like that
Sub RenameTextFile()
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso, MyFile, FileName, TextLine
Set fso = CreateObject("Scripting.FileSystemObject")
Set fol = fso.GetFolder("d:\tmp\")
For Each fil In fol.Files
FileName = fil
Set MyFile = fso.OpenTextFile(FileName, ForReading)
Do While MyFile.AtEndOfStream <> True
Do
Dim tmpLine As String
TextLine = MyFile.ReadLine
tmpLine = removeWhiteSpace(TextLine)
If Len(tmpLine) = 0 Then
TextLine = tmpLine
End If
Loop Until Len(TextLine) > 0
MyFile.Close
If isValidFilename(Trim(TextLine)) Then
On Error Resume Next
fil.Name = Trim(TextLine) & ".txt"
On Error Goto 0
Else
MsgBox "Renaming: " & fil.Name & " -to- " & Trim(TextLine) & " failed", vbCritical + vbOKOnly, "Invalid Filename"
End If
Exit Do
Loop
MyFile.Close
Next fil
End Sub
On the long run you need to think about kind of error handling as a file with the same name might already exist. And my extra loop will also fail in case one of the files only contains empty lines.
Update By the OP's comment I strongly guess that some of the files contain white spaces at the beginning and the OP is not aware of that. One can use the following function (taken from here) to remove these
'Add a reference to Microsoft VBScript Regular Expressions 5.5
Public Function RemoveWhiteSpace(ByVal target As String) As String
With New RegExp
.Pattern = "\s"
.MultiLine = True
.Global = True
RemoveWhiteSpace = .Replace(target, vbNullString)
End With
End Function
This will remove also white spaces from textline even if you want to keep them as the empty spaces in the line of your picture.
Update 2: In order to avoid invalid filename add the following function
Function isValidFilename(ByVal FileName As String) As Boolean
'PURPOSE: Determine If A Given Excel File Name Is Valid
'SOURCE: www.TheSpreadsheetGuru.com/the-code-vault
With New RegExp
.Pattern = "[\\/:\*\?""<>\|\[\]]"
ValidFileName = Not .Test(FileName)
End With
End Function
I modified the main procedure.
Related
I wrote a script where I add a signature from an htm file in the appData ... signature folder to a newly opened email.
My question is - how do i modify this VBA script to add that signature in a way so Outlook knows its a signature and the signature might be changed by a user via gui.
I assume it may have something to do with setting a "_MailAutoSig" bookmark, is that right?
Script looks like this and works so far:
Dim WithEvents m_objMail As Outlook.MailItem
Dim LODGIT_SUBJECT_IDENTIFIERS() As String
Private Sub Application_ItemLoad(ByVal Item As Object)
'MsgBox "Application_ItemLoad"
Select Case Item.Class
Case olMail
Set m_objMail = Item
End Select
End Sub
Private Sub m_objMail_Open(Cancel As Boolean)
'string array containing lodgit email subject identifiers (beginning string!!! of email subject)
LODGIT_SUBJECT_IDENTIFIERS = Split("Angebot von Bödele Alpenhotel,Angebot von,bestätigt Ihre Reservierung,Rechnung Nr.,Stornogutschrift für die Rechnung,Ausstehende Zahlung", ",")
Dim Application As Object
Dim oOutApp As Object, oOutMail As Object
Dim strbody As String, FixedHtmlBody As String
Dim Ret
Set Application = CreateObject("Outlook.Application")
'Change only Mysig.htm to the name of your signature
' C:\Users\nicole\AppData\Roaming\Microsoft\Signatures
Ret = Environ("appdata") & _
"\Microsoft\Signatures\AH Andrea kurz.htm"
If Ret = False Then Exit Sub
'~~> Use the function to fix image paths in the htm file
FixedHtmlBody = FixHtmlBody(Ret)
'CHECK FOR LODGIT IDENTIFIER
If myInStr(m_objMail.Subject, LODGIT_SUBJECT_IDENTIFIERS()) Then
Debug.Print "E-Mail as from Lodgit identified"
Dim str As String
Dim a As Object
str = Replace(m_objMail.Body, vbCrLf, "<br>")
str = Replace(str, vbNewLine, "<br>")
m_objMail.HTMLBody = "<html><body><span style='font-size:11.0pt;font-family:""Times New Roman"" '>" & str & "</span>" & FixedHtmlBody & "</body></html>"
End If
End Sub
'~~> Function to fix image paths in Signature .htm Files
Function FixHtmlBody(r As Variant) As String
Dim FullPath As String, filename As String
Dim FilenameWithoutExtn As String
Dim foldername As String
Dim MyData As String
'~~> Read the html file as text file in a string variable
Open r For Binary As #1
MyData = Space$(LOF(1))
Get #1, , MyData
Close #1
'~~> Get File Name from path
filename = GetFilenameFromPath(r)
'~~> Get File Name without extension
FilenameWithoutExtn = Left(filename, (InStrRev(filename, ".", -1, vbTextCompare) - 1))
'~~> Get the foldername where the images are stored
foldername = FilenameWithoutExtn & "-Dateien"
'~~> Full Path of Folder
FullPath = Left(r, InStrRev(r, "\")) & foldername
'~~> To cater for spaces in signature file name
'FullPath = Replace(FullPath, " ", "%20")
'~~> Replace incomplete path with full Path
FixHtmlBody = Replace(MyData, "AH%20Andrea%20kurz-Dateien", FullPath)
'FixHtmlBody = Replace(MyData, foldername, FullPath)
End Function
'~~> Gets File Name from path
Public 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 Function
'check if str contains on of the elements of a str array
Public Function myInStr(myString As String, a() As String) As Boolean
For Each elem In a
If InStr(1, myString, elem, vbTextCompare) <> 0 Then
myInStr = True
Exit Function
End If
Next
myInStr = False
End Function
Outlook looks for the "_MailAutoSig" bookmark. This needs to be done with Word Object Model, not by setting the HTMLBody property. Something along the lines:
wdStory = 6
wdMove = 0
Set objBkm = Nothing
Set objDoc = Inspector.WordEditor
Set objSel = objDoc.Application.Selection
'remember the cursor position
set cursorRange = objDoc.Range
cursorRange.Start = objSel.Start
cursorRange.End = objSel.End
If objDoc.Bookmarks.Exists("_MailAutoSig") Then
'replace old signature
Debug.Print "old signature found"
set objBkm = objDoc.Bookmarks("_MailAutoSig")
objBkm.Select
objDoc.Windows(1).Selection.Delete
ElseIf objDoc.Bookmarks.Exists("_MailOriginal") Then
' is there the original email? (_MailOriginal)
set objBkm = objDoc.Bookmarks("_MailOriginal")
objSel.Start = objBkm.Start-2 'give room for the line break before. It includes the line
objSel.End = objBkm.Start-2
Else
'insert at the end of the email
objSel.EndOf wdStory, wdMove
End If
'start bookmark
set bkmStart = objDoc.Bookmarks.Add("_tempStart", objSel.Range)
'end bookmark
set bkmEnd = objDoc.Bookmarks.Add("_tempEnd", objSel.Range)
bkmEnd.End = bkmEnd.End + 1
bkmEnd.Start = bkmEnd.Start + 1
objSel.Text = " "
set objBkm = objDoc.Bookmarks.Add("_MailAutoSig", bkmStart.Range)
objBkm.Range.insertFile "c:\Users\<user>\AppData\Roaming\Microsoft\Signatures\test.htm", , false, false, false
objBkm.Range.InsertParagraphBefore
objBkm.End = bkmEnd.Start - 1 'since we added 1 above for bkmEnd
objSel.Start = cursorRange.Start
objSel.End = cursorRange.End
bkmStart.Delete
bkmEnd.Delete
This is driving me mad: I have a sub and a function in a powerpoint vba.
The sub starts by allowing me to select a dir. The function, called from the sub, finds a file in the dir. I want it as a function outside of the sub, as I will need to use it multiple times.
The sub is still under development, so doesn't do much, but works. The function works too if I give it something to do - like open the found file (ie uncomment that line in my code below) - but I can't for the life of me get it to return the filePath to the sub. Please help!
The sub:
Sub ManagementSummaryMerge()
Dim folderPath As String
'select dir
Dim FldrPicker As FileDialog
Set pptApp = CreateObject("PowerPoint.Application")
pptApp.Visible = True
'Retrieve Target Folder Path From User
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
folderPath = .SelectedItems(1) & "\"
End With
'In Case of Cancel
NextCode:
folderPath = folderPath
If folderPath = "" Then GoTo EndOfSub
'set _Main <= string I want to look for
Dim v As String
v = "_Main"
Dim fullFilePathIWantToSet As String
'set value of fullFilePathIWantToSet from findFile function
fullFilePathIWantToSet = findFile(folderPath, v)
'when I test, this MsgBox appears, but blank
MsgBox fullFilePathIWantToSet
'If I can get this working properly, I want to be able to do something like this:
'objFSO.CopyFile fullFilePathIWantToSet, duplicateFilePath
'Presentations.Open (duplicateFilePath)
'numSlides = ActivePresentation.Slides.Count
'etc
EndOfSub:
'let the sub end
End Sub
The function:
Function findFile(ByRef folderPath As String, ByVal v As String) As String
Dim fileName As String
Dim fullFilePath As String
Dim duplicateFilePath As String
Dim numFolders As Long
Dim numSlides As Integer
Dim folders() As String
Dim i As Long
If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
ileName = Dir(folderPath & "*.*", vbDirectory)
While Len(fileName) <> 0
If Left(fileName, 1) <> "." Then
fullFilePath = folderPath & fileName
duplicateFilePath = folderPath & "duplicate " & fileName
If (GetAttr(fullFilePath) And vbDirectory) = vbDirectory Then
ReDim Preserve folders(0 To numFolders) As String
folders(numFolders) = fullFilePath
numFolders = numFolders + 1
Else
'if true, the it matches the string we are looking for
If InStr(10, fullFilePath, v) > 0 Then
'if true, then it isn't in a dir called P/previous, which I want to avoid
If InStr(1, fullFilePath, "evious") < 1 Then
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set f = objFSO.GetFile(fullFilePath)
'If true, then it isn't one of those funny duplicate files that microsoft makes, that has the ~ at the beginning of the file name
If f.Size > 5000 Then GoTo ReturnSettings
' if we're here then we have found the one single file that we want! Go ahead and do our thing
findFile = fullFilePath
Exit Function
End If
End If
End If
End If
End If
fileName = Dir()
Wend
For i = 0 To numFolders - 1
findFile folders(i), v
Next i
End Function
I'm a total VBA noob, so have just pva glued this together from what I can find online. Is it not working because of the findFile loop returning an array of one instead of a string? I thought the 'Exit Function' call would do away with that issue.
Please excuse the recursive if statements - the people that I am doing this for don't have a totally standard way of storing their ppts, but this hones down on the ppt I want. When the sub is complete, it will itself loop through 130 sub dirs of the selected dir, and within each of those sub dirs it will grab various slides from six different ppts and merge them into one, ie consolidate data from 780 ppts into 130 - something I definitely want to automate!
This is my first question posted on stack Overflow, so I hope I have posed it clearly and correctly. I have searched extensively for a solution to this. I hope the solution pops out to you! Many thanks in advance.
This is a classic case of needing to use Option Explicit.
You have a missing f from filename and this goes unchecked as a variable ilename not filename.
You should put Option Explicit at the top of every module and declare all your variables. There is also a missing label for a GoTo statement which I have added.
Note: You are doing a full string case sensitive match on the file name within the selected folder.
Option Explicit
Sub ManagementSummaryMerge()
Dim folderPath As String, FldrPicker As FileDialog, pptApp As Object
Set pptApp = CreateObject("PowerPoint.Application")
pptApp.Visible = True
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
folderPath = .SelectedItems(1) & "\"
End With
'In Case of Cancel
NextCode:
folderPath = folderPath
If folderPath = "" Then GoTo EndOfSub
'set _Main <= string I want to look for
Dim v As String
v = "_Main"
Dim fullFilePathIWantToSet As String
'set value of fullFilePathIWantToSet from findFile function
fullFilePathIWantToSet = findFile(folderPath, v)
'when I test, this MsgBox appears, but blank
MsgBox fullFilePathIWantToSet
'If I can get this working properly, I want to be able to do something like this:
'objFSO.CopyFile fullFilePathIWantToSet, duplicateFilePath
'Presentations.Open (duplicateFilePath)
'numSlides = ActivePresentation.Slides.Count
'etc
EndOfSub:
'let the sub end
End Sub
Function findFile(ByRef folderPath As String, ByVal v As String) As String
Dim fileName As String
Dim fullFilePath As String
Dim duplicateFilePath As String
Dim numFolders As Long
Dim numSlides As Integer
Dim folders() As String, i As Long
If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
fileName = Dir(folderPath & "*.*", vbDirectory)
While Len(fileName) <> 0
If Left(fileName, 1) <> "." Then
fullFilePath = folderPath & fileName
duplicateFilePath = folderPath & "duplicate " & fileName
If (GetAttr(fullFilePath) And vbDirectory) = vbDirectory Then
ReDim Preserve folders(0 To numFolders) As String
folders(numFolders) = fullFilePath
numFolders = numFolders + 1
Else
'if true, the it matches the string we are looking for
If InStr(10, fullFilePath, v) > 0 Then
'if true, then it isn't in a dir called P/previous, which I want to avoid
If InStr(1, fullFilePath, "evious") < 1 Then
Dim objFSO As Object, f As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set f = objFSO.GetFile(fullFilePath)
'If true, then it isn't one of those funny duplicate files that microsoft makes, that has the ~ at the beginning of the file name
If f.Size > 5000 Then GoTo ReturnSettings
' if we're here then we have found the one single file that we want! Go ahead and do our thing
findFile = fullFilePath
Exit Function
End If
End If
End If
End If
fileName = Dir()
Wend
For i = 0 To numFolders - 1
findFile folders(i), v
Next i
Exit Function
ReturnSettings:
End Function
OK, I have a solution to this. It's not totally elegant, because it relies on globally set variables, but it works and is good enough for me:
' show if a mistake is made
Option Explicit
' globally set the var we want to return to the sub from the function
Public foundFilePath As String
Sub FindIt()
Dim colFiles As New Collection, vFile As Variant, mypath As String
FldrPicker As FileDialog, fileToFind As String, pptApp As Object
Set pptApp = CreateObject("PowerPoint.Application")
pptApp.Visible = True
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
mypath = .SelectedItems(1) & "\"
End With
NextCode:
mypath = mypath
If mypath = "" Then GoTo EndOf
'
' find file
'
fileToFind = "*your_string_here*"
'calls to function RecursiveDir, which sets first matching file as foundFilePath
Call RecursiveDir(colFiles, mypath, fileToFind, True)
' do what you want with foundFilePath
MsgBox "Path of file found: " & foundFilePath
'
'find second file
'
fileToFind = "*your_second_string_here*"
Call RecursiveDir(colFiles, mypath, fileToFind, True)
MsgBox "Second file path: " & foundFilePath
EndOf:
End Sub
Public Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String, fullFilePath As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
strTemp = Dir(strFolder & strFileSpec)
Do While strTemp <> vbNullString
strFileSpec = Replace(strFileSpec, "*", "")
If InStr(strTemp, strFileSpec) > 0 Then
foundFilePath = strFolder & strTemp
Exit Function
End If
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
strTemp = Dir(strFolder, vbDirectory)
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Public Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
That works. What was a better solution for me is the below. It uses separate subs / functions to do the following: pick a folder ; loop through first-child folders ; recursively search for a file, using a partial file name, in all folders and subfolders ; do something with the found file/s (plural if the search function is called on multiple strings).
It's not necessary to separate out like this, but I find it easier for separation of concerns and keeping things simple.
Sub 1: Root folder picker. Passes selected folder onto sub 2
Option Explicit
Public foundFilePath As String
Sub StartSub()
' selects the parent folder and passes it to LoopSuppliers
Dim masterPath As String, FldrPicker As FileDialog, pptApp As Object
Set pptApp = CreateObject("PowerPoint.Application")
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
pptApp.Visible = True
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
masterPath = .SelectedItems(1) & "\"
End With
NextCode:
masterPath = masterPath
If masterPath = "" Then GoTo EndOf
Call LoopSuppliers(masterPath) ' goes to masterFolder in LoopSuppliers sub
EndOf:
End Sub
Sub two: simply loops through the parent folder and passes the path of each first-child sub folder to function three to do something with it. Adapted from here.
Private Sub LoopSuppliers(masterFolder As String)
Dim objFSO As Object, objFolder As Object, objSupplierFolder As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(masterFolder)
For Each objSupplierFolder In objFolder.SubFolders
'objSupplierFolder.path objSubFolder.Name <- object keys I can grab
Call ManipulateFiles(objSupplierFolder.path)
Next objSupplierFolder
End Sub
Function 1: Grabs file paths for doing something with
Private Function ManipulateFiles(ByRef FolderPath As String)
Dim file1 As String, file2 As String, file3 As String
' each of these calls find a file anywhere in a suppliers subfolders, using the second param as a search string, and then holds it as a new var
Call FindSupplierFile(FolderPath, "search_string1")
file1 = foundFilePath
Call FindSupplierFile(FolderPath, "search_string2")
file2 = foundFilePath
Call FindSupplierFile(FolderPath, "search_string3")
file3 = foundFilePath
'
' do something with the files!
'
End Function
Function 2: This is the function that takes a dir, a search string, and then loops through all the dirs folders and sub folders until it gets a match. I've included extra filtering, to show how I further narrowed down the files that could be returned to function 1.
Private Function FindSupplierFile(ByRef FolderPath As String, ByVal v As String) As String
Dim FileName As String, fullFilePath As String, numFolders As Long, Folders() As String, i As Long
Dim objFSO As Object, f As Object
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
'
' my filters
'
If InStr(1, fullFilePath, "evious") < 1 Then ' filter out files in folders called "_p/Previous"
If InStr(10, fullFilePath, v) > 0 Then ' match for our search string 'v'
Set objFSO = CreateObject("Scripting.FileSystemObject") ''
Set f = objFSO.GetFile(fullFilePath) '' use these three code lines to check that the file is more that 5kb - ie not a tiny ~ file
''
If f.Size > 5000 Then ''
foundFilePath = fullFilePath ' if we get in here we have the file that we want
Exit Function ' as we have found the file we want we can exit the function (which means we carry on with ManipulateFiles)
End If ' end f.size
End If ' end InStr v if
End If ' end InStr evious if
'
' end of my filters
'
End If ' end get attr if else
End If ' end left if
FileName = Dir()
Wend ' while len <> 0
For i = 0 To numFolders - 1
FindSupplierFile Folders(i), v
Next i
End Function
I am writing a code to open a text file through VBA, and match the value of mail_id taken from excel in the notepad. I saw few sample codes for this, and have modified it according to my data and requirement, but when counting the number of lines of the code, it is displaying error number 52. Also, please let me know how can I search for a string in notepad, and copy the text below it, if the string matches. Please find below code for reference:
If mail_id <> " " Then
Dim fso As Object
Dim sfile As String
Set fso = CreateObject("shell.application")
sfile = "C:\My Documents\Textfile.txt"
fso.Open (sfile)
f = FreeFile
Do While Not EOF(f)
Ingline = Ingline + 1
Line Input #f, strLine
If InStr(1, strLine, mail_id, vbBinaryCompare) > 0 Then
MsgBox Ingline, vbInformation
bInfound = True
End If
Exit Do
'End If
Loop
Close #f
If Not blnFound Then
MsgBox "Search string not found", vbInformation
End If
Its showing the error at Do While Not EOF(f)
I think you mixed up different methods how to open text files in VBA:
This should open a file and read line by line:
Option Explicit
Public Sub ReadLineByLineFromTextFile()
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim FileName As String
FileName = "c:\temp\test.txt"
Dim MyFile As Object
Set MyFile = fso.OpenTextFile(FileName, ForReading)
'' Read from the file line by line
Do While MyFile.AtEndOfStream <> True
Dim TextLine As String
TextLine = MyFile.ReadLine
'' Do stuff to TextLine here like …
'If InStr(1, TextLine, mail_id, vbBinaryCompare) > 0 Then
' MsgBox "found", vbInformation
'
' Exit Do 'cancel loop when found
'End If
Loop
MyFile.Close
End Sub
You are mixing up two different ways to read a text file in VBA. Both are explained in the same question on SO.
a) Use open: https://stackoverflow.com/a/11528932/7599798
b) Use FileSystemObject: https://stackoverflow.com/a/11529980/7599798
I am trying to edit code that someone else wrote. I have done NO VBA and very little coding in general.
The original code is written for a 5 digit number and we now have files that are six digits. I have tried to copy the code but change it to 6 digit numbers below the current code above Next objFile at the end. This has not worked.
The main issue here is I didn't write the original code and I don't understand the logic. I have tried just changing all of the 5's to 6's and the 99999 to 999999. I have tried copying from Folder = "" down, changing them to 6 digits and pasting below Next objFile. This didn't work either.
Sub CopyPics()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim varDirectory As Variant
Dim objSubFolder As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(Application.ActiveWorkbook.Path)
Dim Dest As String
Dest = "R:\Field Assurance\FA PHOTOS AND INFORMATION\"
'Loop through each file in this folder
For Each objFile In objFolder.Files
Folder = "" 'Empty old folder name
MainFolder = "" 'Empty old folder name
For i = 1 To Len(objFile.Name)
Test = Mid(objFile.Name, i, 5)
If Test >= 10000 And Test <= 99999 Then 'For files: Find any five numbers in a row and assume it to be the file number.
Folder = "NC-" & Mid(objFile.Name, i, 5) 'If found, create new folder.
i = Len(objFile.Name) 'In other words, take the first 5 numbers, then get out.
End If
Next
For Each objSubFolder In objFolder.subfolders 'Find the main folder.
If Right(Folder, 5) >= Mid(objSubFolder.Name, 4, 5) And Right(Folder, 5) <= Mid(objSubFolder.Name, 18, 5) Then 'If my file number is within the main folder bounds...
MainFolder = objSubFolder.Name & "\" 'Use that folder.
End If
Next objSubFolder
If Len(Folder) = 8 And Len(MainFolder) = 23 Then 'If real folders are identified...
On Error Resume Next
If Dir(Dest & MainFolder & Folder) = "" Then 'Check to see if the directory/folder does not exist...
objFSO.CreateFolder (Dest & MainFolder & Folder) 'If not, make one.
End If
'Rename that file's directory to be the new one - aka cut and paste file into new folder.
Name Application.ActiveWorkbook.Path & "\" & objFile.Name As Dest & MainFolder & Folder & "\" & objFile.Name
End If
Next objFile
ActiveWorkbook.Close
End Sub
This is a bit more complex than your original code but I think it's more robust...
Lightly tested.
Option Explicit
Sub CopyPics()
'use constants for fixed values
Const DEST As String = "R:\Field Assurance\FA PHOTOS AND INFORMATION\"
Dim objFSO As Object, srcFolder As Object, objFile As Object
Dim objSubFolder As Object, destFolder As Object, fNum, folderName, picFolderName
Dim FileWasMoved As Boolean, sMsg
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set srcFolder = objFSO.GetFolder(Application.ActiveWorkbook.Path) 'ThisWorkbook.Path ?
Set destFolder = objFSO.GetFolder(DEST) 'parent destination folder
'Loop through each file in this folder
For Each objFile In srcFolder.Files
FileWasMoved = False 'reset "moved" flag
fNum = ExtractNumber(objFile.Name) 'get the file number
If Len(fNum) > 0 Then 'any number found?
folderName = "NC-" & fNum
For Each objSubFolder In destFolder.subfolders 'Find the subfolder.
If IsTheCorrectFolder(objSubFolder.Name, fNum) Then
picFolderName = objSubFolder.Path & "\" & folderName
If Not objFSO.folderexists(picFolderName) Then
objFSO.CreateFolder picFolderName
End If
'move the file
Name objFile.Path As picFolderName & "\" & objFile.Name
FileWasMoved = True 'flag file as moved
Exit For
End If
Next objSubFolder
End If 'filename contains a number
'if file was not moved then add it to the list....
If Not FileWasMoved Then sMsg = sMsg & vbLf & objFile.Name
Next objFile
'warn user if some files were not moved
If Len(sMsg) > 0 Then
MsgBox "Some files were not moved:" & vbLf & sMsg, vbExclamation
End If
End Sub
'Return true/false depending on whether this is the correct
' folder to hold the specified filenumber
Function IsTheCorrectFolder(folderName, fileNumber) As Boolean
Dim arr, num1, num2, rv As Boolean
rv = False 'default return value
arr = Split(folderName, "thru") 'split folder name on "thru"
If UBound(arr) = 1 Then 'should have two parts
'get the numbers from each part and compare against the file number
num1 = ExtractNumber(arr(0))
num2 = ExtractNumber(arr(1))
If Len(num1) > 0 And Len(num2) > 0 Then
fileNumber = CLng(fileNumber) 'convenrt to Long for comparison
rv = (fileNumber >= CLng(num1) And fileNumber <= CLng(num2))
End If
End If
IsTheCorrectFolder = rv
End Function
'Extract the first 5- or 6-digit number from a string
' Match is "greedy" so if there are six digits it will match 6 and
' not just the first 5...
Function ExtractNumber(txt)
Dim re As Object, allMatches, rv
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "(\d{5,6})"
re.ignorecase = True
re.Global = True
Set allMatches = re.Execute(txt)
If allMatches.Count > 0 Then rv = allMatches(0) 'if there's a match then return the first one
ExtractNumber = rv
End Function
You need to change the lower limit in IF condition also. Like
If Test >= 10000 And Test <= 99999 Then
becomes
If Test >= 100000 And Test <= 999999 Then
Currently the loop could be exiting when it finds the first five digit number.
Sub Command3_Click()
Dim fs As FileSystemObject
Dim f As TextStream
Dim a As Variant
Dim i As Long
Set fs = CreateObject("Scripting.FileSystemObject")
' Read file into array
If fs.FileExists("C:\rbc.csv") Then
Set f = fs.OpenTextFile("C:\rbc.csv", ForReading, False)
a = Split(f.ReadAll, vbNewLine, -1, vbTextCompare)
f.Close
Else
MsgBox "The file path is invalid.", vbCritical, vbNullString
Exit Sub
End If
' Write line > 1 to file
Set f = fs.OpenTextFile("C:\rbc.csv", ForWriting, True)
For i = 1 To UBound(a)
f.WriteLine a(i)
Next
f.Close
End Sub
It worked fine when I tried it on csv files , but then I had a problem . One of the files has the first line as " A,B,C,D, " (NO Quotes ) and second file has first line as
" 01-JUL-2014,RBC_BASELII_07012014,,,,,,,,,,,,,,,,,,, " .
Now when I try to delete the first line of the second file , the entire file text gets deleted , while it deletes only one line at a time in the first file. Please help me out.
Hopefully the comments explain what's going on.
Sub Command3_Click()
Dim fs As FileSystemObject
Dim f As TextStream
Dim a As Variant
Dim i As Long
Dim sLineBreak As String
Dim sAll As String
Const sFILE As String = "K:\rbc.csv"
Set fs = CreateObject("Scripting.FileSystemObject")
If fs.FileExists(sFILE) Then
Set f = fs.OpenTextFile(sFILE, ForReading, False)
sAll = f.ReadAll
'if there are cr's, then it's either only cr's or it's both
If InStr(1, sAll, vbCr) > 0 Then
a = Split(sAll, vbCr, -1, vbTextCompare)
Else 'no cr's means it's only line feeds
a = Split(sAll, vbLf, -1, vbTextCompare)
End If
f.Close
Else
MsgBox "The file path is invalid.", vbCritical, vbNullString
Exit Sub
End If
' Write line > 1 to file
Set f = fs.OpenTextFile(sFILE, ForWriting, True)
For i = 1 To UBound(a)
'if there are cr's, then we split on them, so they wouldn't
'be there anymore. But if it was both, there would be
'vblf's left over and we want to get rid of those
f.WriteLine Replace(a(i), vbLf, vbNullString)
Next
f.Close
End Sub