Extract phone numbers from comments - excel

I have column with comments in it (more then 5000 cases).
Those comments have text, numbers, date, everything.
I need to get phone number out of those comments.
Phone numbers are in random places for every comment, so LEFT,MID or RIGHT will not work
The closest result that I have reached is with Kutools =EXTRAXTNUMBERS() ...... but I get a line of numbers which includes date, ID`s, etc.
Would prefer a formula. :)
Two sample comments below, required phone numbers are in bold
Thursday, February 2, 2017 2:37 PM Coordinated Universal Time .3868 67076939 ,pers .pārv.Tatjana Call outcome chosen: Noruna citā laikā - 2017-02-03 07:15 Wednesday, February 8, 2017 8:18 AM Coordinated Universal Time .3868 nr.67074071-neeksistē,personāla daļas vad.Tatjana neatbild,arī nr.67076600 Call outcome chosen: Neceļ Friday, February 10, 2017 7:15 AM Coordinated Universal Time * .3868 *** piezv ap 13 Call outcome chosen: Noruna citā laikā - 2017-02-10 11:15
Thursday, February 2, 2017 11:15 AM Coordinated Universal Time 4213 zvanīt 66119908 Call outcome chosen: Noruna citā laikā - 2017-02-07 09:00 Tuesday, February 14, 2017 12:59 PM Coordinated Universal Time .4532 * anita#dzintarniece#rtp.lv Call outcome chosen: Turpināt internetā

This small UDF() will return all the 8 digit numeric sub-strings in a string:
Public Function PHNum(s As String) As String
Dim L As Long, i As Long, temp As String
Dim CH As String
L = Len(s)
temp = ""
PHNum = ""
For i = 1 To L
CH = Mid(s, i, 1)
If IsNumeric(CH) Then
temp = temp & CH
If Len(temp) = 8 Then
PHNum = PHNum & vbCrLf & temp
End If
Else
temp = ""
End If
Next i
End Function
Note:
To get the stacked format in the output cell, format it to wrap on.

Regexp Solution
This UDF extracts to you the phone numbers from a Text, as an array. You can eventually use Join to transform it into a csv string, or you can paste the array into a range of cells.
Function extractPhones(s As String) As String()
Dim i As Long, matches, match, ret
With CreateObject("VBScript.Regexp")
.Global = True
.Pattern = "\W[26]\d{7}\W"
Set matches = .Execute(s)
End With
ReDim ret(1 To matches.Count) As String
For Each match In matches
i = i + 1
ret(i) = Mid(match, 2, Len(match) - 2)
Next
extractPhones = ret
End Function
It uses a regular expression that matches phone number with these specs:
are exactly 8 digits
start by 6 or 2
are not preceded or followed by an alphanumeric letter, but by blanks or punctuation characters.

Using an UDF you can accomplish this by using the following code:
To use it:
Press ALT + F11
Insert Module
Paste Code
In Excel Sheet, use this formula =get_phone("CELL_WITH_NUMBER_HERE") to get the first sequence of 8 digits in your cell.
Code:
Public Function get_phone(cell As Range)
Dim s As String
Dim i As Integer
Dim num
Dim counter As Integer
'get cell value
s = cell.Value
'set the counter
counter = 0
'loop through the entire string
For i = 1 To Len(s)
'check to see if the character is a numeric one
If IsNumeric(Mid(s, i, 1)) = True Then
'add it to the number
num = num + Mid(s, i, 1)
counter = counter + 1
'check if we've reached 8 digits
If counter = 8 Then
get_phone = num
Exit Function
End If
Else
'was not numeric so reset counter and answer
counter = 0
num = ""
End If
Next i
End Function
Example Image:

Another regexp option that returns all matches to a single cell
See https://regex101.com/r/Hdv65h/1
Function StrPhone(strIn As String) As String
Dim objRegexp As Object
Set objRegexp = CreateObject("VBScript.Regexp")
With objRegexp
.Global = True
.Pattern = ".*?(\d{8})|.*$"
StrPhone = Trim(.Replace(strIn, "$1 "))
End With
End Function

There is add-on in Excel that I used in the past for regular expressions (http://seotoolsforexcel.com/regexpfind/). In your case, it could be complicated as you don't know how many times a phone number will appear in your cell. For these cases I suggest you use the VBA scripts that have been provided by other users.

Related

How can i can truncate upto certain characters in Excel VBA?

I have an excel sheet with some rows of descriptions in a single column, what I am aiming is to get a vba that would go though all those rows of descriptions and truncate it upto certain character limit for example 30 characters and if the truncation stops at 30 character in the middle of the word then I want the complete word(could extend beyond 30 characters in this case).
I tried to do this with the VBA code below, but I am not able to get what I am looking for.
Function foo(r As Range)
Dim sentence As Variant
Dim w As Integer
Dim ret As String
' assign this cell's value to an array called "sentence"
sentence = Split(r.Value, " ")
' iterate each word in the sentence
For w = LBound(sentence) To UBound(sentence)
' trim to 6 characters:
sentence(w) = Left(sentence(w), 6)
Next
' Join the array back to a string/sentence
ret = Join(sentence, " ")
'Make sure the sentence is max 20 chars:
ret = Left(ret, 20)
'return the value to your function expression:
foo = ret
End Function
I expect the code to go through all the rows of a specific column and truncate it upto 30 characters and if the truncation stops in the middle of the word, then it should keep that word.
Since you tagged it for a formula
=LEFT(A1,FIND(" ",A1,30)-1)
I think you're looking for the instr() function. This could give you the first space-character after position 30.
You would get the following:
Dim SpacePosition as Integer
'return the position for the first space-character after position 29
SpacePosition = Instr(30, r.value," ")
if SpacePosition <> 0 then
'fill ret with the substring up to the first space after position 29
ret = left(r.value, SpacePosition - 1)
else
'if there is no space-character (after position 29) then take the whole string
ret = r.value
end if
Hope that helps.
Best & brilliant solution by #scott Craner. However, In you VBA code you may Change the followings to get required result
'Join the array back to a string/sentence
'ret = Join(sentence, " ")
ret = ""
For w = LBound(sentence) To UBound(sentence)
' trim to 6 characters:
sentence(w) = Left(sentence(w), 6)
ret = ret & IIf(Len(ret) > 0, " ", "") & sentence(w)
If Len(ret) >= 30 Then Exit For
Next w
'Make sure the sentence is max 20 chars:
' ret = Left(ret, 20)
Public Function foo(r As Range, length As Integer) As String
If Len(r.Value) <= length Then
foo = r.Value
Else
foo = Left(r.Value, 1 + length)
foo = RTrim(Left(foo, InStrRev(foo, " ")))
End If
End Function
I suppose you would want to run that by passing 20 as the 2nd parameter
Loop rows from sheet 1, column A starting from row 1:
Option Explicit
Sub test()
Dim Lastrow As Long, i As Long
With ThisWorkbook.Worksheets("Sheet1")
Lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 1 To Lastrow
'Insert Code
Next i
End With
End Sub

Extract 5 Digit Number From a String with Excel-VBA

I'm facing a problem as a non dev. I have a column in Excel that contains info as such:
46843 xxxx xxx x
xxxx 65483 xxxx
xxxx xxx 65432 xxxxx 4 xx
"x" being normal caracters.
What I want is to be able to extract only the numbers of five digits only.
I started something like this but struggle to put a loop so that it scans all the string:
Function test()
val_in = "rue 4 qsdqsd CURIE 38320 EYBENS"
Filte = Left(val_in, 5)
If IsNumeric(Filte) Then
test = Left(val_in, 5)
Else
sp1 = InStr(1, val_in, " ")
sp2 = InStr(sp1 + 1, val_in, " ")
spt = sp2 + sp1
If spt > 5 Then
extr = Mid(val_in, spt, 5)
End If
End If
End Function
How could I turn the part after "Else" into a loop so that it would scan every space of the string and extract only the numbers that contains 5 digits?
Using regex
Option Explicit
Public Function GetNumbers(ByVal rng As Range) As Variant
Dim arr() As String, i As Long, matches As Object, re As Object
Set re = CreateObject("VBScript.RegExp")
With re
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = "\b\d{5}\b"
If .test(rng.Value) Then
Set matches = .Execute(rng.Value)
ReDim arr(0 To matches.Count - 1)
For i = LBound(arr) To UBound(arr)
arr(i) = matches(i)
Next i
Else
arr(i) = rng.Value
End If
End With
GetNumbers = Join(arr, ",")
End Function
Data:
If there is more than one match a comma separated list is returned.
Sub TestMe()
Dim valIn As String
valIn = "rue 4 qsdqsd CURIE 38320 EYBENS 43443"
Dim i As Long
Dim splitted As Variant
splitted = Split(valIn)
For i = LBound(splitted) To UBound(splitted)
If IsNumeric(splitted(i)) And Len(splitted(i)) = 5 Then
Debug.Print splitted(i)
End If
Next i
End Sub
Considering that in your example you mean that the 5 digit numbers are splitted by space, the above works. It splits the string by space to an array and loops through the elements of the array. If the element is with 5 chars and is numeric, it prints it.
If the rule for the spaces is not something that one can count on, here is a different implementation:
Sub TestMe()
Dim valIn As String
valIn = "44244rue4qsdqsdCURIE383201EYBENS43443"
Dim i As Long
For i = 1 To Len(valIn) - 4
If IsNumeric(Mid(valIn, i, 5)) Then
Debug.Print Mid(valIn, i, 5)
End If
Next i
End Sub
It starts looping through the string, checking whether each 5 chars are numeric. When you have numeric 6 chars, it gives two results - 1 to 5 and 2 to 6. Thus 383201 is "translated" as the following 2:
38320
83201
If you have always space between words/numbers then this should do
Sub test()
Dim TestStr As String
Dim Temp As Variant
Dim i As Long, FoundVal As Long
TestStr = "rue 4 qsdqsd CURIE 38320 EYBENS"
Temp = Split(TestStr, " ")
For i = 0 To UBound(Temp)
If Len(Trim(Temp(i))) = 5 And IsNumeric(Temp(i)) Then
FoundVal = Temp(i)
MsgBox FoundVal
End If
Next i
End Sub
From the solution you are trying to apply (creating custom function in VBA) I understand that you actually need to use it in a formula.
To find number with five digits from cell A1 you can use the following formula without VBA:
=IF(ISERROR(FIND("0"&REPT("1",5)&"0",CONCAT(0+(ISNUMBER(-MID(" "&A1&" ",ROW(INDIRECT("1:"&LEN(A1)+2)),1))+0)))),"",MID(A1,FIND("0"&REPT("1",5)&"0",CONCAT(0+(ISNUMBER(-MID(" "&A1&" ",ROW(INDIRECT("1:"&LEN(A1)+2)),1))+0))),5))
To search for other number of digits change the three occurrences of number 5 to your desired digits count in the formula.

Deleting everything after the second occurrence of a number

Quick question, if I want to delete everything after the second occurrence of a number:
i.e -
I have:
1105 Bracket Ave. Suite 531 Touche
5201 Used St. 1351 Bored Today
I want:
1105 Bracket Ave. Suite 531
5201 Used St. 1351
is there a simple formula or VBA I would use for this?
Here is a UDF using VBA's regular expression engine to remove all after the second integer.
Option Explicit
Function FirstTwoNumbers(S As String) As String
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.Pattern = "(\d+\D+\d+).*"
FirstTwoNumbers = .Replace(S, "$1")
End With
End Function
If there is only a single integer, it will return the entire string.
If the numbers might be decimal numbers, will need to modify .Pattern
And here is another UDF using only native VBA methods:
Function FirstTwo(S As String) As String
Dim V
Dim tS As String
Dim I As Long, numNumbers As Long
V = Split(S)
Do Until numNumbers = 2
tS = tS & Space(1) & V(I)
I = I + 1
If IsNumeric(V(I - 1)) Then numNumbers = numNumbers + 1
Loop
FirstTwo = Mid(tS, 2)
End Function
and finally, a formula with no particular assumptions:
=LEFT(A1,FIND(CHAR(1),SUBSTITUTE(A1," ",CHAR(1),LOOKUP(2,1/ISNUMBER(-TRIM(MID(SUBSTITUTE(A1," ",REPT(" ",99)),seq_99,99))),seq))))
seq and seq99 are Named Formulas Formula ► Define Name
seq Refers to: =ROW(INDEX($1:$255,1,1):INDEX($1:$255,255,1))
seq_99 Refers to: =IF(ROW(INDEX($1:$255,1,1):INDEX($1:$255,255,1))=1,1,(ROW(INDEX($1:$255,1,1):INDEX($1:$255,255,1))-1)*99)
This solution is with these assumptions:-
First occurrence of a number will not have a length > 10
There will atleast a distance of 10 or 10 alphabets including spaces between first and second number
There will always be a 'space' existing after second number
There will always be a second number present in the string
Try this:-
=TRIM(MID(A1,1,FIND(" ",A1,MIN(FIND({0,1,2,3,4,5,6,7,8,9},A1&"0123456789",MIN(FIND({0,1,2,3,4,5,6,7,8,9},A1&"0123456789"))+10)))))
Here is a VBA approach, amend range to suit. It puts the answer in the adjacent column
Sub x()
Dim oMatches As Object, r As Range
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "\d+"
For Each r In Range("A1:A5")
If .Test(r) Then
Set oMatches = .Execute(r)
If oMatches.Count > 1 Then
r.Offset(, 1).Value = Left(r, oMatches(1).firstindex + oMatches(1).Length)
Else
r.Offset(, 1).Value = r.Value
End If
Else
r.Offset(, 1).Value = r.Value
End If
Next r
End With
End Sub
You can use the following formula,if A1 is your string,in B1 write:
=LEFT(A1,MAX(IFERROR(ISNUMBER(VALUE(MID(A1,ROW(INDIRECT("1:"&LEN(A1))),1)))*ROW(INDIRECT("1:"&LEN(A1))),0)))
press Ctrl+Shift+Enter at the same time Array Formula
This will read the length of the string and return the Maximum place of numbers (last number in the string) and return the Left() string till this number

For-Next Loop using end of month dates as iteration

I currently have a "For Next" loop that iterates through various years and I want to modify it to loop through dates, specifically the end of each month. My generic code for the year loop is below. Clearly looping through years is relatively easy since you have a start year, which is an integer, and the iteration is 1. Now I want to modify the loop to iterate though various end of month dates. For example, 1/31/2003, 2/28/2003, ......, 12/31/2007. Also, note that for each iteration I create a new worksheet with the name of the current iteration as the name of the worksheet. Again, this is relatively easy for a year but using a date with a "/" complicates things. Does anyone have any ideas for creating a loop using end of month dates as well as creating sheets using dates? I do have an array of the dates so the code could refer to the array within a sheet. And the name of the sheet could be in any format. For example, "mm-dd-yyyy".
Sub YearLoop()
Dim FirstYr As Integer
Dim LastYr As Integer
Dim Sheetname As String
Dim Counter1 As Single
FirstYr = Sheets("Model").Range("ax15").Value
LastYr = Sheets("Model").Range("ax16").Value
Counter1 = 0
For J = FirstYr To LastYr
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = J
Sheetname = J
'do stuff
Counter1 = Counter1+1
Next
End Sub
The DateSerial function produces the end-of-month date of the previous month when you give any month a day of zero.
dim m as integer
for m = 2 to 13
debug.print dateserial(2016, m, 0)
next m
The characters that can't be used in sheet names are ASCII \/[]*:?, but you can use Unicode characters like ⁄∕/
d = #1/31/2003#
While d <= #12/31/2007#
Sheets.Add(, ActiveSheet).Name = Replace(d, "/", ChrW(8260))
d = d + 32
d = d - Day(d)
Wend
Update
Or you can use Jeeped's answer like this:
For m = FirstYr * 12 + 2 To LastYr * 12 + 13
Sheets.Add(, ActiveSheet).Name = Replace(DateSerial(0, m, 0), "/", ChrW(8260))
Next
Public Sub ReadAndDisplay()
' Get Range
Dim rg As Range
Set rg = ThisWorkbook.Worksheets("Returns Calc").Range("C118:C319")
' Create dynamic array
Dim Arr() As Variant
' Read values into array from sheet1
Arr = rg
For Each mark In Arr
Dim CurrentDate1 As Date, DimCurrentDate2 As String
CurrentDate1 = mark
CurrentDate2 = Replace(CurrentDate1, "/", ".")
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = CurrentDate2 & " Rtns"
'do Stuff
Next mark
End Sub

extract and convert string into decimal number using excel VBA

I am new at excel VB and seek assistant in the following problem below:
I have a column A with following values below:
column A
"VL50s"
"M50s"
"H50s"
"VL50s"
"H50s"
I would like to extract the numbers and run the following arithmetic function below into coloumn B.
key:
x is a number
VLx --> (x) + 1
Mx -->(x) + 2
Hx --> (x) + 3
the output should look like the following using the key above:
coloumn B
51
52
53
51
53
I would like to ask how would i go about doing this function in VBA. Thank you for your assistance.
Because you say the number of letter/number combos is much greater than in your example I think this is a problem for VBA and not a worksheet function. A WS function would become to hard to maintain and to beastly very quickly.
I made these 4 functions. The GetCharArray function parses the text of the string you pass it to return that text as an array of characters (even though BA doesn't have a char type just a string type so I am returning a string. Same idea)
Then given that we can call GetNumberFromChars to get the 50 from VL50s and call GetLeftMostLetters to get the VL from VL50s.
Then is some worksheet I made a named range called keys where column 1 of the range is letters like "VL", "H", "M" ... and the corresponding value associated with it is in column 2. It would look like
Col1 Col2
VL 1
M 2
H 3
... ...
We can use the vlookup worksheet function with the Range("keys") and the result of GetLeftMostLetters to find the number that should be added to the result of GetNumberFromChars.
Function GetNewNumber(inString As String) As Double
Dim searchString As String, numberToAddFromKeys As Double, numberToAddToFromCell As Long, cellChars() As String
cellChars = GetCharArray(inString)
searchString = GetLeftMostLetters(cellChars)
numberToAddToFromCell = GetNumberFromChars(cellChars)
'use the keys named range where column 1 is your letters ("VL","H"...)
'and column 2 is the corresponding value for that letter set
numberToAddFromKeys = WorksheetFunction.VLookup(searchString, Range("keys"), 2, 0)
GetNewNumber = CDbl(numberToAddFromKeys) + CDbl(numberToAddToFromCell)
End Function
Function GetNumberFromChars(inChars() As String) As Long
Dim returnNumber As String, i As Long, numberStarted As Boolean
For i = 1 To UBound(inChars)
If IsNumeric(inChars(i)) Then
If Not numberStarted Then numberStarted = True
returnNumber = returnNumber & inChars(i)
Else
If numberStarted Then
'this will ignore that "s" on the end of your sample data
'hopefully that's what you need
GetNumberFromChars = returnNumber
Exit Function
End If
End If
Next
End Function
Function GetLeftMostLetters(inChars() As String) As String
Dim returnString As String, i As Long
For i = 1 To UBound(inChars)
If Not IsNumeric(inChars(i)) Then
returnString = returnString & inChars(i)
Else
GetLeftMostLetters = returnString
End If
Next
End Function
Function GetCharArray(inText As String) As String()
Dim s() As String, i As Long
ReDim s(1 To Len(inText))
For i = 1 To UBound(s)
s(i) = Mid$(inText, i, 1)
Next
GetCharArray = s
End Function
So it can be used as such...
Dim cell As Range, rng As Range
'set this range to your actual range.
Set rng = Sheets("your sheet name").Range("A1:A5")
For Each cell In rng
'put this resulting value wherever you want.
Debug.Print GetNewNumber(cell.Value)
Next cell
You don't even have to use VBA for that, you can use a (very ugly) formula to determine this:
=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A1, "VL",""), "M",""), "H", ""),
"s", "") + IF(LEFT(A1, 2) = "VL", 1, IF(LEFT(A1, 1) = "M", 2,
IF(LEFT(A1,1) = "H", 3, 0)))
In reality this formula should be on one line, but I've broken it up here so that it's readable. Place the formula in cell B1, and then copy it down to any other cells you need. It strips out all instances of "VL", "M", "H" and "s", and then adds the extra number based on the left 1 or 2 characters of the A cell.
This will return the first number found in the input value:
Function GetNumber(val)
Dim re As Object
Dim allMatches
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "(\d+)"
re.ignorecase = True
re.Global = True
Set allMatches = re.Execute(val)
If allMatches.Count > 0 Then
GetNumber = allMatches(0)
Else
GetNumber = ""
End If
End Function
EDIT: just noticed your question title says "decimal" numbers - will your values have any decimal places, or all they all whole numbers?

Resources