VB.Net: Regular Expressions - regex

I'm creating an application that will be able to tell me who is logged onto what PC, in the manufacturing center, where I work.
I'm using psexec's psloggedon cmd process to get me the information for me and a VB.net windows application to show me the information.
I begin by first querying a databse for all the PC's we currently have active and dumping the data into a datagridview object. (Shown below)
Private Sub Button(sender As System.Object, e As System.EventArgs) Handles btngetPC.Click
'GET AREAS FROM DATABASE
Dim ds As New DataSet()
Dim db As String = "QUERY STRING GOES HERE"
'CONNECT TO DATABASE
Using da As New SqlDataAdapter(db, MySQLConnection)
da.Fill(ds, "MACHINE_NAME")
End Using
With datagridView1
.DataSource = ds.Tables("MACHINE_NAME")
End With
'ADD COLUMN TO DATAGRIDVIEW
datagridView1.Columns.Add("LOGGED_IN", "LOGGED_IN")
MySQLConnection.Close()
End Sub
Once I have my datagridview object filled out with all my active PC's, I can then use the machine names to run the psloggedon cmd to get who is logged in. I do so by using:
Private Sub execute(sender As Object, e As EventArgs) Handles bntExecuteCmd.Click
'COUNT ENTRIES
Dim RowCount As Integer = datagridView1.RowCount
''EXECUTE CMD
For i = 0 To RowCount - 2
'PERFORM PSLOGGEDON ROUTINE
Dim Proc1 As New Process
Proc1.StartInfo = New ProcessStartInfo("psloggedon")
Proc1.StartInfo.Arguments = "-l \\" & datagridView1.Rows(i).Cells(1).Value & ""
Proc1.StartInfo.RedirectStandardOutput = True
Proc1.StartInfo.UseShellExecute = False
Proc1.StartInfo.CreateNoWindow = True
Proc1.Start()
If Not Proc1.WaitForExit(300) Then
Proc1.Kill()
End If
'INSERT RESULTS INTO LOGGEN_IN COLUMN
Dim msg As String = Proc1.StandardOutput.ReadToEnd
Dim idx As Integer = msg.LastIndexOf("\"c)
Dim user As String = msg.Substring(idx + 1)
Dim final As String = UCase(System.Text.RegularExpressions.Regex.Replace(user, "^ELP.*$", ""))
datagridView1.Rows(i).Cells(2).Value = final
Next
End Sub
Finally, here is my question:
To get the employee names I must use regex becuase the raw format is unacceptable.
raw format:
"Connecting to Registry of \ELPSC171698...
Users logged on locally:
ECHOSTAR\Jane.Doe"
format after applying:
'INSERT RESULTS INTO LOGGEN_IN COLUMN
Dim msg As String = Proc1.StandardOutput.ReadToEnd
Dim idx As Integer = msg.LastIndexOf("\"c)
Dim user As String = msg.Substring(idx + 1)
Dim final As String = UCase(System.Text.RegularExpressions.Regex.Replace(user, "^ELP.*$", ""))
datagridView1.Rows(i).Cells(2).Value = final
"PAULA.RODRIGUEZ"
Looks good, right? However, when the raw format has more than one associate, like so:
"Connecting to Registry of \ELPSC173068...
Users logged on locally:
ECHOSTAR\John.Doe
ECHOSTAR\Ben.Doe"
the code I have written will get me the last person in this list. In this case, I will get JOHN.DOE when I need to get BEN.DOE.
Now the question: How can I change this code:
'INSERT RESULTS INTO LOGGEN_IN COLUMN
Dim msg As String = Proc1.StandardOutput.ReadToEnd
Dim idx As Integer = msg.LastIndexOf("\"c)
Dim user As String = msg.Substring(idx + 1)
Dim final As String = UCase(System.Text.RegularExpressions.Regex.Replace(user, "^ELP.*$", ""))
datagridView1.Rows(i).Cells(2).Value = final
To get me the first person, "JOHN.DOE" from here:
"Connecting to Registry of \ELPSC173068...
Users logged on locally:
ECHOSTAR\John.Doe
ECHOSTAR\Ben.Doe"
I hope my question was clear and well constructed. Thank you.

Use more specific regex and a capture group to get multiple items.
Dim mc As MatchCollection = Regex.Matches("Users logged on locally: ECHOSTAR\John.Doe ECHOSTAR\Ben.Doe", "[\t ]+[a-z_0-9]+\\(?<n>[a-z_\.0-9]+)(([\t ])|($))", RegexOptions.ExplicitCapture Or RegexOptions.IgnoreCase Or RegexOptions.Multiline)
For Each m As Match In mc
Dim name As String = m.Groups("n").value
Next
if you just want the first one then...
If mc.Count >= 1 Then
Dim name As String = mc(0).Groups("n").Value
End If

Change
Dim idx As Integer = msg.LastIndexOf("\"c)
to
Dim idx As Integer = msg.IndexOf("\"c)
Alternatively consider this:
Assuming you've validated the string first.
Dim user As String = msg.Split({"\"c, " "c}, StringSplitOptions.RemoveEmptyEntries)(1) _
.Replace("."c, " "c).ToUpper
To leave the decimal in just remove .Replace("."c, " "c)

Related

How to find a multiline match with RegEx in a text file using VBA?

I have a .txt report that I need to extract certain elements (using RegEx groups) that occur multiple times throughout the file. In order to capture the specific elements I need, I have to match the text in a textline against a pattern then look ahead to see if the next line matches a different pattern. If both are true, then return values of those groups into an array.
I am working in Excel 2013 using VBA and the RegExp library. I was able to write a multiline pattern in RegEx along with all the relevant fields I need in named groups. I am able to open a text file and load the data to a string. What I can't do is both at the same time. I've only been able to identify one line of text against one pattern.
Edit: Here is some sample text that has been redacted from the original source
DAILY CONTROL REPORT WORK OF: 08/07/19
ACQUIRING PAGE: 1
CUSTOMER : ACME CORP
CUSTOMER ID : 0000000001
Other miscellaneous data
DAILY CONTROL REPORT WORK OF: 08/07/19
ISSUING PAGE: 2
CUSTOMER : ACME CORP
CUSTOMER ID : 0000000001
Other miscellaneous data
DAILY CONTROL REPORT WORK OF: 08/07/19
NET SETTLEMENT SUMMARY PAGE: 3
CUSTOMER : ACME CORP
CUSTOMER ID : 0000000001
Other miscellaneous data
DAILY CONTROL REPORT WORK OF: 08/07/19
ACQUIRING PAGE: 4
CUSTOMER : ACME INC
CUSTOMER ID : 0000000002
Other miscellaneous data
DAILY CONTROL REPORT WORK OF: 08/07/19
ISSUING PAGE: 5
CUSTOMER : ACME INC
CUSTOMER ID : 0000000002
Other miscellaneous data
DAILY CONTROL REPORT WORK OF: 08/07/19
NET SETTLEMENT SUMMARY PAGE: 6
CUSTOMER : ACME INC
CUSTOMER ID : 0000000002
Other miscellaneous data
Dim rgx As Object
Set rgx = CreateObject("VBScript.RegExp")
Dim MyFolder As String
Dim MyFile As String
Dim TextLine As String
Dim strPattern As String
Dim strReport As String
Dim MyArray() As Variant
Dim i As Integer
MyFolder = "C:\Reports\Samples\"
MyFile = "MySampleFile.txt"
strPattern = "WORK OF: (?<WORKOF>\d\d\/\d\d\/\d\d)\s+\n\s+NET SETTLEMENT
SUMMARY.+\n.{22}(?<NAME>.+\b)\s+\n.{22}(?<NUM>\d+)\s+\n"
With rgx
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = strPattern
End With
Open (MyFolder & MyFile) For Input As #1
Do Until EOF(1)
Line Input #1, TextLine
strReport = strReport & TextLine
Loop
Close #1
i = 0
If rgx.Test(strReport) Then
ReDim Preserve MyArray(0 to i, 0 to 2)
MyArray(i, 0) = rgx.Replace(strReport, WORKOF)
MyArray(i, 1) = rgx.Replace(strReport, NAME)
MyArray(i, 2) = rgx.Replace(strReport, NUM)
i = i +1
End IF
There are a few things I know are wrong with this code:
1. The strReport string does not include CrLF at end of each line
2. The rgx.Test statement should loop through strReport looking for a
multiline match but the way it is written, it would always fail the test.
3. I am not sure how the array would be populated but it would end up with several hundred records, each with 3 values {WORKOF, NAME, NUM}
4. WORKOF is in the header of every page but the string "NET SETTLEMENT SUMMARY" only occurs about every 3rd or 4th page. That is where the information I want to capture resides.
I don't have your text files but I created a test input file with the following content:
WORK OF: 12/34/56 NET SETTLEMENT SUMMARY name1 789
WORK OF: 01/23/45 NET SETTLEMENT SUMMARY name1 6789
I could match the pattern with the following code:
Option Explicit
' Include: Tools > References > Microsoft VBScript Regular Expressions 5.5
Public Sub FindPatternInTextFile_Test()
Dim varResult As Variant: varResult = FindPatternInTextFile("C:\Reports\Samples\MySampleFile.txt", "WORK OF: (\d\d\/\d\d\/\d\d)\s+NET SETTLEMENT SUMMARY\s+(.+\b)\s+(\d+)")
End Sub
Public Function FindPatternInTextFile(strPath As String, strPattern As String) As Variant
Dim strContent As String: strContent = ReadWholeTextFile(strPath)
Dim rgxPattern As RegExp: Set rgxPattern = CreateRegex(strPattern)
Dim mtcFound As MatchCollection: Set mtcFound = rgxPattern.Execute(strContent)
If 0 < mtcFound.Count Then
Dim strResult() As String: ReDim strResult(0 To mtcFound.Count - 1, 0 To 2)
Dim i As Long: For i = 0 To mtcFound.Count - 1
strResult(i, 0) = mtcFound(i).SubMatches(0)
strResult(i, 1) = mtcFound(i).SubMatches(1)
strResult(i, 2) = mtcFound(i).SubMatches(2)
Next i
FindPatternInTextFile = strResult
Else
FindPatternInTextFile = vbNullString
End If
End Function
Public Function ReadWholeTextFile(strPath As String) As String
Dim strResult As String: strResult = vbNullString
Open strPath For Input As #1
Do Until EOF(1)
Dim strLine As String: strLine = vbNullString
Line Input #1, strLine
strResult = strResult & strLine & vbCrLf
Loop
Close #1
ReadWholeTextFile = strResult
End Function
Public Function CreateRegex(strPattern As String) As RegExp
Dim rgxResult As RegExp: Set rgxResult = New RegExp
With rgxResult
.Global = True
.MultiLine = True
.IgnoreCase = True
.Pattern = strPattern
End With
Set CreateRegex = rgxResult
End Function
You will have to customize the regex pattern based on your actual data.

RegEx for matching a special pattern in VB.net

I have code that extracts the text in different SGM files using the files Entity reference (&Ch1;). The code works great for this, but now it's expanded to need to get entity references for sectioned files with entity calls with this type of reference &Ch1-1; This can also grow to &Ch1-1-1;
I need to expand the code to accept these new entities so those files content can be added to the master file.
I believe the issue is the regular expression used so I changed it to
Dim rx = New Regex("&Ch(?<EntityNumber>\d+?[-\d+]?)?")
This doesn't create an error, but it also doesn't bring the file contents into the master document. I'm used to regular expressions but I've nevered used named capturing groups and found the explanations on the web a bit confusing.
Sub runProgram()
Dim DirFolder As String = txtDirectory.Text
Dim Directory As New IO.DirectoryInfo(DirFolder)
Dim allFiles As IO.FileInfo() = Directory.GetFiles("*.sgm")
Dim singleFile As IO.FileInfo
Dim Response As String
Dim Prefix As String
Dim newMasterFilePath As String
Dim masterFileName As String
Dim newMasterFileName As String
Dim startMark As String = "<!--#start#-->"
Dim stopMark As String = "<!--#stop#-->"
searchDir = txtDirectory.Text
Prefix = txtBxUnique.Text
For Each singleFile In allFiles
If File.Exists(singleFile.FullName) Then
Dim fileName = singleFile.FullName
Debug.Print("file name : " & fileName)
' A backup first
Dim backup As String = fileName & ".bak"
File.Copy(fileName, backup, True)
' Load lines from the source file in memory
Dim lines() As String = File.ReadAllLines(backup)
' Now re-create the source file and start writing lines inside a block
Dim insideBlock As Boolean = False
Using sw As StreamWriter = File.CreateText(backup)
For Each line As String In lines
If line = startMark Then
' start writing at the line below
insideBlock = True
ElseIf line = stopMark Then
' Stop writing
insideBlock = False
ElseIf insideBlock = True Then
' Write the current line in the block
sw.WriteLine(line)
End If
Next
End Using
End If
Next
masterFileName = Prefix & $"_Master_Document.sgm"
newMasterFileName = Prefix & $"_New_Master_Document.sgm"
newMasterFilePath = IO.Path.Combine(searchDir, newMasterFileName)
Dim existingMasterFilePath = IO.Path.Combine(searchDir, masterFileName)
'Read all text of the Master Document
'and create a StringBuilder from it.
'All replacements will be done on the
'StringBuilder as it is more efficient
'than using Strings directly
Dim strMasterDoc = File.ReadAllText(existingMasterFilePath)
Dim newMasterFileBuilder As New StringBuilder(strMasterDoc)
'Create a regex with a named capture group.
'The name is 'EntityNumber' and captures just the
'entity digits for use in building the file name
Dim rx = New Regex("&Ch(?<EntityNumber>\d+(-?\d*)*)?")
Dim rxMatches = rx.Matches(strMasterDoc)
For Each match As Match In rxMatches
Dim entity = match.ToString
'Build the file name using the captured digits from the entity in the master file
Dim entityFileName = Prefix & $"_Ch{match.Groups("EntityNumber")}.sgm.bak"
Dim entityFilePath = Path.Combine(searchDir, entityFileName)
'Check if the entity file exists and use its contents
'to replace the entity in the copy of the master file
'contained in the StringBuilder
If File.Exists(entityFilePath) Then
Dim entityFileContents As String = File.ReadAllText(entityFilePath)
newMasterFileBuilder.Replace(entity, entityFileContents)
End If
Next
'write the processed contents of the master file to a different file
File.WriteAllText(newMasterFilePath, newMasterFileBuilder.ToString)
Dim largeFilePath As String = newMasterFilePath
Dim lines1 = File.ReadLines(largeFilePath).ToList 'don't use ReadAllLines
Dim reg = New Regex("\<\!NOTATION.*$|\<\!ENTITY.*$", RegexOptions.IgnoreCase)
Dim entities = From line In lines1
Where reg.IsMatch(line)
Dim dictionary As New Dictionary(Of Integer, String)
Dim idx = -1
For Each s In entities
idx = lines1.IndexOf(s, idx + 1)
dictionary.Add(idx, s.Trim)
Next
Dim deletedItems = 0
For Each itm In dictionary
lines1.RemoveAt(itm.Key - deletedItems)
deletedItems += 1
Next
Dim uniqueDict = dictionary.GroupBy(Function(itm) itm.Value).
Select(Function(group) group.First()).
ToDictionary(Function(itm) itm.Key, Function(itm) itm.Value)
For Each s In uniqueDict.Values
lines1.Insert(1, s)
Next
Dim builtMaster As String = Prefix & "_FinalDeliverable.sgm"
Dim newBuiltMasterFilePath = IO.Path.Combine(searchDir, builtMaster)
Dim builtMasterDoc As String = newBuiltMasterFilePath
Using sw As New System.IO.StreamWriter(builtMasterDoc)
For Each line As String In lines1
sw.WriteLine(line)
Next
sw.Flush()
sw.Close()
End Using
'Delete the master document and new master document
If System.IO.File.Exists(existingMasterFilePath) = True Then
System.IO.File.Delete(existingMasterFilePath)
End If
If System.IO.File.Exists(newMasterFilePath) = True Then
System.IO.File.Delete(newMasterFilePath)
End If
For Each filename As String In IO.Directory.GetFiles(searchDir, "*.bak")
IO.File.Delete(filename)
Next
Response = MsgBox("File 'FinalDeliverable.sgm' has been created.", vbOKOnly, "SGM Status")
If Response = vbOK Then ' User chose Yes.
Close()
Else ' User chose No.
' Perform some action.
End If
End Sub
The results I'm expecting is for files with names Ch1-1.sgm content between and content will be added to a master file.
This does work for file entities that are &Ch1; it grabs Ch1.sgm content correctly.
Thank you for the help,
Maxine
Sample Code:
Master_Document.sgm
<!DOCTYPE DOC PUBLIC "-//USA-DOD//DTD 38784STD-BV7//EN"[
]>
&Ch1;
<body numcols="2">
&Ch2-1;
&Ch2-2;
&Ch2-3;
&Ch2-4;
&Ch2-5;
&Ch2-6;
&Ch2-7;
&Ch2-8;
&Ch2-9;
&Ch3;
</body></doc>
Sample SGM file
<?Pub /_gtinsert>
<body numcols="2">
<!--#start#-->
<chapter id="Chapter_4__Procedures">
<title>Procedures</title>
<section>
<title>Introduction</title>
<!--#stop#-->
<para0 verdate="7 Never 2012" verstatu
<title>Description</title>
<para>This chapterfor the following:
It turns out the problem is that &Ch(?<EntityNumber>\d+?[-\d+]?)? matched &Ch and then one or more, but as few as possible, digits (with \d+?) and then an optional single -, digit or + symbol. That is, after &Ch, only 1 digit was matched (as there is always a digit in your cases) and then a - was matched if it followed, and then matching stopped.
Use
Dim rx = New Regex("&Ch(?<EntityNumber>\d+(?:-\d+)*);")
See the regex demo and the regex graph:

outlook vba regex on each mail item in array

I am using the code below to create output showing how many emails were in a defined folder per day. This all works fine... My question is in the section with XXXXX, how do I reference each mail item so that I can do a regex for a word pattern? The end goal is to find out how many emails contained a keyword on a given day. The desired output is something like this:
,,
2015-01-01,15,2,5
2015-01-01,23,22,0
...
...
I'm ok to figure out the code on determining the number of emails based on the keyword, just not certain how to reference the email messages based on the code as is today...
Thanks for your advice.
Sub HowManyEmails()
Dim objOutlook As Object, objnSpace As Object, objFolder As MAPIFolder
Dim EmailCount As Integer
Set objOutlook = CreateObject("Outlook.Application")
Set objnSpace = objOutlook.GetNamespace("MAPI")
On Error Resume Next
Set objFolder = objnSpace.Folders("Personal Folders").Folders("Inbox").Folders("jobs.keep")
If Err.Number <> 0 Then
Err.Clear
MsgBox "No such folder."
Exit Sub
End If
EmailCount = objFolder.Items.Count
MsgBox "Number of emails in the folder: " & EmailCount, , "email count"
Dim dateStr As String
Dim myItems As Outlook.Items
Dim dict As Object
Dim msg As String
Set dict = CreateObject("Scripting.Dictionary")
Set myItems = objFolder.Items
myItems.SetColumns ("SentOn")
' Determine date of each message:
For Each myItem In myItems
xxxxxxx
xxxxxxx
xxxxxxx
dateStr = GetDate(myItem.SentOn)
If Not dict.Exists(dateStr) Then
dict(dateStr) = 0
End If
dict(dateStr) = CLng(dict(dateStr)) + 1
Next myItem
'Write output to file
Dim enviro As String
enviro = CStr(Environ("USERPROFILE"))
FILEPATH = enviro & "\Desktop\emails.csv"
Open FILEPATH For Output As 1
msg = ""
For Each o In dict.Keys
msg = msg & o & "," & dict(o) & vbCrLf
'MsgBox msg
Next
Print #1, msg
Close #1
'Write output to file
Set objFolder = Nothing
Set objnSpace = Nothing
Set objOutlook = Nothing
End Sub
Function GetDate(dt As Date) As String
GetDate = Year(dt) & "-" & Month(dt) & "-" & Day(dt)
End Function
You need to check the type of item in your code:
Dim myMailItem As Outlook.mailItem
....
For each myItem in myItems
If TypeOf myItem Is MailItem Then
Set myMailItem = myItem
XXXXXXXXXXX and rest of code here use myMailItem instead of myItem to get info
End If
Next myItem
First of all, I'd recommend using the Find/FindNext or Restrict methods of the Items class to find the subset of items that match to the specified condition. Iterating through all items in the folder may take a lot of time.
objnSpace.Folders("Personal Folders").Folders("Inbox")
Use the GetDefaultFolder method of the Namespace class to get a folder that represents the default folder of the requested type for the current profile.
Outlook uses EntryID values for identifying Outlook items uniquely. See Working with EntryIDs and StoreIDs for more information. If you know the IDs of an item and the folder it's stored in, you can directly reference the item using the NameSpace.GetItemFromID method.

Using regex in a libreoffice calc macro to extract text from parentheses in a cell

Using Libreoffice 3.5.7.2 on Ubuntu 12.04.
I have text in calc cells in the form of: (IBM) Ibm Corporation.
I am trying to use regex to extract the text between the ()'s using a basic macro. This is what I have tried so far.
Sub getMktValue()
Dim oDoc as Object
Dim oSheet as Object
Dim oCell as Object
oDoc = ThisComponent
oSheet = oDoc.Sheets.getByName("Income")
'regex test code'
oCell = oSheet.getCellByPosition(0, 1)
stk = oCell.String()
myRegex = oCell.createSearchDescriptor
myRegex.SearchRegularExpression = True
myRegex.SearchString = "\((.*)\)" '"[\([A-Z]\)]" "\(([^)]*)\)" "\(([^)]+)\)"'
found = oCell.FindFirst(myRegex)
MsgBox found.String
End Sub
The myRegex.SearchString line contains the various versions I have tried. The result is always the same. The entire contents of the cell are returned not just the text between the ()'s. Is there a way to extract just the text between the ()'s?
Thanks, Jim
The method you tried, .FindFirst, finds in an XSearchable (such as a spreadsheet or range) the first occurrence of the SearchString.
If you want to search within a string value, then you need a different service, com.sun.star.util.TextSearch.
Sub getMktValue()
Dim oDoc as Object
Dim oSheet as Object
Dim oCell as Object
oDoc = ThisComponent
oSheet = oDoc.Sheets.getByName("Income")
'regex test code'
oCell = oSheet.getCellByPosition(0, 1)
stk = oCell.getString()
oTextSearch = CreateUnoService("com.sun.star.util.TextSearch")
oOptions = CreateUnoStruct("com.sun.star.util.SearchOptions")
oOptions.algorithmType = com.sun.star.util.SearchAlgorithms.REGEXP
oOptions.searchString = "\((.*)\)"
oTextSearch.setOptions(oOptions)
oFound = oTextSearch.searchForward(stk, 0, Len(stk))
sFound = mid(stk, oFound.startOffset(0) + 1, oFound.endOffset(0) - oFound.startOffset(0))
MsgBox sFound
sFound = mid(stk, oFound.startOffset(1) + 1, oFound.endOffset(1) - oFound.startOffset(1))
MsgBox sFound
End Sub
Greetings
Axel

What is the RegExp Pattern to Extract Bullet Points Between Two Group Words using VBA in Word?

I can't seem to figure out the RegExp to extract the bullet points between two group of words in a word document.
For example:
Risk Assessment:
Test 1
Test 2
Test 3
Internal Audit
In this case I want to extract the bullet points between "Risk Assessment" and "Internal Audit", one bullet at a time and assign that bullet to an Excel cell. As shown in the code below I have pretty much everything done, except I cant figure out the correct Regex pattern. Any help would be great. Thanks in advance!
Sub PopulateExcelTable()
Dim fd As Office.FileDialog
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
.AllowMultiSelect = False
.Title = "Please select the file."
.Filters.Clear
.Filters.Add "Word 2007-2013", "*.docx"
If .Show = True Then
txtFileName = .SelectedItems(1)
End If
End With
Dim WordApp As Word.Application
Set WordApp = CreateObject("Word.Application")
Dim WordDoc As Word.Document
Set WordDoc = WordApp.Documents.Open(txtFileName)
Dim str As String: str = WordDoc.Content.Text ' Assign entire document content to string
Dim rex As New RegExp
rex.Pattern = "\b[^Risk Assessment\s].*[^Internal Audit\s]"
Dim i As long : i = 1
rex.Global = True
For Each mtch In rex.Execute(str)
Debug.Print mtch
Range("A" & i).Value = mtch
i = i + 1
Next mtch
WordDoc.Close
WordApp.Quit
End Sub
This is probably a long way around the problem but it works.
Steps I'm taking:
Find bullet list items using keywords before and after list in regexp.
(Group) regexp pattern so that you can extract everything in-between words.
Store listed items group into a string.
Split string by new line character into a new array.
Output each array item to excel.
Loop again since there may be more than one list in document.
Note: I don't see your code for a link to Excel workbook. I'll assume this part is working.
Dim rex As New RegExp
rex.Pattern = "(\bRisk Assessment\s)(.*)(Internal\sAudit\s)"
rex.Global = True
rex.MultiLine = True
rex.IgnoreCase = True
Dim lineArray() As String
Dim myMatches As Object
Set myMatches = rex.Execute(str)
For Each mtch In rex.Execute(str)
'Debug.Print mtch.SubMatches(1)
lineArray = Split(mtch.SubMatches(1), vbLf)
For x = LBound(lineArray) To UBound(lineArray)
'Debug.Print lineArray(x)
Range("A" & i).Value = lineArray(x)
i = i + 1
Next
Next mtch
My test page looks like this:
Results from inner Debug.Print line return this:
Item 1
Item 2
Item 3