Wildcard use within Replace - Specific number of characters - vba

I have the following problem:
I want to replace a link to another sheet. This link changes during the Macro from "MeasData!E10" to "MeasData_XXX!E10" (XXX any number) and can be any of these during the Macro. Now I want to replace one of these with a Cell of the current sheet.
The problem is, that my cells contain more than one of the strings like above, e.g.:
=MeasData_110!E10*MeasData_110!E15*MeasData_110!E20
When using the Cells.Replace method this will correctly replace MeasData_110!E10 with the set string. However, if the link I am looking for is not in the first position, e.g.:
=MeasData_110!E20*MeasData_110!E10*MeasData_110!E15
It will be replaced to:
=STRING*MeasData_110!E15
because I am just using a wildcard:
Worksheets(1).Cells.Replace _
What:="MeasData*!E10", Replacement:=STRING
I have not yet found out if there is a wildcard for
a) specific letters
AND
b) specific/variable number of letters (0-4)
Someone got a solution?

I think the quickest way is to use Replace() in a loop:
Sub MM()
Dim foundCell As Excel.Range
Dim foundAddress As String
Dim findString As String
Dim replaceString As String
findString = "MeasData!E10"
replaceString = Range("AD48").Value
Set foundCell = Sheets(1).Cells.Find(What:=findString, LookIn:=xlFormulas, LookAt:=xlPart)
If Not foundCell Is Nothing Then
foundAddress = foundCell.Address
Do
With foundCell
.Formula = Replace(.Formula, findString, replaceString)
End With
Set foundCell = Sheets(1).Cells.FindNext(foundCell)
Loop While Not foundCell Is Nothing
End If
End Sub
Or
You can, if you wish, use the VBScript.RexExp object via late binding like so:
Function ReplaceAllWith(old_string As String, pattern As String, new_string As String) As String
With CreateObject("VBScript.RegExp")
.pattern = pattern
.Global = True
If .Test(old_string) Then
ReplaceAllWith = .Replace(old_string, new_string)
Else
ReplaceAllWith = old_string
End If
End With
End Function
Insert the above into your module, and use like so:
For Each cell In Sheets(1).UsedRange.Cells
If cell.HasFormula Then
cell.Formula = ReplaceAllWith(cell.Formula, "MeasData(_[\d]{3})?!E10", Range("AD48").Value)
End If
Next

If you know the cell numbers you can use the below
Dynamically pass values for the variables Cells1,cells2 and cells3
cells1 = "110!E10"
cells2 = "110!E15"
Cells3 = "110!E20"
str1 = "=MeasData_" & cells1 & "*Measdata_" & cells2 & "*MeasData_" & Cells3
'Debug.Print str1 'Print and verify if you need

Have you tried regular expressions? you need to add reference to Microsoft VBScript regular expressions 5.5 for this
Sub test()
Dim a As String
a = "=MeasData_110!E20*Measdata_110!E10*MeasData_110!E15*Measdata_123!E10"
a = ReplaceNumbers(a, "MeasData_STRING!E10")
MsgBox a
End Sub
Private Function ReplaceNumbers(inputString As String, replacement As String) As String
Pattern = "Meas[dD]ata_([0-9]{1,})\!E10"
output = inputString
Dim re As New RegExp
re.Pattern = Pattern
re.Global = True: re.MultiLine = True
If re.test(inputString) Then
ReplaceNumbers = re.Replace(inputString, replacement)
Else
ReplaceNumbers = inputString
End If
End Function

Related

How to remove part of a string if it is separated by white space only in Excel using a VBA formula?

