Skip to content

Commit 6e51c9e

Browse files
committed
Initial commit
See the README and the CHANGELOG
0 parents  commit 6e51c9e

8 files changed

+299
-0
lines changed

CHANGELOG.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Change Log
2+
All notable changes to this project will be documented in this file.
3+
4+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
5+
and this project adheres to [Semantic Versioning](http://semver.org/).
6+
7+
## [Unreleased] - yyyy-mm-dd
8+
9+
### To Be Added
10+
11+
- ???
12+
13+
## [1.0.0] - 2022-03-09
14+
15+
### Added
16+
17+
- RegexFunctions.bas, which contains the source code in a form you can read on your favorite text editor
18+
- RegexFunctions.xlam, an Excel add-in that requires Microsoft VBScript Regular Expressions 5.5
19+
- regexfunctions_addin_test.xlsx, which contains tests for the worksheet functions from RegexFunctions.
20+
- These functions are included (corresponding Python `re` functions in parens):
21+
- RegexReplace (workalike of `re.sub`, except no function replacements)
22+
- RegexContains (`bool(re.search(x))`)
23+
- RegexFullMatch (`bool(re.fullmatch(x))`)
24+
- RegexMatch (`bool(re.match(x))`)
25+
- RegexMatchEnd (like `bool(re.match(x))`, but matches the end)
26+
- RegexMatches (`re.finditer`)
27+
- RegexFindall (sort of like stringjoining `re.findall`)
28+
- RegexFind (sort of like indexing in the list from `re.findall`)
29+
- RegexSplit(like `re.split`)
30+
- RegexSplitToString (like stringjoining the list from `re.split`)
31+
- All of those functions except RegexSplit and RegexMatches can be used as worksheet functions once the add-in has been enabled.

CONTRIBUTING.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
If you think I should change something, just ask.
2+
3+
I'm fairly new to Git and GitHub, so I can't guarantee I'll be able to apply your changes very quickly or cleanly, but I'll try my best.

LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2022-2022 Mark Johnston Olson <mjolsonsfca at gmail dot com>
2+
3+
Permission is hereby granted, free of charge, to any person
4+
obtaining a copy of this software and associated documentation
5+
files (the "Software"), to deal in the Software without
6+
restriction, including without limitation the rights to use,
7+
copy, modify, merge, publish, distribute, sublicense, and/or
8+
sell copies of the Software, and to permit persons to whom the
9+
Software is furnished to do so, subject to the following
10+
conditions:
11+
12+
The above copyright notice and this permission notice shall be
13+
included in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16+
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
18+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
VBA regex functions
2+
============
3+
4+
*VBA adapations of Python re functions*
5+
6+
Features
7+
--------
8+
9+
* Regular expression functions adapted from python's re library
10+
* Several functions that can be used from a worksheet once the add-in is enabled.
11+
12+
How to use
13+
----------
14+
15+
* The functions can be used in VBA macros if you want, but if you want to use them as worksheet functions, do the following:
16+
1. Open Excel.
17+
2. Go to Options -> Add-ins -> Go -> Browse
18+
3. Select the path to RegexFunctions.xlam in your file explorer
19+
4. Open up regexfunctions_addin_test.xlsx and use F9 to refresh all the functions. You can visually inspect the results and source code to make sure it looks like successful_test_outcomes.png.
20+
* See the CHANGELOG for what functions you can use.
21+
22+
Contributing
23+
------------
24+
25+
Be sure to read the [contribution guidelines]
26+
27+
(https://github.com/molsonkiko/vba_regex_funcs/blob/main/CONTRIBUTING.md).

RegexFunctions.bas

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
Attribute VB_Name = "RegexFunctions"
2+
' REFERENCE "Microsoft VBScript Regular Expressions 5.5" FOR ( RegExp )
3+
Function RegexReplace(pat As String, _
4+
repl As String, _
5+
str As String, _
6+
Optional is_global As Boolean = True, _
7+
Optional ignore_case As Boolean = False, _
8+
Optional multiline As Boolean = False) As String
9+
' Replace all instances of pat in str with repl.
10+
Dim regex As Object
11+
Set regex = New RegExp
12+
With regex:
13+
.Global = is_global
14+
.IgnoreCase = ignore_case
15+
.Pattern = pat
16+
.multiline = multiline
17+
End With
18+
RegexReplace = regex.Replace(str, repl)
19+
End Function
20+
21+
22+
Function RegexContains(pat As String, _
23+
str As String, _
24+
Optional ignore_case As Boolean = False, _
25+
Optional multiline As Boolean = False) As Boolean
26+
' Return True if regular expression pat matches the string else False
27+
Dim regex: Set regex = New RegExp
28+
With regex:
29+
.IgnoreCase = ignore_case
30+
.Pattern = pat
31+
.multiline = multiline
32+
End With
33+
RegexContains = regex.Test(str)
34+
End Function
35+
36+
37+
Function RegexFullMatch(pat As String, _
38+
str As String, _
39+
Optional ignore_case As Boolean = False, _
40+
Optional multiline As Boolean = False) As Boolean
41+
' Return True if regular expression pat matches the string EXACTLY else False
42+
Dim regex: Set regex = New RegExp
43+
With regex:
44+
.IgnoreCase = ignore_case
45+
.Pattern = "^" + pat + "$"
46+
.multiline = multiline
47+
End With
48+
RegexFullMatch = regex.Test(str)
49+
End Function
50+
51+
52+
Function RegexMatch(pat As String, _
53+
str As String, _
54+
Optional ignore_case As Boolean = False, _
55+
Optional multiline As Boolean = False) As Boolean
56+
' Return True if regular expression pat matches the string BEGINNING else False
57+
Dim regex: Set regex = New RegExp
58+
With regex:
59+
.IgnoreCase = ignore_case
60+
.Pattern = "^" + pat
61+
.multiline = multiline
62+
End With
63+
RegexMatch = regex.Test(str)
64+
End Function
65+
66+
67+
Function RegexMatchEnd(pat As String, _
68+
str As String, _
69+
Optional ignore_case As Boolean = False, _
70+
Optional multiline As Boolean = False) As Boolean
71+
' Return True if regular expression pat matches the string END else False
72+
Dim regex: Set regex = New RegExp
73+
With regex:
74+
.IgnoreCase = ignore_case
75+
.Pattern = pat + "$"
76+
.multiline = multiline
77+
End With
78+
RegexMatchEnd = regex.Test(str)
79+
End Function
80+
81+
82+
Function RegexMatches(pat As String, _
83+
str As String, _
84+
Optional is_global As Boolean = True, _
85+
Optional ignore_case As Boolean = True, _
86+
Optional multiline As Boolean = False) As Object
87+
' Get all the matches for a pattern in string str with those parameters
88+
Dim regex: Set regex = New RegExp
89+
With regex:
90+
.Global = is_global
91+
.IgnoreCase = ignore_case
92+
.Pattern = pat
93+
.multiline = multiline
94+
End With
95+
Set RegexMatches = regex.Execute(str) ' find all matches
96+
End Function
97+
98+
99+
Function RegexFindAll(pat As String, _
100+
str As String, _
101+
Optional sep As String = ", ", _
102+
Optional is_global As Boolean = True, _
103+
Optional ignore_case As Boolean = False, _
104+
Optional multiline As Boolean = False) As String
105+
' Find all matches and stringjoin them together with sep.
106+
' Unlike RegexMatches, this is suitable for use as a worksheet formula.
107+
Dim matches
108+
Dim num_matches As Integer
109+
Dim ii As Integer
110+
Set matches = RegexMatches(pat, str, is_global, ignore_case, multiline)
111+
num_matches = matches.Count - 1
112+
ReDim strings(num_matches) As String ' this will be joined at the end
113+
For ii = 0 To num_matches
114+
' include every match found, with sep (the argument) separating
115+
' each match
116+
strings(ii) = matches.Item(ii)
117+
Next ii
118+
RegexFindAll = Join(strings, sep)
119+
End Function
120+
121+
122+
Function RegexFind(pat As String, _
123+
str As String, _
124+
Optional match_num As Integer = 0, _
125+
Optional submatch_sep As String = "", _
126+
Optional ignore_case As Boolean = False, _
127+
Optional multiline As Boolean = False) As String
128+
' Get the match_num^th match in string str to a regex with various params.
129+
Dim is_global As Boolean
130+
Dim matches
131+
Dim num_submatches As Integer
132+
is_global = IIf(match_num = 0, False, True)
133+
Set matches = RegexMatches(pat, str, is_global, ignore_case, multiline)
134+
If matches.Count - 1 < match_num Then
135+
MsgBox ("Match number " + match_num _
136+
+ " couldn't be found in matches of length " + matches.Count)
137+
Exit Function
138+
End If
139+
num_submatches = matches.Item(match_num).SubMatches.Count - 1
140+
If num_submatches < 0 Then
141+
RegexFind = matches.Item(match_num).Value
142+
Else
143+
ReDim strings(num_submatches) As String
144+
Dim ii As Integer
145+
For ii = 0 To num_submatches
146+
strings(ii) = matches.Item(match_num).SubMatches.Item(ii)
147+
Next ii
148+
RegexFind = Join(strings, submatch_sep)
149+
End If
150+
End Function
151+
152+
153+
Function RegexSplit(pat As String, _
154+
str As String, _
155+
Optional ignore_case As Boolean = False, _
156+
Optional multiline As Boolean = False, _
157+
Optional num_splits = -1) As Variant
158+
' If there are no capturing groups in pat, get an array of all the substrings not matched
159+
' by the regex.
160+
' if there are capturing groups in the regex, each capturing group gets its
161+
' own element in the string list at the location where it's found in the string
162+
' in addition to all the substrings not matched by the regex
163+
Dim is_global As Boolean
164+
Dim matches
165+
is_global = IIf(num_splits = 0, False, True)
166+
Set matches = RegexMatches(pat, str, is_global, ignore_case, multiline)
167+
ReDim out(1) As Variant
168+
If matches.Count = 0 Then
169+
out(0) = str
170+
Else
171+
Dim num_strings As Integer
172+
Dim matches_so_far As Integer
173+
Dim first_index As Long
174+
Dim match_index As Long
175+
Dim unmatched As String
176+
Dim num_submatches As Integer
177+
Dim submatch
178+
num_submatches = matches.Item(0).SubMatches.Count
179+
ReDim out(matches.Count * (num_submatches + 1))
180+
For Each match In matches
181+
If (num_splits > 0) And (matches_so_far = num_splits) Then Exit For
182+
match_index = match.FirstIndex
183+
unmatched = Mid(str, first_index + 1, match_index - first_index)
184+
' VBA's Mid function uses 1-based indexing
185+
first_index = match_index + match.Length
186+
out(num_strings) = unmatched
187+
num_strings = num_strings + 1
188+
For Each submatch In match.SubMatches
189+
out(num_strings) = submatch
190+
num_strings = num_strings + 1
191+
Next submatch
192+
matches_so_far = matches_so_far + 1
193+
' MsgBox ("{" + Join(out, ", ") + "}")
194+
Next match
195+
out(num_strings) = Mid(str, first_index + 1, Len(str) - first_index)
196+
ReDim Preserve out(num_strings)
197+
End If
198+
RegexSplit = out
199+
End Function
200+
201+
202+
Function RegexSplitToString(pat As String, _
203+
str As String, _
204+
Optional sep As String = ", ", _
205+
Optional ignore_case As Boolean = False, _
206+
Optional multiline As Boolean = False, _
207+
Optional num_splits = -1) As String
208+
' Uses RegexSplit to split out all instances of the regex
209+
' (or split and include capturing groups as described above)
210+
' and then stringjoin the resulting array with sep.
211+
' Unlike RegexSplit, this is suitable for use as a worksheet formula.
212+
Dim strings As Variant
213+
strings = RegexSplit(pat, str, ignore_case, multiline, num_splits)
214+
RegexSplitToString = Join(strings, sep)
215+
End Function
216+

RegexFunctions.xlam

19 KB
Binary file not shown.

regexfunctions_addin_test.xlsx

12.2 KB
Binary file not shown.

successful_test_outcomes.PNG

28.3 KB
Loading

0 commit comments

Comments
 (0)