I have string "ololo123".
I need get position of first digit - 1.
How to set mask of search ?
Here is a lightweight and fast method that avoids regex/reference additions, thus helping with overhead and transportability should that be an advantage.
Public Function GetNumLoc(xValue As String) As Integer
For GetNumLoc = 1 To Len(xValue)
If Mid(xValue, GetNumLoc, 1) Like "#" Then Exit Function
Next
GetNumLoc = 0
End Function
Something like this should do the trick for you:
Public Function GetPositionOfFirstNumericCharacter(ByVal s As String) As Integer
For i = 1 To Len(s)
Dim currentCharacter As String
currentCharacter = Mid(s, i, 1)
If IsNumeric(currentCharacter) = True Then
GetPositionOfFirstNumericCharacter = i
Exit Function
End If
Next i
End Function
You can then call it like this:
Dim iPosition as Integer
iPosition = GetPositionOfFirstNumericCharacter("ololo123")
Not sure on your environment, but this worked in Excel 2010
'Added reference for Microsoft VBScript Regular Expressions 5.5
Const myString As String = "ololo123"
Dim regex As New RegExp
Dim regmatch As MatchCollection
regex.Pattern = "\d"
Set regmatch = regex.Execute(myString)
MsgBox (regmatch.Item(0).FirstIndex) ' Outputs 5
I actually have that function:
Public Function GetNumericPosition(ByVal s As String) As Integer
Dim result As Integer
Dim i As Integer
Dim ii As Integer
result = -1
ii = Len(s)
For i = 1 To ii
If IsNumeric(Mid$(s, i, 1)) Then
result = i
Exit For
End If
Next
GetNumericPosition = result
End Function
You could try regex, and then you'd have two problems. My VBAfu is not up to snuff, but I'll give it a go:
Function FirstDigit(strData As String) As Integer
Dim RE As Object REMatches As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.Pattern = "[0-9]"
End With
Set REMatches = RE.Execute(strData)
FirstDigit = REMatches(0).FirstIndex
End Function
Then you just call it with FirstDigit("ololo123").
If speed is an issue, this will run a bit faster than Robs (noi Rob):
Public Sub Example()
Const myString As String = "ololo123"
Dim position As Long
position = GetFirstNumeric(myString)
If position > 0 Then
MsgBox "Found numeric at postion " & position & "."
Else
MsgBox "Numeric not found."
End If
End Sub
Public Function GetFirstNumeric(ByVal value As String) As Long
Dim i As Long
Dim bytValue() As Byte
Dim lngRtnVal As Long
bytValue = value
For i = 0 To UBound(bytValue) Step 2
Select Case bytValue(i)
Case vbKey0 To vbKey9
If bytValue(i + 1) = 0 Then
lngRtnVal = (i \ 2) + 1
Exit For
End If
End Select
Next
GetFirstNumeric = lngRtnVal
End Function
An improved version of spere's answer (can't edit his answer), which works for any pattern
Private Function GetNumLoc(textValue As String, pattern As String) As Integer
For GetNumLoc = 1 To (Len(textValue) - Len(pattern) + 1)
If Mid(textValue, GetNumLoc, Len(pattern)) Like pattern Then Exit Function
Next
GetNumLoc = 0
End Function
To get the pattern value you can use this:
Private Function GetTextByPattern(textValue As String, pattern As String) As String
Dim NumLoc As Integer
For NumLoc = 1 To (Len(textValue) - Len(pattern) + 1)
If Mid(textValue, NumLoc, Len(pattern)) Like pattern Then
GetTextByPattern = Mid(textValue, NumLoc, Len(pattern))
Exit Function
End If
Next
GetTextByPattern = ""
End Function
Example use:
dim bill as String
bill = "BILLNUMBER 2202/1132/1 PT2200136"
Debug.Print GetNumLoc(bill , "PT#######")
'Printed result:
'24
Debug.Print GetTextByPattern(bill , "PT#######")
'Printed result:
'PT2200136
Related
I am doing a VBA Macro for Excel and I need to get the part of a file path that matches with a specific string.
I mean, I have a Variant called FileInfo that contains the path of the Workbook that I am using at that moment (inside a For), for example, Variant may look like:
C:\Users\myUser\Desktop\SVN-Folder\trunk\G\INC20825\Estimación Temporal_v01r00.xlsx
I want to make a function that returns only the part of the path that matches with "INC*" and if the path doesn't have that match, return null.
So the function in this case may return: INC20825
I tried with this but did not work
'This function returns the INC folder where is contained
Function INCFolder(FileInfo As Variant)
Dim i As Integer
If FileInfo Like "INC*" Then
i = InStr(FileInfo, "INC")
INCFolder = Mid(FileInfo, i, 8)
Else
INCFolder = Null
End If
End Function
EDIT with partial solution:
I made it working to get the 8 characters of INC* with the following code:
'This function returns the INC folder where is contained
Function INCFolder(FileInfo As Variant)
Dim i As Integer
i = InStr(FileInfo, "INC")
If i = 0 Then
INCFolder = Null
Else
INCFolder = Mid(FileInfo, i, 8)
End If
End Function
Problems will come when INC is bigger or smaller than 8
You can use Split to seperate your \ from your full path to PathArr array elements, and then loop through PathArr elements and look for "INC".
The code below will give you flexibility with the number of characters you have for "INC".
Code
Option Explicit
Sub test()
Const FullName = "C:\Users\myUser\Desktop\SVN-Folder\trunk\G\INC20825\Estimación Temporal_v01r00.xlsx"
Dim INCSection As String
INCSection = INCFolder(FullName)
End Sub
Function INCFolder(FileInfo As Variant) As String
Dim i As Long
Dim PathArr As Variant
If FileInfo Like "*INC*" Then
PathArr = Split(FileInfo, "\") ' split folders to array
For i = 0 To UBound(PathArr) ' loop through array and look for "*INC*"
If PathArr(i) Like "*INC*" Then
INCFolder = PathArr(i)
Exit Function
End If
Next i
Else
INCFolder = "Error!"
End If
End Function
Just add one more * in the Like:
Option Explicit
Public Const pathName = "C:\Folder\trunk\G\INC20825\Estimación Temporal_v01r00.xlsx"
Function INCFolder(FileInfo As Variant)
Dim i As Long
If FileInfo Like "*INC*" Then
i = InStr(FileInfo, "INC")
INCFolder = Mid(FileInfo, i, 8)
Else
INCFolder = False
End If
End Function
Just the alternate way to get the result
Function INCFolder(FileInfo As Variant)
If FileInfo Like "*INC*" Then
INCFolder = Mid(WorksheetFunction.Substitute(Mid(FileInfo, InStr(FileInfo, "\INC"), Len(FileInfo)), "\", "|", 2), 2, WorksheetFunction.Search("|", WorksheetFunction.Substitute(Mid(FileInfo, InStr(FileInfo, "\INC"), Len(FileInfo)), "\", "|", 2)) - 2)
Else
INCFolder = Null
End If
End Function
This is my code to get right side of string specifying char separator and either to keep separator within string or not. Possibility also to specify if just last occurence of char separator or manually define it. My question is how to make same version but this time to get right side of string instead of left?
Public Shared Function GetLetSideStringByChar(splitterChar As String, searchingWord As String, keepCharAsWell As Boolean, lastindexof As Boolean, splitterCharPosition As Integer) As String
Dim index As Integer
Select Case lastindexof
Case False
index = GetNthIndex(searchingWord, splitterChar, splitterCharPosition)
Case True
index = searchingWord.LastIndexOf(splitterChar)
End Select
If index > 0 Then
If keepCharAsWell Then
searchingWord = searchingWord.Substring(0, index + splitterChar.Length)
Else
searchingWord = searchingWord.Substring(0, index)
End If
Else
searchingWord = String.Empty
End If
Return searchingWord
End Function
'jesli n separator nie odnalzeiony bedzie return -1, np jesli charseparator = . i damy n = 2 a word bedzie mial tlko jedna . to -1
Public Shared Function GetNthIndex(searchingWord As String, charseparator As Char, n As Integer) As Integer
Dim count As Integer = 0
For i As Integer = 0 To searchingWord.Length - 1
If searchingWord(i) = charseparator Then
count += 1
If count = n Then
Return i
End If
End If
Next
Return -1
End Function
I had written a code for your previous problem that you deleted.
To make the code more understandable I used an Enum to specify right or left:
Public Enum Direction
Left = 0
Right = 1
End Enum
then you can call the function like this:
Console.WriteLine(StringExtract("5345.342.323.323#$%", Direction.Right, 2, False))
Here the Seperator Index represents the dot number (starting at 1)
For Example:
648674.2327.12 first dot is 1 second is 2
And here is the function, I'm sure it could be shortened:
Public Function StringExtract(ByVal MyStr As String, ByVal Side As Direction, ByVal SeperatorIndex As Integer, ByVal SeperatorKeep As Boolean) As String
Dim MySubs() As String = MyStr.Split(".".ToCharArray, StringSplitOptions.RemoveEmptyEntries)
Dim IndexOfSplit As Integer
Dim MyResult As String = ""
If Side = Direction.Left Then
IndexOfSplit = SeperatorIndex - 1
For i As Integer = IndexOfSplit To 0 Step -1
MyResult = MyResult.Insert(0, MySubs(i) & ".")
Next
If SeperatorKeep = False Then
MyResult = MyResult.Remove(MyResult.LastIndexOf("."), 1)
End If
Else
IndexOfSplit = SeperatorIndex
For i As Integer = IndexOfSplit To MySubs.Length - 1
MyResult = MyResult & MySubs(i) & "."
Next
If SeperatorKeep = False Then
MyResult = MyResult.Remove(MyResult.LastIndexOf("."), 1)
Else
MyResult = "." & MyResult.Remove(MyResult.LastIndexOf("."), 1)
End If
End If
Return MyResult
End Function
Output example:
Input 1:
StringExtract("5345.342.323.323#$%", Direction.Right, 2, False)
Output 1:
323.323#$%
Input 2:
StringExtract("5345.342.323.323#$%", Direction.Right, 3, True)
Output 2:
.323#$%
Input 3:
StringExtract("4.34!2.3323.", Direction.Left, 2, True)
Output 3:
4.34!2.
PS: If anyone has any suggestions to shorten the function let me know I'm happy to learn.
I searched the website but was not succesfful and tried doing some research on this but facing with " Type Mismatch" error.
I declared an array as integer type but the FILTER function seems to work only with STRING's. Can you please let me know how I can use the FILTER function for integers?
If UBound(Filter(CntArr(), count)) > 0 Then
msgbox "found"
End If
as i understand you need to know if specified count present in array. You can use for loop for it:
Dim found as Boolean
found = False
For i = 0 To UBound (CntArr())
If CntArr(i) = count Then
found = True
Exit For
End If
Next i
If found Then msgbox "found" End If
Below I have created IsIntegerInArray() function that returns boolean. Follow the two Subs for an example of integer array declaration. Declaring array as Integer should also prevent some unnecessary bugs caused by implicit data conversion.
Sub test_int_array()
Dim a() As Integer
ReDim a(3)
a(0) = 2
a(1) = 15
a(2) = 16
a(3) = 8
''' expected result: 1 row for each integer in the array
Call test_printing_array(a)
End Sub
Sub test_printing_array(arr() As Integer)
Dim i As Integer
For i = 1 To 20
If IsIntegerInArray(i, arr) Then
Debug.Print i & " is in array."
End If
Next i
End Sub
Function IsIntegerInArray(integerToBeFound As Integer, arr() As Integer) As Boolean
Dim i As Integer
''' incorrect approach:
''' IsIntegerInArray = (UBound(Filter(arr, integerToBeFound)) > -1) ' this approach searches for string, e.g. it matches "1" in "12"
''' correct approach:
IsIntegerInArray = False
For i = LBound(arr) To UBound(arr)
If arr(i) = integerToBeFound Then
IsIntegerInArray = True
Exit Function
End If
Next i
End Function
Which are the combinations that the sum of each digit is equal to 8 or less, from 1 to 88,888,888?
For example,
70000001 = 7+0+0+0+0+0+0+1 = 8 Should be on the list
00000021 = 0+0+0+0+0+0+2+1 = 3 Should be on the list.
20005002 = 2+0+0+0+5+0+0+2 = 9 Should not be on the list.
Sub Comb()
Dim r As Integer 'Row (to store the number)
Dim i As Integer 'Range
r = 1
For i = 0 To 88888888
If i = 8
'How can I get the sum of the digits on vba?
ActiveSheet.Cells(r, 1) = i
r = r + 1
End If
Else
End Sub
... Is this what you're looking for?
Function AddDigits(sNum As String) As Integer
Dim i As Integer
AddDigits = 0
For i = 1 To Len(sNum)
AddDigits = AddDigits + CInt(Mid(sNum, i, 1))
Next i
End Function
(Just remember to use CStr() on the number you pass into the function.
If not, can you explain what it is you want in a bit more detail.
Hope this helps
The method you suggest is pretty much brute force. On my machine, it ran 6.5min to calculate all numbers. so far a challenge I tried to find a more efficient algorithm.
This one takes about 0.5s:
Private Const cIntNumberOfDigits As Integer = 9
Private mStrNum As String
Private mRng As Range
Private Sub GetNumbers()
Dim dblStart As Double
Set mRng = Range("a1")
dblStart = Timer
mStrNum = Replace(Space(cIntNumberOfDigits), " ", "0")
subGetNumbers 8
Debug.Print (Timer - dblStart) / 10000000, (Timer - dblStart)
End Sub
Private Sub subGetNumbers(intMaxSum As Integer, Optional intStartPos As Integer = 1)
Dim i As Integer
If intStartPos = cIntNumberOfDigits Then
Mid(mStrNum, intStartPos, 1) = intMaxSum
mRng.Value = Val(mStrNum)
Set mRng = mRng.Offset(1)
Mid(mStrNum, intStartPos, 1) = 0
Exit Sub
End If
For i = 0 To intMaxSum
Mid(mStrNum, intStartPos, 1) = CStr(i)
subGetNumbers intMaxSum - i, intStartPos + 1
Next i
Mid(mStrNum, intStartPos, 1) = 0
End Sub
It can be sped up further by about factor 10 by using arrays instead of writing directly to the range and offsetting it, but that should suffice for now! :-)
As an alternative, You can use a function like this:
Function isInnerLowr8(x As Long) As Boolean
Dim strX As String, inSum As Long
isInnerLowr8 = False
strX = Replace(CStr(x), "0", "")
For i = 1 To Len(strX)
Sum = Sum + Val(Mid(strX, i, 1))
If Sum > 8 Then Exit Function
Next i
isInnerLowr8 = True
End Function
Now change If i = 8 to If isInnerLowr8(i) Then.
How to make a style as a bookmark in word 2010?
You won't be able to use most of the text in the document as the bookmark name. It is just illegal to use certain characters in a bookmark name in Word/VBA. It may be possible to add such characters in bookmark names in an XML format of the document, so if it is required, you can ask a separate question.
This feels like way too much code to post on SO. You really need to explain what framework you have in place and tell us where your hurdles are. We can't do this again. "Works for me". If you have any questions though don't hesitate to ask.
Run the "RunMe" macro at the bottom.
Private Function IsParagraphStyledWithHeading(para As Paragraph) As Boolean
Dim flag As Boolean: flag = False
If InStr(1, para.Style, "heading", vbTextCompare) > 0 Then
flag = True
End If
IsParagraphStyledWithHeading = flag
End Function
Private Function GetTextRangeOfStyledParagraph(para As Paragraph) As String
Dim textOfRange As String: textOfRange = para.Range.Text
GetTextRangeOfStyledParagraph = textOfRange
End Function
Private Function BookmarkNameAlreadyExist(bookmarkName As String) As Boolean
Dim bookmark As bookmark
Dim flag As Boolean: flag = False
For Each bookmark In ActiveDocument.Bookmarks
If bookmarkName = bookmark.name Then
flag = True
End If
Next
BookmarkNameAlreadyExist = flag
End Function
Private Function CreateUniqueBookmarkName(bookmarkName As String)
Dim uniqueBookmarkName As String
Dim guid As String: guid = Mid$(CreateObject("Scriptlet.TypeLib").guid, 2, 36)
guid = Replace(guid, "-", "", , , vbTextCompare)
uniqueBookmarkName = bookmarkName & guid
CreateUniqueBookmarkName = uniqueBookmarkName
End Function
Private Function BookmarkIt(rng As Range, bookmarkName As String)
Dim cleanName As String: cleanName = MakeValidBMName(bookmarkName)
If BookmarkNameAlreadyExist(cleanName) Then
cleanName = CreateUniqueBookmarkName(cleanName)
End If
ActiveDocument.Bookmarks.Add name:=cleanName, Range:=rng
End Function
''shamelessly stolen from gmaxey at http://www.vbaexpress.com/forum/showthread.php?t=37674
Private Function MakeValidBMName(strIn As String)
Dim pFirstChr As String
Dim i As Long
Dim tempStr As String
strIn = Trim(strIn)
pFirstChr = Left(strIn, 1)
If Not pFirstChr Like "[A-Za-z]" Then
strIn = "A_" & strIn
End If
For i = 1 To Len(strIn)
Select Case Asc(Mid$(strIn, i, 1))
Case 49 To 58, 65 To 90, 97 To 122
tempStr = tempStr & Mid$(strIn, i, 1)
Case Else
tempStr = tempStr & "_"
End Select
Next i
tempStr = Replace(tempStr, " ", " ")
MakeValidBMName = tempStr
End Function
Sub RunMe()
Dim para As Paragraph
Dim textOfPara As String
For Each para In ActiveDocument.Paragraphs
If IsParagraphStyledWithHeading(para) Then
textOfPara = GetTextRangeOfStyledParagraph(para)
If para.Range.Bookmarks.Count < 1 Then
BookmarkIt para.Range, textOfPara
End If
End If
Next
End Sub