there are quite a few different excel macro files with a link that has been updated. Is there a way to modify a macro with a macro?
This is what I have been able to find so far using CodeModule, find line where the link is at and then replace the whole line. I have this macro in one file, and I run it in a file that I want to change (later I may automate it to go through all files in a folder), but need to get it working first.
One part that is confusing me, at which point does SL (starting line) gets changed?
What are CodeModule columns? My link is say 35characters long, why is the column 35, should it not be 1?
Is there a better way? Thanks
Sub findLink()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim FindWhat As String
Dim SL As Long ' start line
Dim EL As Long ' end line
Dim SC As Long ' start column
Dim EC As Long ' end column
Dim Found As Boolean
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents("Module1")
Set CodeMod = VBComp.CodeModule
'find text to replace
FindWhat = "https://www.abc.net.au/"
With CodeMod
SL = 1
EL = .CountOfLines
SC = 1
EC = 255
Found = .Find(target:=FindWhat, StartLine:=SL, StartColumn:=SC, _
EndLine:=EL, EndColumn:=EC, _
wholeword:=True, MatchCase:=False, patternsearch:=False)
Do Until Found = False
MsgBox CStr(SL)
Debug.Print "Found at: Line: " & CStr(SL) & " Column: " & CStr(SC)
EL = .CountOfLines
SC = EC + 1
EC = 255
Found = .Find(target:=FindWhat, StartLine:=SL, StartColumn:=SC, _
EndLine:=EL, EndColumn:=EC, _
wholeword:=True, MatchCase:=False, patternsearch:=False)
Loop
'If Found Then
Call .ReplaceLine(SL, "Link = ""https://www.abc2.net.au/"" ")
End With
MsgBox "Found at: Line: " & CStr(SL) & " Column: " & CStr(SC)
End Sub
"One part that is confusing me, at which point does SL (starting line) gets changed?"
When a match is found it is returned/changed like the line number on which the matching text is found. Independent of its initial value. So, when trying to search from the beginning, it is enough to only declare the variable.
And, in order to let the code searching the rest of the lines, you should increment this returned line number/variable:
So, you should replace:
SC = EC + 1
with:
SL = SL + 1 'to start searching from the next code line
"What are CodeModule columns? My link is say 35characters long, why is the column 35, should it not be 1?"
Well, "Each character in a code line is in a separate column, beginning with zero on the left code line side. If a match is found, the value of the StartColumn argument is set to the column in which the beginning character of the matching string is found.
So, if your code returns 35 (as StartColumn, after the match) it means that there are other 34 characters before the first one of the link string. Space characters are also counted...
If the respective code line contains other words which should be kept, the code may be adapted to change only the necessary string.
And, if there are more occurrences, your code (without the above suggested modification) will return only the first one. After modification, the final MsgBox will return only the last occurrence...
Related
I posted another question that was close to this question earlier but it is actually different. I have this VLOOKUP code that takes input from a user to get the file to use the VLOOKUP with. It works in my one macro when I run the whole thing, but if I run the private sub by itself, I get an error message 1004 on the first VLOOKUP line. I then tried changing the code to use FormulaR1C1, and it ended up working correctly using that version. Why won't it work using my current code but it works when I use FormulaR1C1?
Sub NEWTRY()
'
' Create_VLOOKUP_Using_Old_Kronos_Full_File Macro
'
'
Dim iRet As Integer
Dim strPrompt As String
Dim strTitle As String
' Promt
strPrompt = "Please select the last Kronos Full File before the dates of this HCM Report." & vbCrLf & _
"This will be used to find the Old Position, Org Unit, and Old Cost Center." & vbCrLf & _
"For example, if the date of this report is 7-28-17 thru 8-25-17, the closest Kronos Full File you would want to use is 7-27-17."
' Dialog's Title
strTitle = "Last Kronos Full File for Old Positions"
'Display MessageBox
iRet = MsgBox(strPrompt, vbOK, strTitle)
Dim LR As Long
Dim X As String
Dim lNewBracketLocation As Long
X = Application.GetOpenFilename( _
FileFilter:="Excel Files (*.xls*),*.xls*", _
Title:="Choose the Kronos Full File.", MultiSelect:=False)
Dim wbk As Workbook
Set wbk = Workbooks.Open(Filename:=X, ReadOnly:=True)
Dim shtName As String
shtName = wbk.Worksheets(1).name
wbk.Close
MsgBox "You selected " & X
'Find the last instance in the string of the path separator "\"
lNewBracketLocation = InStrRev(X, Application.PathSeparator)
'Edit the string to suit the VLOOKUP formula - insert "["
X = Left$(X, lNewBracketLocation) & "[" & Right$(X, Len(X) - lNewBracketLocation)
Range("T2").FormulaR1C1 = "=VLOOKUP(RC11,'" & X & "]'!R3C2:R9846C49,13,0)"
ActiveWorkbook.ActiveSheet.Range("U2").Formula = "=VLOOKUP($E2,'" & X & "]'!$B$1:$AP$99999,41,0)"
Range("V2").Formula = "=VLOOKUP($E2,'" & X & "]shtName'!$B$1:$AP$99999,18,0)"
The issue is I believe in the last 3 lines, or how it is reading X and putting that in there. The last 3 lines with the VLOOKUPS is where it errors except now the first line with R1C1 actually works. I was trying other versions with the other lines but they don't work.
I would rather not use the R1C1 but it doesn't want to work unless I use it.
So, you're trying to do a lookup on a sheet whose name is the last part of the selected path?
Add a line msgbox x before your lookups so you can make sure that x is being calculated as you intended... For me it returned:
c:\path\[filename.xlsm
What is an example of x ?
...the 3 formulas getting pasted in are:
=VLOOKUP(RC11,'c:\path\[filename.xlsm]'!R3C2:R9846C49,13,0)
=VLOOKUP($E2,'c:\path\[filename.xlsm]'!$B$1:$AP$99999,41,0)
=VLOOKUP($E2,'c:\path\[filename.xlsm]shtName'!$B$1:$AP$99999,18,0)
I am writing code which matches a date (from a file), puts this into a collection and then attempts to find this on a spreadsheet. Once it finds it, it puts the following two items in the collection in the two cells. When I run this I get the following error: "Object variable or With block variable not set". I have attempted to debug my code and it shows that after the first loop of the code below, the range object, "rthecell", changes to the proper value. Once the second iteration of the loop occurs the value of "rthecell" changes to "Nothing".
Ex:
Set rtheCell = Range("A:A").Find(What:=LineItem1)
rtheCell.Offset(, 1).Value = LineItem3
rtheCell.Offset(, 2).Value = LineItem2
Set rtheCell = Nothing
Again, everything works as intended on the first iteration of the loop but I receive the error once the second iteration occurs.
Here is the full code:
Sub InputData()
'Declare variables
Dim sFilePath As String
Dim sLineFromFile As String
Dim saLineItems() As String
Dim element As Variant
Dim col As Collection
Dim LineItem1 As String
Dim LineItem2 As String
Dim LineItem3 As String
Dim rtheCell As Range
Set col = New Collection
'Insert file path name here, this file will be overwritten each morning
sFilePath = "P:\Billing_Count.csv"
Open sFilePath For Input As #1
Do Until EOF(1)
Line Input #1, sLineFromFile
'Split each line into a string array
'First replace all space with comma, then replace all double comma with single comma
'Replace all commas with space
'Then perform split with all values separated by one space
sLineFromFile = Replace(sLineFromFile, Chr(32), ",")
sLineFromFile = Replace(sLineFromFile, ",,", ",")
sLineFromFile = Replace(sLineFromFile, ",", " ")
saLineItems = Split(sLineFromFile, " ")
'Add line from saLineItem array to a collection
For Each element In saLineItems
If element <> " " Then
col.Add element
End If
Next
Loop
Close #1
'Place each value of array into a smaller array of size 3
Dim i As Integer
i = 1
Do Until i > col.Count
'Place each value of array into a string-type variable
'This line is the date
LineItem1 = col.Item(i)
i = i + 1
'This line should be the BW count make sure to check
LineItem2 = col.Item(i)
i = i + 1
'This line should be the ECC count make sure to check
LineItem3 = col.Item(i)
i = i + 1
'Find the matching date in existing Daily Billing File (dates on Excel must be formatted as
'general or text) and add ECC and BW counts on adjacent fields
Set rtheCell = Range("A3:A37").Find(What:=LineItem1)
rtheCell.Offset(, 1).Value = LineItem3 'This is LineItem3 since we can ECC data to appear before BW
rtheCell.Offset(, 2).Value = LineItem2
Set rtheCell = Nothing
LineItem1 = 0
Loop
'Format cells to appear as number with no decimals
'Format cells to have horizontal alignment
Sheets(1).Range("B3:C50").NumberFormat = "0"
Sheets(1).Range("C3:C50").HorizontalAlignment = xlRight
End Sub
when you use the Range.Find method, typically you would either use the After:= parameter in subsequent calls or use the Range.FindNext method which assumes After:= the last found item. Since you are not modifying the actual found cells' value(s) in any way, you need to record the original found cell (typically the address) because eventually you will loop back to the original.
dim fndrng as range, fndstr as string
set fndrng = Range("A:A").Find(What:=LineItem1, after:=cells(rows.count, "A"))
if not fndrng is nothing then
fndstr = fndrng.address
do while True
'do stuff here
set fndrng = Range("A:A").FindNext(after:=fndrng)
if fndstr = fndrng.address then exit do
loop
end if
That should give you the idea of looping through all the matching calls until you loop back to the original. tbh, it is hard to adequately expand on the small amount of code supplied.
I'm trying to figure out a way to read the first line of text in a .txt through excel VBA without opening the file, something I've been having trouble finding since all the examples I've seen involve opening the .txt one way or another.
Aside from this, I was wondering if there was any way for me to get the VBA code to delete the mentioned .txt a set time after excel has been closed... which I'm not too sure is even remotely possible (with VBA at least).
EDIT:
The simplified code goes like this:
Option Explicit
Public g_strVar As String
Sub Test_Proc()
Dim row as Long
row = 2
Do While Cells(row, 1) <> ""
Cells(row, 2) = ImportVariable(Cells(row, 1))
row = row + 1
Loop
End Sub
Function ImportVariable(strFile As String) As String
Open strFile For Input As #1
Line Input #1, ImportVariable
Close #1
End Function
Column 1 contains the locations of each and every .txt file, and on the column next to it I have to detail what is the first line of text on each file. Problem is that the list has been in a couple occasions about 10K long, and the only place I can think of from where I can improve on the time this takes to execute is in the "Open / Close" since some of these .txt files are 12.000 KB in size and take a bit to open.
This might be faster than opening each file (reads first line from a 18.5 Mb file in 0.1953125 sec)
Option Explicit
Dim cmdLine As Object
Sub Test_Proc()
Dim i As Long, minRow As Long, maxRow As Long, rng1 As Range, rng2 As Range
Dim t As Double, ws As Worksheet, x As Variant, col1 As Variant, col2 As Variant
Set ws = ThisWorkbook.Worksheets(1)
minRow = 2
With ws
.Columns(2).Delete
maxRow = .UsedRange.Rows.Count
Set rng1 = .Range("A1:A" & maxRow)
Set rng2 = .Range("B1:B" & maxRow)
End With
col1 = rng1.Value2: col2 = rng2.Value2
Set cmdLine = CreateObject("WScript.Shell")
Application.ScreenUpdating = False
t = Timer
For i = minRow To maxRow
If Len(col1(i, 1)) > 0 Then
ws.Cells(i, 2).Value2 = Replace(ImportLine(col1(i, 1)), vbCrLf, vbNullString)
End If
Next
'rng2.Value2 = col2
Application.ScreenUpdating = True
InputBox "Duration: ", "Duration", Timer - t '18.5 Mb file in 0.1953125 sec
End Sub
Function ImportLine(ByVal strFile As String) As String
ImportLine = Replace(cmdLine.Exec( _
"%comspec% /C FindStr /N . " & strFile & " | FindStr ^1:" _
).STDOut.ReadAll, "1:", vbNullString)
End Function
A bit nested but it does the following:
CMD /C - opens a command line window, then closes it when completed
FindStr /N . C:\test.txt - Find any character, and output the line with line number in format "1:"
| FindStr ^1: - redirect to another FindStr that uses regex to find "1:" at start of line
When the command line is completed, return the output to the Replace function
Replace removes "1:" and returns the string
If your files might contain the string "1:" somewhere else within the first line
we can use the Right() function: return Right(output, Len(output)-2)
or we can use a different command line that numbers the lines with "[1]":
Find /N " " C:\test.txt | Find "[1]"
I have a long list of Accounts labels which I need to format out the contact information to leave only the name (first word in every paragraph). I have some experience with VBA excel, but this is my first foray into word.
So what I want to do is delete everything after the first word, but leave all paragraph breaks intact, if possible (whoever made the list formatted it with lots of breaks, rather than spacing).
Thanks a ton in advance!
Try something like this, modify as needed. Not 100% sure it will preserve your paragraph breaks but this should at least get you to a list of "first word" in each of the paragraphs.
Sub FirstWord()
Dim myString$
Dim MyDoc As Document
Dim DocPara As Paragraph
Dim i%
Dim p%
Set MyDoc = ActiveDocument
For p = MyDoc.Paragraphs.Count To 1 Step -1
Set DocPara = MyDoc.Paragraphs(p)
i = InStr(1, DocPara.Range.Text, " ")
DocPara.Range.Text = _
Left(DocPara.Range.Text, i) & Right(DocPara.Range.Text, 1)
Next
End Sub
UPDATED
To address leading spaces indenting each paragraph, try this instead. I'm going to modify the above routine so you can see a few changes to this code and how I just adapt it. I haven't tested this version yet, letme know if any problems.
Sub FirstWordIndented()
Dim myString$
Dim x% '<-- this is new
Dim MyDoc As Document
Dim DocPara As Paragraph
Dim i%
Dim p%
Set MyDoc = ActiveDocument
For p = MyDoc.Paragraphs.Count To 1 Step -1
Set DocPara = MyDoc.Paragraphs(p)
'// Make sure to ignore leading spaces
'// This block should remove leading spaces
myString = DocPara.Range.Text
Do
If Not Left(myString,1) = " " Then Exit Do
'// Removes the first character if it's a space
myString = Right(myString, Len(myString) - 1)
'// Loop until the first character isn't a space
Loop
'// Some small modifications to use the myString variable in this block:
i = InStr(1, myString, " ")
DocPara.Range.Text = _
Left(myString, i) & Right(myString, 1)
Next
End Sub
BEFORE
AFTER
In the code below I could wrote a code to display filenames from a folder. The problem is now that I should display them in the correct Row. For now they are displayed in random sequence and that is not the purpose.
In column "A" the filenames to search for are called with similar name format PBM12.T5.103.
The actual filename to find is called with similar name format 1_29_PBM_12_T5__103.
I have to find a solution to compare "only" the Fat marked letters and numbers like displayed here above, without . or _
As you will see PBM12T5103 is returning in both namestructures.
Please don't try fixed length counts because the filenames are dynamic and the amount of letters are variable. The comparison of the SUBSTITUTED length of column "A" ( PBM12T5103) is the key to comparison but I can not handle to establish this comparison.
When the filename in column "A" has been found, in column "C" the full filename of found file has to be displayed as the original format 1_29_PBM_12_T5__103
Maybe a solution can be found when extra columns can be made to establish the comparison?
In Excel I could approach a solution, but this will not work automized like it should do.
I made the LEN(count dynamic), but this is still no solution to display the full filenames in the required row...
Hopefully somebody can help me out ..
Option Explicit
Sub fileNames_in_folder()
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Dim fldpath
Dim fld As Object, fil As Object, fso As Object, j As Long
fldpath = "C:\"
On Error Resume Next
Thisworkbook.Sheets("1").Activate
'start count row
j = 11
Set fso = CreateObject("scripting.filesystemobject")
Set fld = fso.getfolder(fldpath)
For Each fil In fld.Files
'here I have to add an IF statement in order to compare the filenames written in column "A" with files from folderPath C:\"
'When the correct files is found it should be displayed in column "C"
If
then
Cells(j, 34).Value = fso.GetBaseName(fil.path)
End If
'count behaviour
j = j + 1
Next
Columns("AH").AutoFit
End Sub
I will suggest you different way of getting file names. Instead of FileSystemObject let's use simple Dir function which allows to check the pattern of file name.
1) Files in my testing folder are as follows
2) I assumed that file pattern is as follows:
XXXY.Z.W
where:
XXX > 3 letters text
Y > any length number/text
Z > any length number/text
W > any length number/text
3) The code of subroutine is placed in 2013-06-01...xlsm file which you could see in the pic above (the same folder where your files are). Code is as follows (change where appropriate):
Sub solution()
Dim j As Long, LastRow As Long
Dim fldPath
'your path below
fldPath = ThisWorkbook.Path
ChDir fldPath
Dim arrPattern As Variant
Dim filName As String
For j = 1 To Range("A1").End(xlDown).Row
arrPattern = Split(Cells(j, "A"), ".")
'I suggest to use different way of checking _
pattern of file name. Pattern rules:
'*YYY*XX*Z*W*
filName = Dir("*" & Left(arrPattern(0), 3) & "*" & _
Mid(arrPattern(0), 4) & "*" & _
arrPattern(1) & "*" & _
arrPattern(2) & "*")
If Len(filName) > 0 Then
Cells(j, "B") = filName
Else
Cells(j, "B") = "not found"
End If
Next j
End Sub
4) results are presented in the picture below: