I have a rather tricky problem. I am trying to split and declare a different parts of a string for further use. Obviously there I have a different delimiters to do that.
Say I wanted to split a standard screw code: DIN912M6x10A2 into it's different parts since each part of that code means specific something.
ScreHead is Left up to first "M" without the delimiter = DIN912
ScrewThickness is "M" included up to "x" excluded = M5
ScrewLenght is "x" excluded up to "A" excluded = 10
ScrewMaterial is "A" included up to the " " or if there's no " " then up to the end of the string = A2
What I have so far codewise is (I am working in 5th column):
Dim ScrewHead As Long
ScrewHead = Split(Cells(i, 5), "M"-1)
Dim ScrewDiameter As Long
ScrewDiameter =Split(i,5),"M", "x"-1)
Dim ScrewLenght As Long
ScrewLenght =Split(i,5),"x"-1, "A"-1)
Dim ScrewMaterial As Long
ScrewMaterial =Split(i,5),"A", " ")
Could someone help me with figuring this one out?
Sounds like a nice job for a regular expression to be honest when you can capture all the parts in their own groups. For example through:
^(.+?)(M\d+)x(\d+)(.+?)(?:\s.*)?$
See the online demo
^ - Start line anchor.
(.+?) - A 1st capture group holding 1+ (lazy) characters upto;
(M\d+) - 2nd Capture group with a literal "M" followed by 1+ (greedy) digits.
x - A literal "x".
(\d+) - A 3rd capture group holding 1+ (greedy) digits.
(.+?) - A 4th capture group holding 1+ (lazy) characters upto;
(?:\s.*)? - An optional non-capture group of a space character with 0+ (greedy) characters.
$ - End line anchor.
Here is a quick code to run to retrieve these groups:
Sub Test()
Dim str As String: str = "DIN912M6x10A2 test"
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "^(.+?)(M\d+)x(\d+)(.+?)(?:\s.*)?$"
If .Test(str) = True Then
For Each Match In .Execute(str)(0).Submatches
Debug.Print Match
Next
End If
End With
End Sub
A more extensive code-example for a better understanding:
Sub Test()
Dim str As String: str = "DIN912M6x10A2 test"
Dim ScrewHead As String, ScrewDiameter As String, ScrewLenght As Long, ScrewMaterial As String
Dim matches
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "^(.+?)(M\d+)x(\d+)(.+?)(?:\s.*)?$"
If .Test(str) = True Then
Set matches = .Execute(str)
ScrewHead = matches(0).Submatches(0)
ScrewDiameter = matches(0).Submatches(1)
ScrewLenght = matches(0).Submatches(2)
ScrewMaterial = matches(0).Submatches(3)
End If
End With
End Sub
Here's a plain VBA based sledgehammer approach. You can adopt the code to suit your requirements.
Public Sub GetDiffPartsofString()
Dim strInput As String, strScrewHead As String, strScrewThck As String, strScrewLeng As String, strScrewMatl As String
Dim i As Long, j As Long
strInput = "DIN912M6x10A2"
j = 1
For i = 1 To Len(strInput)
Select Case Mid(strInput, i, 1)
Case "M"
strScrewHead = Mid(strInput, j, i - 1)
j = i
Case "x"
strScrewThck = Mid(strInput, j, i - j)
j = i
Case "A"
strScrewLeng = Mid(strInput, j + 1, i - j - 1)
strScrewMatl = Mid(strInput, i, Len(strInput))
End Select
Next i
Debug.Print strScrewHead, strScrewThck, strScrewLeng, strScrewMatl
End Sub
Tricky split approach via Val()
Another way leading to Rome:
Sub AnalyzeID()
Dim s As String: s = "DIN912M6x10A2 test"
Dim parts: parts = Split(Replace(Replace(s, "M", "x"), " ", "x"), "x")
'adjust split elements
parts(1) = "M" & parts(1)
parts(3) = Split(parts(2), Val(parts(2)))(1) ' (don't change code line order!)
parts(2) = Val(parts(2))
Debug.Print Join(parts, "|") ' ~~> DIN912|M6|10|A2
End Sub
Output in VB Editor's immediate window
DIN912|M6|10|A2
Related
I have a string in a cell composed of several shorter strings of various lengths with blank spaces and commas in between. In some cases only one or more blanks are in between.
I want to remove every blank space and comma and only leave behind 1 comma between each string element. The result must look like this:
The following doesn't work. I'm not getting an error but the strings are truncated at the wrong places. I don't understand why.
Sub String_adaption()
Dim i, j, k, m As Long
Dim STR_A As String
STR_A = "01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
i = 1
With Worksheets("table")
For m = 1 To Len(.Range("H" & i))
j = 1
Do While Mid(.Range("H" & i), m, 1) = "," And Mid(.Range("H" & i), m - 1, 1) <> Mid(STR_A, j, 1) And m <> Len(.Range("H" & i))
.Range("H" & i) = Mid(.Range("H" & i), 1, m - 2) & Mid(.Range("H" & i), m, Len(.Range("H" & i)))
j = j + 1
Loop
Next m
End With
End Sub
I'd use a regular expression to replace any combination of spaces and comma's. Something along these lines:
Sub Test()
Dim str As String: str = "STRING_22 ,,,,,STRING_1 , , ,,,,,STRING_333 STRING_22 STRING_4444"
Debug.Print RegexReplace(str, "[\s,]+", ",")
End Sub
Function RegexReplace(x_in, pat, repl) As String
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = pat
RegexReplace = .Replace(x_in, repl)
End With
End Function
Just for the sake of alternatives:
Formula in B1:
=TEXTJOIN(",",,TEXTSPLIT(A1,{" ",","}))
The following function will split the input string into pieces (words), using a comma as separator. When the input string has multiple commas, it will result in empty words.
After splitting, the function loops over all words, trims them (remove leading and trailing blanks) and glue them together. Empty words will be skipped.
I have implemented it as Function, you could use it as UDF: If your input string is in B2, write =String_adaption(B2) as Formula into any cell.
Function String_adaption(s As String) As String
' Remove duplicate Commas and Leading and Trailing Blanks from words
Dim words() As String, i As Long
words = Split(s, ",")
For i = 0 To UBound(words)
Dim word As String
word = Trim(words(i))
If word <> "" Then
String_adaption = String_adaption & IIf(String_adaption = "", "", ",") & word
End If
Next i
End Function
P.S.: Almost sure that this could be done with some magic regular expressions, but I'm not an expert in that.
If you have recent Excel version, you can use simple worksheet function to split the string on space and on comma; then put it back together using the comma deliminater and ignoring the blanks (and I just noted #JvdV had previously posted the same formula solution):
=TEXTJOIN(",",TRUE,TEXTSPLIT(A1,{" ",","}))
In VBA, you can use a similar algorithm, using the ArrayList object to collect the non-blank results.
Option Explicit
Function commaOnly(s As String) As String
Dim v, w, x, y
Dim al As Object
Set al = CreateObject("System.Collections.ArrayList")
v = Split(s, " ")
For Each w In v
x = Split(w, ",")
For Each y In x
If y <> "" Then al.Add y
Next y
Next w
commaOnly = Join(al.toarray, ",")
End Function
This preserves the spaces within the smaller strings.
Option Explicit
Sub demo()
Const s = "STRING 22,,,, ,,STRING 1,,,, ,,STRING 333 , , , STRING_22 STRING_44"
Debug.Print Cleanup(s)
End Sub
Function Cleanup(s As String) As String
Const SEP = ","
Dim regex, m, sOut As String, i As Long, ar()
Set regex = CreateObject("vbscript.regexp")
With regex
.Global = True
.MultiLine = False
.IgnoreCase = True
.Pattern = "([^,]+)(?:[ ,]*)"
End With
If regex.Test(s) Then
Set m = regex.Execute(s)
ReDim ar(0 To m.Count - 1)
For i = 0 To UBound(ar)
ar(i) = Trim(m(i).submatches(0))
Next
End If
Cleanup = Join(ar, SEP)
End Function
Code categories approach
For the sake of completeness and to show also other ways "leading to Rome", I want to demonstrate an approach allowing to group the string input into five code categories in order to extract alphanumerics by a tricky match (see [B] Function getCats()):
To meet the requirements in OP use the following steps:
1) remove comma separated tokens if empty or only blanks (optional),
2) group characters into code categories,
3) check catCodes returning alpha nums including even accented or diacritic letters as well as characters like [ -,.+_]
Function AlphaNum(ByVal s As String, _
Optional IgnoreEmpty As Boolean = True, _
Optional info As Boolean = False) As String
'Site: https://stackoverflow.com/questions/15723672/how-to-remove-all-non-alphanumeric-characters-from-a-string-except-period-and-sp/74679416#74679416
'Auth.: https://stackoverflow.com/users/6460297/t-m
'Date: 2023-01-12
'1) remove comma separated tokens if empty or only blanks (s passed as byRef argument)
If IgnoreEmpty Then RemoveEmpty s ' << [A] RemoveEmpty
'2) group characters into code categories
Dim catCodes: catCodes = getCats(s, info) ' << [B] getCats()
'3) check catCodes and return alpha nums plus chars like [ -,.+_]
Dim i As Long, ii As Long
For i = 1 To UBound(catCodes)
' get current character
Dim curr As String: curr = Mid$(s, i, 1)
Dim okay As Boolean: okay = False
Select Case catCodes(i)
' AlphaNum: cat.4=digits, cat.5=alpha letters
Case Is >= 4: okay = True
' Category 2: allow only space, comma, minus
Case 2: If InStr(" -,", curr) <> 0 Then okay = True
' Category 3: allow only point, plus, underline
Case 3: If InStr(".+_", curr) <> 0 Then okay = True
End Select
If okay Then ii = ii + 1: catCodes(ii) = curr ' increment counter
Next i
ReDim Preserve catCodes(1 To ii)
AlphaNum = Join(catCodes, vbNullString)
End Function
Note: Instead of If InStr(" -,", curr) <> 0 Then in Case 2 you may code If curr like "[ -,]" Then, too. Similar in Case 3 :-)
[A] Helper procedure RemoveEmpty
Optional clean-up removing comma separated tokens if empty or containing only blanks:
Sub RemoveEmpty(ByRef s As String)
'Purp: remove comma separated tokens if empty or only blanks
Const DEL = "$DEL$" ' temporary deletion marker
Dim i As Long
Dim tmp: tmp = Split(s, ",")
For i = LBound(tmp) To UBound(tmp)
tmp(i) = IIf(Len(Trim(tmp(i))) = 0, DEL, Trim(tmp(i)))
Next i
tmp = Filter(tmp, DEL, False) ' remove marked elements
s = Join(tmp, ",")
End Sub
[B] Helper function getCats()
A tricky way to groups characters into five code categories, thus building the basic logic for any further analyzing:
Function getCats(s, Optional info As Boolean = False)
'Purp.: group characters into five code categories
'Auth.: https://stackoverflow.com/users/6460297/t-m
'Site: https://stackoverflow.com/questions/15723672/how-to-remove-all-non-alphanumeric-characters-from-a-string-except-period-and-sp/74679416#74679416
'Note: Cat.: including:
' 1 ~~> apostrophe '
' 2 ~~> space, comma, minus etc
' 3 ~~> point separ., plus etc
' 4 ~~> digits 0..9
' 5 ~~> alpha (even including accented or diacritic letters!)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'a) get array of single characters
Const CATEG As String = "' - . 0 A" 'define group starters (case indep.)
Dim arr: arr = Char2Arr(s) ' << [C] Char2Arr()
Dim chars: chars = Split(CATEG)
'b) return codes per array element
getCats = Application.Match(arr, chars) 'No 3rd zero-argument!!
'c) display in immediate window (optionally)
If info Then Debug.Print Join(arr, "|") & vbNewLine & Join(getCats, "|")
End Function
[C] Helper function Char2Arr
Assigns every string character to an array:
Function Char2Arr(ByVal s As String)
'Purp.: assign single characters to array
s = StrConv(s, vbUnicode)
Char2Arr = Split(s, vbNullChar, Len(s) \ 2)
End Function
I am using a function to extract numbers from string with conditions that number with 8 digits and does not contain characters (. , #).
It works with 8 digits , but if the number is followed by characters (. , #) ,it also extract that number and that not required.
This my string 11111111 12345678.1 11111112 11111113 and the expected output is 11111111 11111112 11111113 without 12345678.1.
I tried to use negative Lookahead \d{8}(?!.,#) but it is useless.
Thanks all for your help.
Function Find8Numbers(st As String) As Variant
Dim regex As New RegExp
Dim matches As MatchCollection, mch As match
regex.Pattern = "\d{8}" 'Look for variable length numbers only
regex.IgnoreCase = True
regex.Global = True
regex.MultiLine = True
If (regex.Test(st) = True) Then
Set matches = regex.Execute(st) 'Execute search
For Each mch In matches
Find8Numbers = LTrim(Find8Numbers) & " " & mch.value
Next
End If
End Function
In line with your question and current attempt, you could indeed use regex:
Function Find8Numbers(st As String) As String
With CreateObject("vbscript.regexp")
.Pattern = "(?:^|\s)(\d{8})(?![.,#\d])"
.Global = True
If .Test(st) Then
Set Matches = .Execute(st)
For Each mch In Matches
Find8Numbers = LTrim(Find8Numbers & " " & mch.submatches(0))
Next
End If
End With
End Function
Invoke through:
Sub Test()
Dim s As String: s = "11111111 12345678.1 11111112 11111113"
Debug.Print Find8Numbers(s)
End Sub
Prints:
11111111 11111112 11111113
Pattern used:
(?:^|\s)(\d{8})(?![.,#\d])
See an online demo
(?:^|\s) - No lookbehind in VBA thus used a non-capture group to match start-line anchor or whitespace;
(\d{8}) - Exactly 8 digits in capture group;
(?![.,#\d]) - Negative lookahead to assert position isn't followed by any of given characters including digits.
I'm not sure you need Regex for what is a reasonably simple pattern. You could just go with a VBA solution:
Public Function Find8Numbers(str As String) As String
Dim c As String, c1 As String
Dim i As Long, numStart As Long
Dim isNumSeq As Boolean
Dim result As String
If Len(str) < 8 Then Exit Function
For i = 1 To Len(str)
c = Mid(str, i, 1)
If i = Len(str) Then
c1 = ""
Else
c1 = Mid(str, i + 1, 1)
End If
If c >= "0" And c <= "9" Then
If isNumSeq Then
If i - numStart + 1 = 8 Then
If c1 <> "." And c1 <> "," And c1 <> "#" Then
If result <> "" Then result = result & " "
result = result & Mid(str, numStart, 8)
isNumSeq = False
End If
End If
Else
If i > Len(str) - 8 + 1 Then Exit For
isNumSeq = True
numStart = i
End If
Else
isNumSeq = False
End If
Next
Find8Numbers = result
End Function
I have created a VBA code to remove all special characters available in a column. As an example I have a Alphanumeric character with some special characters in every cells of a column:
Suppose in a cell I have a value: abc#123!-245
After executing my code I got output abc 123 245
Here my code is working fine to remove all the special characters. My code is given below:
Sub ReplaceSpecial()
Dim cel As Range
Dim strVal As String
Dim i As Long
Application.ScreenUpdating = False
For Each cel In Selection
strVal = cel.Value
For i = 1 To Len(strVal)
Select Case Asc(Mid(strVal, i, 1))
Case 32, 48 To 57, 65 To 90, 97 To 122
' Leave ordinary characters alone
Case Else
Mid(strVal, i, 1) = " "
End Select
Next i
cel.Value = strVal
Next cel
Application.ScreenUpdating = True
End Sub
Now if I want to remove the space for my output so that output should look like abc123245, how to do that in VBA?
Input: abc#123!-245
Current Output: abc 123 245
Required Output: abc123245
You could construct a new string with just the permitted characters.
Sub ReplaceSpecial()
Dim cel As Range
Dim strVal As String, temp As String
Dim i As Long
Application.ScreenUpdating = False
For Each cel In Selection
strVal = cel.Value
temp = vbNullString
For i = 1 To Len(strVal)
Select Case Asc(Mid(strVal, i, 1))
Case 32, 48 To 57, 65 To 90, 97 To 122
temp = temp & Mid(strVal, i, 1)
End Select
Next i
cel.Value = temp
Next cel
Application.ScreenUpdating = True
End Sub
My sole intention for this late post was to
test some features of the ►Application.Match() function (comparing a string input against valid characters) and to
demonstrate a nice way to "split" a string into single characters as alternative and possibly instructive solution (see help function String2Arr()).
I don't intend, however to show better or faster code here.
Application.Match() allows not only to execute 1 character searches in an array, but to compare even two arrays in one go,
i.e. a character array (based on an atomized string input) against an array of valid characters (blanks, all digits and chars from A to Z).
As Application.Match is case insensitive, it suffices to take e.g. lower case characters.
All findings of input chars return their position in the valid characters array (otherwise resulting in Error 2042).
Furthermore it was necessary to exclude the wild cards "*" and "?", which would have been considered as findings otherwise.
Function ValidChars(ByVal s, Optional JoinResult As Boolean = True)
'Purp: return only valid characters if space,digits,"A-Z" or "a-z"
'compare all string characters against valid characters
Dim tmp: tmp = foundCharAt(s) ' get array with found positions in chars
'overwrite tmp array
Dim i As Long, ii As Long
For i = 1 To UBound(tmp)
If IsNumeric(tmp(i)) Then ' found in valid positions
If Not Mid(s, i, 1) Like "[?*]" Then ' exclude wild cards
ii = ii + 1
tmp(ii) = Mid(s, i, 1) ' get char from original string
End If
End If
Next
ReDim Preserve tmp(1 To ii) ' reduce to new size
'join tmp elements to resulting string (if argument JoinResult = True)
ValidChars = IIf(JoinResult, Join(tmp, ""), tmp)
End Function
Help function foundCharAt()
Returns an array of found character positions in the valid chars array:
Function foundCharAt(ByVal s As String) As Variant
'Purp: return array of found character positions in chars string
'Note: (non-findings show Error 2042; can be identified by IsError + Not IsNumeric)
Dim chars: chars = String2Arr(" 0123456789abcdefghijklmnopqrstuvwxyz")
foundCharAt = Application.Match(String2Arr(s), chars, 0)
End Function
Help function String2Arr()
Assigns an array of single characters after atomizing a string input:
Function String2Arr(ByVal s As String) As Variant
'Purp: return array of all single characters in a string
'Idea: https://stackoverflow.com/questions/13195583/split-string-into-array-of-characters
s = StrConv(s, vbUnicode)
String2Arr = Split(s, vbNullChar, Len(s) \ 2)
End Function
Use a regular expression's object and replace all unwanted characters by using a negated character class. For demonstration purposes:
Sub Test()
Dim str As String: str = "abc#123!-245"
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "[^0-9A-Za-z ]"
str = .Replace(str, "")
End With
Debug.Print str
End Sub
The pattern [^0-9A-Za-z ] is a negated character class and captured everything that is not a alphanumeric or a space character. You'll find a more in-depth explaination in this online demo.
At time of writing I'm unsure if you want to leave out the space characters or not. If so, just remove the space from the pattern.
Thought I'd chuck in another alternative using the Like() operator:
For i = Len(str) To 1 Step -1
If Mid(str, i, 1) Like "[!0-9A-Za-z ]" Then
str= Application.Replace(str, i, 1, "")
End If
Next
Or with a 2nd string-type variable (as per #BigBen's answer):
For i = 1 to Len(str)
If Mid(str, i, 1) Like "[0-9A-Za-z ]" Then
temp = temp & Mid(str, i, 1)
End If
Next
If you want to build on your current effort, replace:
cel.Value = strVal
with:
cel.Value = Replace(strVal, " ", "")
Consider:
Sub ReplaceSpecial()
Dim cel As Range
Dim strVal As String
Dim i As Long
Application.ScreenUpdating = False
For Each cel In Selection
strVal = cel.Value
For i = 1 To Len(strVal)
Select Case Asc(Mid(strVal, i, 1))
Case 32, 48 To 57, 65 To 90, 97 To 122
' Leave ordinary characters alone
Case Else
Mid(strVal, i, 1) = " "
End Select
Next i
cel.Value = Replace(strVal, " ", "")
Next cel
Application.ScreenUpdating = True
End Sub
I am trying to divide merged information from one cell into separate cells.
one cell:
amount:2 price:253,18 price2:59,24 EU status:WBB NAS MRR OWA PXA min:1 opt:3 category: PNE code z:195750
divided data: (I want to export each part into another cell)
amount:2
price:253,18
price2:59,24 EU
status:WBB NAS MRR OWA PXA
min:1
opt:3
category: PNE
code z:195750
I can't simply divide by finding empty space, status cell which is case-sensitive | status:WBB NAS MRR OWA PXA| has a different data range with spaces that can't be divided.
Split ( expression [,delimiter] [,limit] [,compare] )
Sub Split_VBA()
'Create variables
Dim MyArray() As String, MyString As String, N As Integer, Temp As String
MyString = B2 ' TRYING TO GET DATA FROM CELL B2 TO SPLIT IT
'Use the split function to divide the string using a string "price:"
MyArray = Split(MyString, "price:")
Dim arr() As String
' Split the string to an array
arr = Split(B2, "price:") 'try to divide first part of data when appears string 'price:'
For N = 0 To UBound(MyArray)
'place each array element plus a line feed character into a string
Temp = Temp & MyArray(N) & vbLf
Next N
' I WOULD LIKE TO PROVIDE RESULT IN A ROW NOT IN A COLUMN
Range("A1") = Temp
End Sub
So far this VBA code seems to be a little above my abilities and as far as I checked some online available samples, tried to provide code as below, but I just got stuck and I hereby ask you dear community for some piece of advice.
As the order is the same one way is to simply search for adjacent key names & parse out whats in-between:
Sub g()
Dim stringValue As String
stringValue = "amount:2 price:253,18 price2:59,24 EU status:WBB NAS MRR OWA PXA min:1 opt:3 category: PNE code z:195750"
Debug.Print getPart(stringValue, "amount", "price")
Debug.Print getPart(stringValue, "price", "price2")
Debug.Print getPart(stringValue, "price2", "status")
Debug.Print getPart(stringValue, "status", "min")
Debug.Print getPart(stringValue, "min", "opt")
Debug.Print getPart(stringValue, "opt", "category")
Debug.Print getPart(stringValue, "category", "code z")
Debug.Print getPart(stringValue, "code z", "", True)
End Sub
Function getPart(value As String, fromKey As String, toKey As String, Optional isLast As Boolean = False) As String
Dim pos1 As Long, pos2 As Long
pos1 = InStr(1, value, fromKey & ":")
If (isLast) Then
pos2 = Len(value)
Else
pos2 = InStr(pos1, value, toKey & ":")
End If
getPart = Trim$(Mid$(value, pos1, pos2 - pos1))
End Function
amount:2
price:253,18
price2:59,24 EU
status:WBB NAS MRR OWA PXA
min:1
opt:3
category: PNE
code z:19575
Several choices:
The pattern you show is that each split can be determined by a single word (no spaces) followed by a colon.
This can be easily replicated as a regular expression pattern, and implemented in VBA.
However, if your splitword might have a space, then you'll need a different solution:
VBA Regex Solution
'Set Reference to Microsoft VBScript Regular Expressions 5.5
Option Explicit
Function splitIt(S)
Dim RE As RegExp, MC As MatchCollection, M As Match
Dim vResult As Variant, I As Long
Const sPat As String = "\w+:.*?(?=(?:\w+:)|$)"
Set RE = New RegExp
With RE
.Global = True
.Pattern = sPat
If .Test(S) = True Then
Set MC = .Execute(S)
ReDim vResult(1 To MC.Count)
I = 0
For Each M In MC
I = I + 1
vResult(I) = M
Next M
Else
vResult = "split pattern not present"
End If
End With
splitIt = vResult
End Function
This function outputs a horizontal array of values. In versions of Excel with dynamic arrays, this will Spill into the adjacent cells. In older versions, you may have to enter it as an array formula; use INDEX for each element; or rewrite this as a Sub to output to the specific cells
Split on word: regex explanation
\w+:.*?(?=(?:\w+:)|$)
Match a single character that is a “word character” \w+
Between one and unlimited times, as many times as possible, giving back as needed (greedy) +
Match the colon character :
Match any single character that is NOT a line break character .*?
Between zero and unlimited times, as few times as possible, expanding as needed (lazy) *?
Assert that the regex below can be matched starting at this position (positive lookahead) (?=(?:\w+:)|$)
Match this alternative (?:\w+:)
Match the regular expression below (?:\w+:)
Match a single character that is a “word character” \w+
Between one and unlimited times, as many times as possible, giving back as needed (greedy) +
Match the colon character :
Or match this alternative $
Assert position at the very end of the string $
Created with RegexBuddy
Split - Join - ReSplit
Instead of coding fixed categories, this late approach reads in any category from the base string before executing Split actions (only exception code z will be treated in an extra step):
1 define delimiters
2 tokenize base string (split action via blanks " ") and prefix a Pipe character "|" to the joined category elements
3 return results array via an eventual Pipe Split
Function getParts(ByVal s As String)
'Purpose: split into categories (identified by colon character ":")
'1. a) define delimiters
Const Blank$ = " ", Colon$ = ":", Pipe$ = "|", Xtra$ = "^"
' b) provide for category exception "code z" (the only two words category)
s = Replace(s, "code z", "code" & Xtra & "z")
'2. a) tokenize base string
Dim tokens: tokens = Split(s, Blank)
' b) prefix all ":" elements by Pipe char "|"
Dim i As Long
For i = 0 To UBound(tokens) '
tokens(i) = IIf(InStr(1, tokens(i), Colon), Pipe, Blank) & tokens(i)
Next
' c) restore mutilated "code z" category (back from "code^z")
s = Replace(Join(tokens, vbNullString), Xtra, Blank)
'3. get results array via Pipe split
getParts = Split(Mid$(s,2), Pipe) ' edited due to FaneDurus comment
End Function
I'd look into some regular expression, for example:
[a-z\d ]+:[ ,A-Z\d]+
See an online demo
[a-z\d ]+ - 1+ Lowercase alpha, space, or digit chars.
: - A literal colon.
[ ,A-Z\d]+ - 1+ Space, comma, uppercase alpha or digit.
VBA:
Sub Test()
Dim str As String: str = "amount:2 price:253,18 price2:59,24 EU status:WBB NAS MRR OWA PXA min:1 opt:3 category: PNE code z:195750"
Dim matches As Object
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "[a-z\d]+(?: [a-z\d]+)?:[ ,A-Z\d]+"
If .Test(str) = True Then
Set matches = .Execute(str)
For Each match In matches
Debug.Print Trim(match)
Next
End If
End With
End Sub
A version with a similar logic as Alex K.'s answer, so all the credit goes to him, using two arrays and the processing result being dropped on a row:
Sub extractFromString()
Dim arrStr, arrFin, strInit As String, i As Long, iStart As Long, iEnd As Long, k As Long
strInit = "amount:2 price:253,18 price2:59,24 EU status:WBB NAS MRR OWA PXA min:1 opt:3 category: PNE code z:195750"
arrStr = Split("amount:,price:,price2:,status:,min:,opt:,category:,code z:", ",")
ReDim arrFin(UBound(arrStr))
For i = 0 To UBound(arrStr)
iStart = InStr(strInit, arrStr(i))
If i <> UBound(arrStr) Then
iEnd = InStr(iStart, strInit, arrStr(i + 1))
Else
arrFin(k) = Mid(strInit, iStart): Exit For
End If
arrFin(k) = RTrim(Mid(strInit, iStart, iEnd - iStart)): k = k + 1
Next i
'use here the first cell of the row where the processing result to be returned
Range("A22").Resize(1, UBound(arrFin) + 1) = arrFin
End Sub
Another version of split/join/filter arrays:
Sub extractFromStr()
Dim arrStr, arrFin, strInit As String, i As Long, k As Long
Dim arr1, arr2, firstEl As String, secEl As String
strInit = "amount:2 price:253,18 price2:59,24 EU status:WBB NAS MRR OWA PXA min:1 opt:3 category: PNE code z:195750"
arrStr = Split(strInit, ":") 'split the string by ":" character
ReDim arrFin(UBound(arrStr)) 'ReDim the final array at the same number of elements
For i = 0 To UBound(arrStr) - 1 'iterate between the array elements (except the last)
arr1 = Split(arrStr(i), " ") 'split the i element by space (" ")
arr2 = Split(arrStr(i + 1), " ") 'split the i + 1 element by space (" ")
If i = 0 Then 'for the first array element:
firstEl = arrStr(i) 'it receives the (first) array element value
Else 'for the rest of array elements:
'extract firstEl (category) like first arr1 element, except the case of 'code z' which is extracted in a different way
firstEl = IIf(i = UBound(arrStr) - 1, arr1(UBound(arr1) - 1) & " " & arr1(UBound(arr1)), arr1(UBound(arr1)))
End If
'in order to remove array elements, the code transformes the one to be removed in "|||":
'it could be anything, but "|||" is difficult to suppose that it will be the text of a real element...
arr2(UBound(arr2)) = "|||": If i = UBound(arrStr) - 2 Then arr2(UBound(arr2) - 1) = "|||"
'extract the secEl (the value) by joining the array after removed firstEl:
secEl = IIf(i = UBound(arrStr) - 1, arrStr(UBound(arrStr)), Join(Filter(arr2, "|||", False), " "))
arrFin(k) = firstEl & ":" & secEl: k = k + 1 'create the processed element of the array to keep the result
Next i
'use here the first cell of the row where the processing result to be returned. Here, it returns on the first row:
Range("A1").Resize(1, UBound(arrFin) + 1) = arrFin
End Sub
I have an excel spreadsheet with over 50,000 entries. The entries have a name and address and sometimes a phone number ALL in the same string. I am concentrating on the phone number part of the string which is always at the end and enclosed in parentheses. I have been trying to use VBA code to address this.
How to remove the LAST set Parentheses from a Excel text string that contains only numeric s between the parentheses. In any given string there may be either NO parentheses or multiple parentheses but I only want to remove that LAST set and leave the numbers contained there in the string
Example string "Toone Carkeet J., agt.,Alliance Assurance Co. Ltd. (Provident Life branch), 3 St. Andrew st. (1936)" I have tried using VBScript.RegExp to define "(1936)" but I cannot get the RegExp to match the string and replace the parentheses () with "".
For Each Cell In Range
If strPattern<> "" Then
strInput = Cell
With regEx
.Pattern="\(([0-9]))*)"
.Global=False
End With
If .Pattern= True Then
Replace(Cell.Value, "(","")
End If
Here are two quick user defined functions that do not rely on regular expressions. The first uses VBA's StrReverse and the second InStrRev.
Function RemoveParens1(str As String)
str = StrReverse(str)
str = Replace(str, "(", vbNullString, 1, 1)
str = Replace(str, ")", vbNullString, 1, 1)
RemoveParens1 = StrReverse(str)
End Function
Function RemoveParens2(str As String)
Dim o As Integer, c As Integer
o = InStrRev(str, "(")
c = InStrRev(str, ")")
str = Left(str, c - 1) & Mid(str, c + 1)
str = Left(str, o - 1) & Mid(str, o + 1)
RemoveParens2 = str
End Function
If you don't want to use UDFs, just pick the logic method you prefer and adapt it for your own purposes.
Here's one more using regular expression's Replace.
Function RemoveParens3(str As String)
Static rgx As Object, cmat As Object, tmp As String
If rgx Is Nothing Then Set rgx = CreateObject("vbscript.regexp")
With rgx
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = "\([0-9]*\)"
If .test(str) Then
Set cmat = .Execute(str)
tmp = cmat.Item(cmat.Count - 1)
tmp = Mid(tmp, 2, Len(tmp) - 2)
str = .Replace(str, tmp)
End If
End With
RemoveParens3 = str
End Function
Here's an example using similar logic to yours.
I changed the names of the range variables as it is not a good idea to use keywords for named variables, even if the editor will allow that.
Instead of just deleting the parentheses, we match the entire (nnnn) substring with the numbers inside a capturing group, and then replace that match with just the captured group.
Since Replace won't do anything if there is no match, there is no need to test.
Also, note that we set up the regEx OUTSIDE the loop.
With regEx
.Pattern = "\((\d+)\)"
.Global = False
End With
For Each myCell In myRange
myCell = regEx.Replace(myCell, "$1")
Next myCell
If necessary due to other substrings with the same pattern, you could change the pattern to ensure the match is at the end of the line, or that it is the last pattern of that type in the string.
For example:
Substring at end of the line
\((\d+)\)$
Substring the last one
\((\d+)\)(?!.*\(\d+\))
And there may be other modifications necessary if your string is in multiple lines within the cell.
Dim x, y, z As Long
x = 2 'ASSUMING YOUR DATA START AT RANGE A2
With Sheet1
Do While .Cells(x, 1).Value <> ""
If Right(.Cells(x, 1).Value, 1) = ")" Then
.Cells(x, 1).Value = Replace(.Cells(x, 1).Value, ")", "")
z = VBA.Len(.Cells(x, 1).Value)
For y = z To 1 Step -1
If Mid(.Cells(x, 1).Value, y, 1) = "(" Then
.Cells(x, 1).Value = Replace(.Cells(x, 1).Value, "(", "")
Exit For
End If
Next y
x = x + 1
End If
Loop
End With