I have a string in an Excel cell. I need to remove certain words from the string, but only if they are separated by white space. Here is an example:
I have: hello include in the formula for attachment a dog
I want to delete "in", "for" and "a", but only as entire words, not when they are a part of another word (for example "include", "formula", "attachment".)
I should end up with: hello include the formula attachment dog
I tried:
Function REMOVETEXTS(strInput As String, rngFind As Range) As String
Dim strTemp As String
Dim strFind As String
strTemp = strInput
For Each cell In rngFind
strFind = cell.Value
strTemp = Replace(strTemp, strFind, "" "", , , 1)
Next cell
REMOVETEXTS = strTemp
But it removes, for example, "in" from "include". Any advice?
This should work:
Function REMOVETEXTS(strInput As String, rngFind As Range) As String
Dim strTemp As String
strTemp = strInput
Dim cell As Range
Dim strFind As String
For t = 1 To 10 'The For Each loop has to run multiple times in cases the same search-word occurs back to back.
For Each cell In rngFind
strFind = cell.Value
strTemp = Trim(Replace(" " & strTemp & " ", " " & strFind & " ", " "))
Next cell
Next t
REMOVETEXTS = strTemp
End Function
The following uses a regex to do the removal. It has an additional regex pattern at the end to get rid of excessive white space left behind.
The list of words to remove is passed as a comma separated list without white space. Inside the regex function this is converted to a pattern of, in this case, \b(in|for|a)\b. This is essentially an OR list of individual words i.e. either "in", "for" or "a".
There is an optional 3rd parameter that allows you to choose whether to ignore the case of the matched words. It defaults to False.
You can use it in the sheet as an UDF.
VBA:
Option Explicit
Public Sub test()
Dim j As Long, arr()
arr = Array("hello include in the formula for attachment a dog")
For j = LBound(arr) To UBound(arr)
Debug.Print RemoveUnWantedStrings(arr(j), "in,for,a", False)
Next
End Sub
Public Function RemoveUnWantedStrings(ByVal inputString As String, ByVal sPattern As String, Optional ignoreCase As Boolean = False) As Variant
Dim matches As Object, iMatch As Object
With CreateObject("vbscript.regexp")
.Global = True
.MultiLine = True
.ignoreCase = ignoreCase
.Pattern = "\b(" & Replace$(sPattern, ",", "|") & ")\b"
If .test(inputString) Then
' Set matches = .Execute(inputString)
'For Each iMatch In matches
inputString = .Replace(inputString, vbNullString)
' Next iMatch
Else
RemoveUnWantedStrings = inputString
Exit Function
End If
.Pattern = "\s{2,}"
RemoveUnWantedStrings = .Replace(inputString, Chr$(32))
End With
End Function
In sheet UDF:
Regex: Try it here.
/
\b(in|for|a)\b
/
gm
\b assert position at a word boundary (^\w|\w$|\W\w|\w\W)
1st Capturing Group (in|for|a)
1st Alternative in
in matches the characters in literally (case sensitive)
2nd Alternative for
for matches the characters for literally (case sensitive)
3rd Alternative a
a matches the character a literally (case sensitive)
\b assert position at a word boundary (^\w|\w$|\W\w|\w\W)
My function splits up your original string by spaces " ", and then compares each word of the original string against the words in the other specified range and does not include them in the result if they match.
A1 has the string you start with, and A2:A4 have all of the words that you want to exclude ("in", "for", "a").
If you want to have multiple delimiters beyond just spaces, you can just add them to the arguments as one big string. In the example, it would split words by any of the characters in the string " ,=", a space, a comma, or an equals.
Option Explicit
Sub Test()
MsgBox RemoveWholeWords(Range("A1").Value2, Range("A2:A4"), " ,=")
End Sub
Function RemoveWholeWords(ByVal str As String, ByVal remove As Range, ByVal delimiters) As String
Dim i As Long
If Len(delimiters) > 1 Then
For i = 1 To Len(delimiters)
str = Replace(str, Mid(delimiters, i, 1), Right(delimiters, 1))
Next i
End If
Dim words() As String
words = Split(str, Right(delimiters, 1))
Dim removed As Boolean
For i = LBound(words) To UBound(words)
removed = False
Dim cel As Range
For Each cel In remove
If words(i) = cel.Value2 Then removed = True
Next cel
If Not removed And Len(words(i)) > 0 Then
RemoveWholeWords = Trim$(RemoveWholeWords & " " & words(i))
End If
Next i
End Function

Remove white spaces in a single column

