VBA Excel lookup - vba

I need help in finding an exact match by using VBA in Excel. Here is my object 7 problem.
Objective - to batch process finding and replacing words.
This is a routine task which I'm trying to automate. The task involves finding terms and then replacing them with an alternate word. E.g if the term to be found is "microsoft", I want it to be replaced with say "Company".
While majority of the code is working the limitation is --> if there are two words to be found e.g. 1. Gold 2. Golden and then replace "gold" with "metal" and golden with " mineral here's what happens. If the code find Golden anywhere then the word gold is replaced first and the end product looks like this. Metalen. can someone please help?
Dim wksheet As Worksheet
Dim wkbook As Workbook
Dim fo_filesys As New Scripting.FileSystemObject
Dim RegExpObject As Object
Private Sub cmd_Start_Click()
Dim lsz_dest_path As String
Dim lsz_extn_to_use As String
Dim lsz_filename As String
Dim li_rowtoread As Integer
Application.DisplayAlerts = False
Application.ScreenUpdating = False
lsz_dest_path = VBA.Strings.Trim(Cells(1, 6))
lsz_extn_to_use = VBA.Strings.Trim(Cells(2, 6))
Set RegExpObject = CreateObject("VBScript.RegExp")
RegExpObject.IgnoreCase = True
RegExpObject.Global = True
lsz_filename = Dir(lsz_dest_path & "\" & lsz_extn_to_use)
Do While lsz_filename <> ""
Application.StatusBar = "Scrubbing " & lsz_filename
Set wkbook = Workbooks.Open(lsz_dest_path & "\" & lsz_filename)
For Each wksheet In wkbook.Worksheets
wksheet.Activate
li_rowtoread = 2
Do While Cells(li_rowtoread, 1) <> ""
user_process_file Cells(li_rowtoread, 1), Cells(li_rowtoread, 2), lsz_filename
li_rowtoread = li_rowtoread + 1
DoEvents
Loop
Next wksheet
wkbook.Close True
lsz_filename = Dir
Loop
Application.StatusBar = ""
End Sub
Sub user_process_file(lsz_searh_str As String, lsz_replace_str As String, filename As String)
Dim myRange As Range
Dim lo_tstream As TextStream
Dim lo_reader_tstream As TextStream
Dim lsz_file As String
Dim lb_replaced As Boolean
If fo_filesys.FileExists(filename & ".log") Then
Set lo_reader_tstream = fo_filesys.OpenTextFile(filename & ".log", ForReading)
lsz_file = lo_reader_tstream.ReadAll
lo_reader_tstream.Close
End If
If lsz_searh_str = "RRD" Then
' MsgBox "Here"
End If
Set myRange = wksheet.Cells
myRange.Cells.Find(What:="", After:=ActiveCell, lookat:=xlPart).Activate
'myRange.Replace What:=lsz_searh_str, Replacement:=lsz_replace_str, LookAt:=xlWorkbook, MatchCase:=False, searchorder:=xlByRows ', LookIn:=xlFormulas
With myRange
Set c = .Find(lsz_searh_str, LookIn:=xlValues, lookat:=xlPart)
If Not c Is Nothing Then
firstAddress = c.Address
Do
c.Value = CustomReplace(c.Value, lsz_searh_str, lsz_replace_str)
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
Set lo_tstream = fo_filesys.OpenTextFile(filename & ".log", ForAppending, True)
lb_replaced = myRange.Replace(What:=lsz_searh_str, Replacement:=lsz_replace_str, lookat:=xlWhole, MatchCase:=True, searchorder:=xlByRows)
If lb_replaced = True Then
lo_tstream.WriteLine lsz_replace_str
lo_tstream.Close
End If
End Sub
Function user_eval(lookfor As String, loc_data As String) As Boolean
Dim lsz_val_at_loc As String
If InStr(1, loc_data, lookfor) = 1 Then
user_eval = True
Else
user_eval = False
End If
End Function
Function CustomReplace(OriginalString As String, FindString As String, ReplaceString As String)
RegExpObject.Pattern = "[^a-zA-Z0-9]*" & FindString & "[^a-zA-Z0-9]*"
CustomReplace = RegExpObject.Replace(OriginalString, ReplaceString)
End Function

I do not have permissions to add a comment, so answering the only way I can:
There is a problem with your regex find string [^a-zA-Z0-9]* and [^a-zA-Z0-9]*.
Try using \bgold\w+\b to match words starting with gold and \bgold\b to match gold exactly.
Although I'm answering late, it might help somebody who has a similar problem...

Related

Regex vba script throwing error : runtime error 9 ... Subscript out of range

I have a Word doc with some numbers referred in the foot notes. and I am exporting these references as a csv file.
Sub FindNumber()
Dim exp, exp1 As RegExp
Set exp = New RegExp
exp.Pattern = "\b[A-Za-z]{3}[0-9]{7}\b"
exp.Global = True
Dim splits(1000) As String
Dim x As Long
Dim results As MatchCollection
Set results = exp.Execute(ActiveDocument.StoryRanges(wdFootnotesStory))
x = 1
For Each res In results
splits(x) = res
x = x + 1
Next res
Dim Filename As String, line As String
Dim i As Integer
Filename = "C:\VBA Export" & "\Numbers.csv"
Open Filename For Output As #2
Print #2, "Control Numbers"
For i = LBound(splits) To UBound(splits)
Print #2, splits(i)
Next i
Close #2
MsgBox "Numbers were exported to " & Filename, vbInformation
End Sub
The code above was working fine and just suddenly starting throwing error at 'splits(x) = res'
I have tried checking my regex and I can see that it works fine. If I change splits(x) to splits(6) or something similar it works like a charm .
Can someone please help ?
EDIT - changed code to write matches directly to Excel.
Sub Tester()
Dim oXl As Excel.Application 'add reference to MS Excel object library...
Dim oWb As Excel.Workbook, c As Excel.Range, i As Long, col As Collection
Set oXl = New Excel.Application
oXl.Visible = True
Set oWb = oXl.Workbooks.Add()
Set c = oWb.Worksheets(1).Range("A1")
ListMatchesInExcel ActiveDocument.StoryRanges(wdFootnotesStory), _
"\b[A-Za-z]{3}[0-9]{7}\b", _
"Id Numbers", c
Set c = c.Offset(0, 1)
ListMatchesInExcel ActiveDocument.StoryRanges(wdFootnotesStory), _
"\b[A-Za-z]{2}[0-9]{9}\b", _
"Other Numbers", c
Set c = c.Offset(0, 1)
'etc etc
End Sub
'Search through `SearchText` for text matching `patt` and export all
' matches to Excel with a header `HeaderText`, starting at range `c`
Sub ListMatchesInExcel(SearchText As String, patt As String, _
headerText As String, c As Excel.Range)
'add reference to MicroSoft VBscript regular expressions
Dim exp, exp1 As RegExp, col As New Collection
Dim results As MatchCollection, res As Match, i As Long
Set exp = New RegExp
exp.Pattern = patt
exp.Global = True
Set results = exp.Execute(SearchText)
'log to Immediate pane
Debug.Print (col.Count - 1) & " matche(s) for '" & patt & "'"
c.Value = headerText
i = 1
For Each res In results
c.Offset(i).Value = res
i = i + 1
Next res
c.EntireColumn.AutoFit
End Sub

VBA search for value on next sheet

is there I way for searching a value on the next sheet (ActiveSheet.Next.Activate) without jumping on to it?
Here the whole Code:
the problem is, it jumps to the next sheet even if there is no searched value.
Dim ws As Worksheet
Dim Loc As Range
Dim StrVal As String
Dim StrRep As String
Dim i As Integer
Private Sub CommandButton1_Click()
i = 1
Call Replacing
End Sub
Private Sub CommandButton2_Click()
i = 2
Call Replacing
End Sub
Public Sub Replacing()
StrVal = Userform1.Textbox1.Text
StrRep = Me.Textbox1.Text
if Trim(StrVal) = "" Then Exit Sub
Dim fstAddress As String
Dim nxtAddress As String
For Each ws In ThisWorkbook.Worksheets
With ws
Set Loc = .Cells.Find(what:=StrVal)
fstAddress = Loc.Address
If Not Loc Is Nothing Then
If Not StrRep = "" And i = 1 Then
Loc.Value = StrRep
Set Loc = .Cells.FindNext(Loc)
ElseIf i = 2 Then Set Loc = Range(ActiveCell.Address)
Set Loc = .Cells.FindNext(Loc)
nxtAddress = Loc.Address
If Loc.Address = fstAddress Then
ActiveSheet.Next.Activate '****Here it should jump only if found something on the next sheet****
GoTo repeat
nxtAddress = Loc.Address
End If
If Not Loc Is Nothing Then Application.Goto ws.Range(nxtAddress), False
End If
i = 0
End If
End With
Set Loc = Nothing
repeat:
Next ws
End Sub
the variable "i" which switches between the values 0, 1 and 2 is bound to two buttons. these buttons are "Replace" and "Skip (to next found value)".
This code asks on each occurrence of StrVal whether you want to replace the value or skip it.
I found a problem checking if Found_Address = First_Found_Address:
If you've replaced the value in in First_Found_Address it won't find that address again and miss the starting point in the loop.
Also the original source of the code stops at the last value using Loop While Not c Is Nothing And c.Address <> firstAddress. The problem here is that if the value in c is being changed eventually c will be Nothing but it will still try and check the address of c - causing an error (Range Find Method).
My solution to this is to build up a string of visited addresses on the sheet and checking if the current address has already been visited using INSTR.
I've included the code for calling from a button click or from within another procedure.
Private Sub CommandButton1_Click()
FindReplace Userform1.Textbox1.Text, 1
End Sub
Private Sub CommandButton2_Click()
FindReplace Userform1.Textbox1.Text, 1, Me.Textbox1.Text
End Sub
Sub Test()
FindReplace "cd", 1, "ab"
End Sub
Sub FindReplace(StrVal As String, i As Long, Optional StrRep As String = "")
Dim ws As Worksheet
Dim Loc As Range
Dim fstAddress As String
Dim bDecision As Variant
For Each ws In ThisWorkbook.Worksheets
'Reset the visited address list on each sheet.
fstAddress = ""
With ws
Set Loc = .Cells.Find(what:=StrVal, LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
If Not Loc Is Nothing Then
Do
fstAddress = fstAddress & "|" & Loc.Address
Loc.Parent.Activate 'Activate the correct sheet.
Loc.Activate 'and then the cell on the sheet.
bDecision = MsgBox("Replace value?", vbYesNo + vbQuestion, "Replace or Select value?")
If bDecision = vbYes Then
Loc = StrRep 'Raise the blade, make the change.
'Re-arrange it 'til it's sane.
End If
Set Loc = .Cells.FindNext(Loc)
If Loc Is Nothing Then Exit Do
Loop While InStr(fstAddress, Loc.Address) = 0
End If
End With
Next ws
End Sub

Merge Cells with Duplicate Data VBA

I'm trying to get a Macro working to merge cells with duplicate data. It will work on small numbers of cells, but I get the following error if I try to run it on a larger group of cells. I'm not sure if there's a more efficient way for excel to run through this.
Run-Time error '1004':
Method 'Range' of object '_Global' failed
Here's the code:
Sub MergeDuplicates()
Dim varData As Variant, varContent As Variant
Dim strMyRange As String
Application.ScreenUpdating = False
Application.DisplayAlerts = False
strMyRange = ActiveCell.Address
varContent = ActiveCell.Value
For Each varData In Selection.Cells
If varData.Value <> varContent Then
strMyRange = strMyRange & ":" & Cells(varData.Row - 1, varData.Column).Address & ", " & Cells(varData.Row, varData.Column).Address
varContent = Cells(varData.Row, varData.Column).Value
End If
Next
strMyRange = strMyRange & Mid(Selection.Address, InStr(1, Selection.Address, ":"), Len(Selection.Address))
Range(strMyRange).Merge
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
I have recreated the issue using the code you posted and it is working for me. I did what you suggested and put the merge into the For loop. Then I split strMyRange using the comma as the delimiter. I set up a test to look for the ":" character in TestArray(0). If it is in that target string, then I know it is ready for the merge. After that I reset strMyRange to the TestArray(1) which is the beginning of the next range.
Note: I was able to step through it with the debugger with 100 cells and it worked. Then I tried running it without any code breakpoints, but it merged all the selected cells. I put a 1 second wait statement right before the final merge and that seems to work.
Here is the code:
Sub MergeDuplicates()
Dim varData As Variant, varContent As Variant
Dim strMyRange As String
Dim TestArray() As String
Dim target As String
Dim pos As Integer
Application.ScreenUpdating = False
Application.DisplayAlerts = False
strMyRange = ActiveCell.Address
varContent = ActiveCell.Value
For Each varData In Selection.Cells
If varData.Value <> varContent Then
strMyRange = strMyRange & ":" & Cells(varData.Row - 1, varData.Column).Address & ", " & Cells(varData.Row, varData.Column).Address
TestArray = Split(strMyRange, ",")
target = TestArray(0)
pos = InStr(target, ":")
If (pos > 0) Then
Range(target).Merge
strMyRange = TestArray(1)
End If
varContent = Cells(varData.Row, varData.Column).Value
End If
Next
strMyRange = strMyRange & Mid(Selection.Address, InStr(1, Selection.Address, ":"), Len(Selection.Address))
Application.Wait (Now + #12:00:01 AM#) 'This helps the application run OK if there are no breakpoints.
Range(strMyRange).Merge
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub

How to loop through an excel sheet from word

I am struggling to find out how i can loop through all rows in excel from word. What I want to achieve is that if there is something in the WP column in excel then save the active worddocument with that filename. However i cant figure out something simple as getting the last row(might be empty rows in between), i just get error code 424, which according to MSDN does not give me any real hint whats wrong. Any ideas whats wrong?
Public Sub test()
Dim xlapp As Object
Set xlapp = CreateObject("Excel.Application")
myFileName = "Z:\Dokumentstyring\UnderArbeid\PARTSLIST.xlsm"
xlapp.Workbooks.Open myFileName
xlapp.Application.ScreenUpdating = False
xlapp.Visible = False
a = xlapp.sheets("List").Range("A1").Value
b = firstBlankRow(xlapp)
c = getColumn("WP", xlapp)
xlapp.Application.ScreenUpdating = True
xlapp.ActiveWorkbook.Close (True)
xlapp.Quit
Set xlapp = Nothing
End Sub
My function to receive the last row:
Function firstBlankRow(ByRef xlapp) As Long
'returns the row # of the row after the last used row
'Or the first row with no data in it
With xlapp.sheets("List")
'~~> Check if there is any data in the sheet
'If xlapp.WorksheetFunction.CountA(.Cells) <> 0 Then
firstBlankRow = .Range("A" & .Rows.Count).End(Excel.XlDirection.xlUp).Row
'Else
firstBlankRow = 1
'End If
End With
End Function
Here is my function for getting the column number
Public Function getColumn(header As String, ByRef xlapp) As Integer
Dim rng1 As Range
With xlapp.sheets("List")
Set rng1 = .Range(.Cells(1, 1), .Cells(1, Columns.Count)).Find(header, , xlValues, xlWhole)
If Not rng1 Is Nothing Then
getColumn = rng1.Column
Else
MsgBox "Column " & header & " does not exist, Typo??", vbCritical
getColumn = -1
End If
End With
End Function
As per our conversation in the comments, change Dim Rng1 As Range to
Dim Rng1 As Object.
You can find the actual values of XlDirection, Xlvalues, xlwhole enums.
Preferably, it's better to do it like this:
Private Const xlUp as long = -4162
'and so on
Edit1:
Here's an adjusted function that solves your problem (I've tested it on my machine)
Public Function getColumn(header As String, ByRef xlapp) As Long
Dim rng1 As Object
With xlapp.sheets("List")
Set rng1 = .Range(.Cells(1, 1), .Cells(1, .Columns.Count))
If rng1 Is Nothing Then
MsgBox ("ERROR: Range object is empty.")
getColumn = -1
Exit Function
End If
For Each m In rng1
If m = "WP" Then
getColumn = m.Column
Exit Function
End If
Next m
MsgBox "Column " & header & " does not exist, Typo??", vbCritical
getColumn = -1
End With
End Function

VBA issue with operators

I am facin strange problem looks like = is not working as it should be. I got code below:
Dim lineText As String
For Each p In WordDoc.Paragraphs
lineText = p.Range.Text
If lineText = "" Then GoTo Dalej
.....
even if i do:
lineText = ""
If lineText = "" Then GoTo Dalej
its not going to Dalej but going next. Looks like its not problem with code but with operators i got similar problem with <>. I tried to workaround tht with InStr or StrComp but its doing completly not as it should be like something inside excel has been changed with application itself. Do you have any idea what this could be?
This is full code:
Sub Sprawdz_Pola_Korespondencji_Click()
Application.ScreenUpdating = True
Dim RowNr As Integer
Dim EWS As Worksheet
RowNr = 30
Set EWS = Sheets("Arkusz do wypełnienia")
Dim FileName As Variant, wb As Workbook
FileName = Application.GetOpenFilename(FileFilter:="Word File (*.docx),*.docx", Title:="Select File To Be Opened")
If FileName = False Then Exit Sub
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = False
Set WordDoc = WordApp.Documents.Open(FileName)
Dim p As Paragraph
If lineText = "" Then GoTo Dalej
If InStr(lineText, PoleExcel) Then
EWS.Cells(5, X).Interior.ColorIndex = 18
Else
EWS.Cells(5, X).Interior.ColorIndex = 3
End If
Dalej:
Next p
Nastepna:
Loop Until EWS.Cells(RowNr, X) = "KONIEC"
'EWS.Activate 'WordDoc.Activate '<============================================================
WordDoc.Close savechanges:=False 'or false
WordApp.Quit
Set WordDoc = Nothing
Set WordApp = Nothing
End Sub
Public Function ReplaceSpaces(strInput As String) As String
' Replaces spaces in a string of text with underscores
Dim Result As String
Result = strInput
If InStr(strInput, " ") > 0 Then
Result = Replace(strInput, " ", "_")
End If
ReplaceSpaces = Result
End Function
You need to write:
Next p
Dalej:
instead. (i.e. switch round the Next p and Dalej:). Currently the label is inside the for loop.
But, it would be far better to use Exit For instead of the GoTo. Doing this means you don't need to maintain a label.
GoTo statements are notoriously brittle.
To strip out the CR do this:
lineText = replace(lineText, chr(13), "")