I have a sheet that I need to remove the spaces from so I can compare it I am using a function I found on here and a sub that puts all the values into an array (for speed) but I cannot get it to work any idea why. I get a
ByRef argument type mismatch error
Public Function RemoveWhiteSpace(target As String) As String
With New RegExp
.Pattern = "\s"
.MultiLine = True
.Global = True
RemoveWhiteSpace = .Replace(target, vbNullString)
End With
End Function
Sub stringRangeToClean()
Dim r As Variant
Dim i As Long
r = ActiveWorkbook.Sheets("Trent BASE DATA").UsedRange
For i = 2 To UBound(r)
r(i, 10).Value2 = RemoveWhiteSpace(r(i, 10))
Next i
End Sub
now trying this have I realised the col I is actually (I,9)
but im getting an error user defined type error on the RegExp line
Public Function RemoveWhiteSpace(target As String) As String
With New RegExp
.Pattern = "\s"
.MultiLine = True
.Global = True
RemoveWhiteSpace = .Replace(target, vbNullString)
End With
End Function
Sub stringRangeToClean()
Dim r As Variant
Dim i As Long
Dim txt As String
r = ActiveWorkbook.Sheets("Trent BASE DATA").UsedRange
For i = 2 To UBound(r)
txt = r(i, 9)
txt = RemoveWhiteSpace(txt)
Next i
End Sub
Here you go, no regex required for simply removing spaces. Your main challenge is just defining your range which is basic VBA:
Sub tgr()
With ActiveWorkbook.Sheets("Trent BASE DATA")
.Range("J2", .Cells(.Rows.Count, "J").End(xlUp)).Replace " ", vbNullString
End With
End Sub
Try like this:
Public Function RemoveWhiteSpace(target As String) As String
Dim RegExp As Object
Set RegExp = CreateObject("VBScript.RegExp")
With RegExp
.Pattern = "\s"
.MultiLine = True
.Global = True
RemoveWhiteSpace = .Replace(target, vbNullString)
End With
End Function
This way you are using a late binding for the regExp variable, thus neither you nor your users have to add any additional library.
If you want to use the early binding, you should add the Microsoft VBScript Regular Expressions 5.5" library to the ones, that VBA uses. The early binding gives you some time bonus and it provides IntelliSense.
Here the selected answer explains step-by-step how to add the library:
How to use Regular Expressions (Regex) in Microsoft Excel both in-cell and loops
Concerning your code - this is very simply loop, try to do it:
Sub StringRangeToClean()
Dim r As Variant
Dim myCell As Range
r = ActiveWorkbook.Sheets("Trent BASE DATA").UsedRange
For Each myCell In r
myCell = RemoveWhiteSpace(txt)
Next myCell
End Sub
This way every cell would be examined and the white spaces would be removed.

VBA Excel lookup

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...

Find all used references in Excel formula

Below is the example set in Excel,
[column1] [column2]
A1 =C3-C5
A2 =((C4-C6)/C6)
A3 =C4*C3
A4 =C6/C7
A5 =C6*C4*C3
I need to extract the used references in formulas
For example,
for "A1", I simply need to get the C3 and C5.
for A2, I need to get the C4 and C6.
This is an update to:
Will work for local sheet references, but not for references off-sheet. – brettdj May 14 '14 at 11:55
By Using Larrys method, just change the objRegEx.Pattern to:
(['].*?['!])?([[A-Z0-9_]+[!])?(\$?[A-Z]+\$?(\d)+(:\$?[A-Z]+\$?(\d)+)?|\$?[A-Z]+:\$?[A-Z]+|(\$?[A-Z]+\$?(\d)+))
This will:
Search for optional External links: (['].*?['!])?
Search for optional Sheet-reference: ([[A-Z0-9_]+[!])?
Do the following steps in prioritized order:
Search for ranges with row numbers (And optional $): \$?[A-Z]+\$?(\d)+(:\$?[A-Z]+\$?(\d)+)?
Search for ranges without row numbers (And optional $): \$?[A-Z]+:\$?[A-Z]+
Search for 1-cell references (And optional $): (\$?[A-Z]+\$?(\d)+)
Resulting in this:
Sub testing()
Dim result As Object
Dim r As Range
Dim testExpression As String
Dim objRegEx As Object
Set r = Cells(1, 2) ' INPUT THE CELL HERE , e.g. RANGE("A1")
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True
objRegEx.Global = True
objRegEx.Pattern = """.*?""" ' remove expressions
testExpression = CStr(r.Formula)
testExpression = objRegEx.Replace(testExpression, "")
objRegEx.Pattern = "(([A-Z])+(\d)+)" 'grab the address
objRegEx.Pattern = "(['].*?['!])?([[A-Z0-9_]+[!])?(\$?[A-Z]+\$?(\d)+(:\$?[A-Z]+\$?(\d)+)?|\$?[A-Z]+:\$?[A-Z]+|(\$?[A-Z]+\$?(\d)+))"
If objRegEx.test(testExpression) Then
Set result = objRegEx.Execute(testExpression)
If result.Count > 0 Then
For Each Match In result
Debug.Print Match.Value
Next Match
End If
End If
End Sub
Doing this, will give you the values of all possible references, I could think of. (Updated this post, because I needed the problem solved).
This function returns you a comma separated list of source cells (precedents):
Function References(rngSource As Range) As Variant
Dim rngRef As Range
Dim strTemp As String
On Error Resume Next
For Each rngRef In rngSource.Precedents.Cells
strTemp = strTemp & ", " & rngRef.Address(False, False)
Next
If Len(strTemp) 0 Then strTemp = Mid(strTemp, 3)
References = strTemp
End Function
However, please note that you cannot use this as a UDF in the worksheet, as rngRef.Address unfortunately causes a circular reference. However, you can use it in a small procedure to populate another column, e.g.
Sub ShowPrecedents()
Dim rng As Range
'Will paste precedents of A1:A6 into D1:D6
For Each rng In Range("D1:D6")
rng.Value = References(rng.Offset(, -3))
Next
End Sub
Just to provide you an alternative... NOTE THAT THIS will return duplicate result if the cells are called more than once
Sub testing()
Dim result As Object
Dim r As Range
Dim testExpression As String
Dim objRegEx As Object
Set r = Cells(1, 2) ' INPUT THE CELL HERE , e.g. cells("A1")
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True
objRegEx.Global = True
objRegEx.Pattern = """.*""" ' remove expressions
testExpression = CStr(r.Formula)
testExpression = objRegEx.Replace(testExpression, "")
objRegEx.Pattern = "(([A-Z])+(\d)+)" 'grab the address
If objRegEx.test(testExpression) Then
Set result = objRegEx.Execute(testExpression)
If result.Count > 0 Then
For Each Match In result
Debug.Print Match.Value
Next Match
End If
End If
End Sub
Results are stored in "Match.Value"

How to change a cell depending on content in VBA

I have a lot of cells that contain some numbers and other non-relevant characters. For example cell may look like: 65f or as 11,345asd.
My goal is to delete everything but numbers in these cells, so I could use these numbers for further calculations. I have found a lot of similar questions on different sites, but they are quite specific and I still don't understand how to do it properly.
So the question is how to use change cells or maybe even a range of cells depending on contents? I have some ideas how to do it using string function Replace. But nothing that looks good.
Thanks!
Another way using RegExp
Adding Reference
Add a reference to Microsoft VBScript Regular Expressions 5.5. See image below
CODE: Paste this in a module
Option Explicit
Function GetNumbers(rng As Range) As Variant
Dim StrSample As String
Dim myRegExp As RegExp
Dim myMatches As MatchCollection
Dim myMatch As Match
StrSample = rng.Value
Set myRegExp = New RegExp
With myRegExp
.Pattern = "[^0-9]"
.IgnoreCase = True
.Global = True
End With
Set myMatches = myRegExp.Execute(StrSample)
For Each myMatch In myMatches
Debug.Print myMatch.Value
StrSample = Replace(StrSample, myMatch.Value, "")
Next
GetNumbers = StrSample
End Function
SCREENSHOT:
EDIT
Here is a shorter version which doesn't use looping at all.
Function GetNumbers(rng As Range) As Variant
Dim myRegExp As RegExp
Set myRegExp = New RegExp
myRegExp.Pattern = "[^0-9]"
myRegExp.Global = True
GetNumbers = myRegExp.Replace(rng.Value, "")
End Function
A wee function
public function TONUM(str As string) As string
dim i As long
for i = 1 To len(str)
if not mid$(str, i, 1) Like "[0-9]" then mid$(str, i, 1) = "?"
next
TONUM = replace$(str, "?", "")
end function