Power query automation for multiple columns as inputs in vba in ms excel - combinations

I have multiple inputs in columns from which I need to create table for each column which has headers, Create connection using power query, Using these connections I need to add to the power query, and export it to sheet2. I have attached sample excel sheet for reference. Below is the half code for the same, I am not pro in vba if any one could help in solving the same. Here is the link for the same which I need to automate.
data set
Option Explicit
Public Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Dim lo As ListObject
Dim tableobjects As ListObject
Dim sName As String
Dim sFormula As String
Dim wq As WorkbookQuery
Dim bExists As Boolean
Dim vbAnswer As VbMsgBoxResult
Dim vbDataModel As VbMsgBoxResult
Dim i As Long
Dim j As Long
Dim k, l As Long
Dim dStart As Double
Dim dTime As Double
Dim CellAddr As String
Dim CellValue As String
Dim RangeAddr As String
Dim Temp As String
Dim TotalNumberInputs As Integer
Dim answer As Integer
Dim TableExists As Boolean
Dim ListObj As ListObject
Repeat:
TotalNumberInputs = InputBox("Enter the total inputs", "input int number")
If TotalNumberInputs = 0 Then
MsgBox "Invalid Input!!!"
answer = MsgBox("Invalid Input!!! Do you want to continue Macro?", vbQuestion + vbYesNo + vbDefaultButton2, "Next step")
If answer = vbYes Then
GoTo Repeat
Else
Exit Sub
End If
End If
'Set variables
dStart = Timer
Set wb = ActiveWorkbook
Set ws = ActiveSheet
'Check of table exist or else create table
For k = 1 To TotalNumberInputs
ws.Cells(1, k).Select
CellAddr = Selection.Address(RowAbsolute:=False, ColumnAbsolute:=False)
CellValue = Cells(1, l).Value ' "Table" & (k + "0") ' Selection.Value
On Error Resume Next
Set ListObj = ActiveSheet.ListObjects(CellValue)
On Error GoTo 0
If ListObj Is Nothing Then
Range(CellAddr).Select
Range(Selection, Selection.End(xlDown)).Select
RangeAddr = Selection.Address
Range(RangeAddr).Activate
ws.ListObjects.Add(SourceType:=xlSrcRange, Source:=Range(RangeAddr), LinkSource:=False, XlListObjectHasHeaders:=xlYes, tablestyleName:="TableStyleMedium28").Name = CellValue
Else
'If the table does exist clear filter from column C
' ActiveSheet.ListObjects(CellValue).Range.AutoFilter Field:=3
End If
Next k
l = 1
'Loop sheets and tables
' For Each ws In ActiveWorkbook.Worksheets
For Each lo In ws.ListObjects ' For j = 1 To TotalNumberInputs ' For Each lo In ws.ListObjects
sName = lo.Name ' ws.Cells(1, l).Value ' lo.Name
l = l + 1
sFormula = "Excel.CurrentWorkbook(){[Name=""" & sName & """]}[Content]"
'Check if query exists
bExists = False
For Each wq In wb.Queries
If InStr(1, wq.Formula, sFormula) > 0 Then
bExists = True
End If
Next wq
'Add query if it does not exist
If bExists = False Then
' Add a Query
wb.Queries.Add Name:=sName, Formula:= _
"let" & Chr(13) & "" & Chr(10) & " Source = Excel.CurrentWorkbook(){[Name=""" & sName & """]}[Content]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Source,{{""" & sName & """, type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Changed Type"""
'Add connection
wb.Connections.Add2 Name:="Query - " & sName, _
Description:="Connection to the '" & sName & "' query in the workbook.", _
ConnectionString:="OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & sName & ";Extended Properties=""""", _
CommandText:="SELECT * FROM [" & sName & "]", _
lCmdtype:=2, _
CreateModelConnection:=False, _
ImportRelationships:=False
'Count connections
i = i + 1
End If
Next lo ' Next j ' Next lo
'Next ws
'Calc run time
dTime = Timer - dStart
MsgBox i & " connections have been created in " & Format(dTime, "0.0") & " seconds.", vbOKOnly, "Process Complete"
End Sub

Related

Select text strings with multiple formatting tags within

Context:
VB.NET application using htmlagility pack to handle html document.
Issue:
In a html document, I'd like to prefixe all the strings starting with # and ending with space by an url whatever formatting tags are used within.
So #sth would became http://www.anything.tld/sth
For instance:
Before:
<p>#string1</p> blablabla
<p><strong>#stri</strong>ng2</p> bliblibli
After:
<p>#string1 blablabla</p>
<p><strong>#stri</strong>ng2 bliblibli</p>
I guess i can achieve this with html agility pack but how to select the entire text string without its formatting ?
Or should i use a simple regex replace routine?
Here's my solution. I'm sure it would make some experienced developpers bleed from every hole but it actually works.
The htmlcode is in strCorpusHtmlContent
Dim matchsHashtag As MatchCollection
Dim matchHashtag As Match
Dim captureHashtag As Capture
Dim strHashtagFormatted As String
Dim strRegexPatternHashtag As String = "#([\s]*)(\w*)"
matchsHashtag = Regex.Matches(strCorpusHtmlContent, strRegexPatternHashtag)
For Each matchHashtag In matchsHashtag
For Each captureHashtag In matchHashtag.Captures
Dim strHashtagToFormat As String
Dim strHashtagValueToFormat As String
' Test if the hashtag is followed by a tag
If Mid(strCorpusHtmlContent, captureHashtag.Index + captureHashtag.Length + 1, 1) = "<" Then
strHashtagValueToFormat = captureHashtag.Value
Dim intStartPosition As Integer = captureHashtag.Index + captureHashtag.Length + 1
Dim intSpaceCharPostion As Integer = intStartPosition
Dim nextChar As Char
Dim blnInATag As Boolean = True
Do Until (nextChar = " " Or nextChar = vbCr Or nextChar = vbLf Or nextChar = vbCrLf) And blnInATag = False
nextChar = CChar(Mid(strCorpusHtmlContent, intSpaceCharPostion + 1, 1))
If nextChar = "<" Then
blnInATag = True
ElseIf nextChar = ">" Then
blnInATag = False
End If
If blnInATag = False And nextChar <> ">" And nextChar <> " " Then
strHashtagValueToFormat &= nextChar
End If
intSpaceCharPostion += 1
Loop
strHashtagToFormat = Mid(strCorpusHtmlContent, captureHashtag.Index + 1, intSpaceCharPostion - captureHashtag.Length)
Else
strHashtagToFormat = captureHashtag.Value
End If
strHashtagFormatted = "" & strHashtagToFormat & ""
strCorpusHtmlContent = Regex.Replace(strCorpusHtmlContent, strHashtagToFormat, strHashtagFormatted)
Next
Next
Before:
<p>#has<strong>hta</strong><em>g_m</em>u<span style="text-decoration: underline;">ltifortmat</span> to convert</p>
After:
<p>#has<strong>hta</strong><em>g_m</em>u<span style="text-decoration: underline;">ltiformat</span> to convert</p>

Extract text from 2 strings from selected Outlook email

I have code to import email body data from Outlook to Excel. I only need Name, ID, code from the email.
I have done everything except to extract the ID from a fixed sentence:
cn=SVCLMCH,OU=Users,OU=CX,DC=dm001,DC=corp,DC=dcsa,DC=com
The id is SVCLMCH in this case, that means I need to extract the text between "cn=" and ",OU=Users".
Sub import_code()
Dim O As Outlook.Application
Set O = New Outlook.Application
Dim ONS As Outlook.Namespace
Set ONS = O.GetNamespace("MAPI")
Dim OMAIL As Outlook.MailItem
Set OMAIL = Nothing
Dim ws As Object
Set ws = ThisWorkbook.Worksheets("Import code from Outlook")
Dim rcount As Long
Dim vText As Variant
Dim sText As String
Dim i As Long
If O.ActiveExplorer.Selection.Count = 0 Then
msgbox "No Items selected!", vbCritical, "Error"
End If
On Error Resume Next
'Process each selected record
rcount = ws.UsedRange.Rows.Count
For Each OMAIL In O.ActiveExplorer.Selection
sText = OMAIL.Body
vText = Split(sText, Chr(13))
'Find the next empty line of the worksheet
rcount = rcount + 1
'Check each line of text in the message body
For i = UBound(vText) To 0 Step -1
If InStr(1, vText(i), "Password Generated and set for:") > 0 Then
vItem = Split(vText(i), Chr(58))
ws.Range("A" & rcount) = Trim(vItem(1))
End If
If InStr(1, vText(i), "cn=") > 0 Then
vItem = Split(vText(i), Chr(58))
ws.Range("b" & rcount) = Trim(vItem(1))
End If
If InStr(1, vText(i), "Password:") > 0 Then
vItem = Split(vText(i), Chr(58))
ws.Range("c" & rcount) = Trim(vItem(1))
End If
Next i
Next OMAIL
End Sub
The trick here is to use the Split() function
Dim Arr() As String
Dim j As Integer
Dim k As Integer
Dim strvar As String
Dim strval As String
Dim strID As String
If InStr(1, vtext(i), "cn=") > 0 Then
' split the whole line in an array - "," beeing the value separator
Arr = Split(vtext(i), ",")
' loop through all array elements
For j = 0 To UBound(r) - 1
' find the position of =
k = InStr(Arr(j), "=")
strvar = Left(Arr(j), k - 1) ' this is the variable name, ie "cn"
strval = Mid(Arr(j), k + 1) ' this is the value, ie "SVCLMCH"
' now do what you want with a specific variable
Select Case strvar
Case "cn"
strID = strval
Case Else
' do nothing
End Select
Next j
End If
you can use a helper function like this:
Function GetID(strng As String)
Dim el As Variant
For Each el In Split(strng, ",")
If InStr(1, el, "cn=") > 0 Then
GetID = Mid(el, InStr(1, el, "cn=") + 3)
Exit Function
End If
Next
End Function
and your main code would exploit it as:
If InStr(1, vText(i), "cn=") > 0 Then ws.Range("b" & rcount) = GetID(CStr(vText(i)))
Use Regular Expression extract the ID from the sentence
Example Pattern = "(?<=cn=)([^.]+)(?=\,OU=Users)"
https://regex101.com/r/67u84s/2
Code Example
Option Explicit
Private Sub Examplea()
Dim Matches As Variant
Dim RegEx As Object
Set RegEx = CreateObject("VbScript.RegExp")
Dim olApp As Object
Set olApp = CreateObject("Outlook.Application")
Dim Item As Outlook.MailItem
Set Item = olApp.ActiveExplorer.Selection.Item(1)
Dim Pattern As String
Pattern = "(?<=cn=)([^.]+)(?=\,OU=Users)"
With RegEx
.Global = False
.Pattern = Pattern
.IgnoreCase = True
Set Matches = .Execute(Item.Body)
End With
If Matches.Count > 0 Then
Debug.Print Matches(0).SubMatches(0)
With ThisWorkbook.Sheets("Sheet1")
.Range("A1").Value = Trim(Matches(0).SubMatches(0))
End With
End If
End Sub

Regular Expression to Test Date VBA

I am looking for a code to test date Format, the date should be in one of these formats
year : 13xx - 20xx
month: xx,x
day: xx,x
the hole date would be on of the following
2012/1/1
2012/01/01
2012/1/01
2012/01/1
I tried the following
Option Explicit
Sub ttt()
MsgBox (testDate("2012/01/01"))
End Sub
Function testDate(strDateToBeTested As String) As Boolean
Dim regularExpression, match
Set regularExpression = CreateObject("vbscript.regexp")
testDate = False
'regularExpression.Pattern = "(14|13|19|20)[0-9]{2}[- /.]([0-9]{1,2})[- /.]([0-9]{1,2})"
'regularExpression.Pattern = "(\d\d\d\d)/(\d|\d\d)/(\d|/dd)"
regularExpression.Pattern = "([0-9]{4}[ /](0[1-9]|[12][0-9]|3[01])[ /](0[1-9]|1[012]))"
regularExpression.Global = True
regularExpression.MultiLine = True
If regularExpression.Test(strDateToBeTested) Then
' For Each match In regularExpression.Execute(strDateToBeTested)
If Len(strDateToBeTested) < 10 Then
testDate = True
' Exit For
End If
'End If
End If
Set regularExpression = Nothing
End Function
The more and more I thought about this (and some research), the more I figured that regex is not the best solution to this format problem. Combining a couple of other ideas (with the ReplaceAndSplit function attributed to the owner), this is what I came up with.
Option Explicit
Sub ttt()
Dim dateStr() As String
Dim i As Integer
dateStr = Split("2012/1/1,2012/01/01,2012/1/01,2012/01/1,1435/2/2," & _
"1435/02/02,1900/07/07,1435/02/02222222,2015/Jan/03", ",")
For i = 1 To UBound(dateStr)
Debug.Print "trying '" & dateStr(i) & "' ... " & testDate(dateStr(i))
Next i
End Sub
Function testDate(strDateToBeTested As String) As Boolean
Dim dateParts() As String
Dim y, m, d As Long
dateParts = ReplaceAndSplit(strDateToBeTested, "/.-")
testDate = False
If IsNumeric(dateParts(0)) Then
y = Int(dateParts(0))
Else
Exit Function
End If
If IsNumeric(dateParts(1)) Then
m = Int(dateParts(1))
Else
Exit Function
End If
If IsNumeric(dateParts(2)) Then
d = Int(dateParts(2))
Else
Exit Function
End If
If (y >= 1435) And (y < 2020) Then 'change or remove the upper limit as needed
If (m >= 1) And (m <= 12) Then
If (d >= 1) And (d <= 30) Then
testDate = True
End If
End If
End If
End Function
'=======================================================
'ReplaceAndSplit by alainbryden, optimized by aikimark
'Uses the native REPLACE() function to replace all delimiters with a common
'delimiter, and then splits them based on that.
'=======================================================
Function ReplaceAndSplit(ByRef Text As String, ByRef DelimChars As String) As String()
Dim DelimLen As Long, Delim As Long
Dim strTemp As String, Delim1 As String, Arr() As String, ThisDelim As String
strTemp = Text
Delim1 = Left$(DelimChars, 1)
DelimLen = Len(DelimChars)
For Delim = 2 To DelimLen
ThisDelim = Mid$(DelimChars, Delim, 1)
If InStr(strTemp, ThisDelim) <> 0 Then _
strTemp = Replace(strTemp, ThisDelim, Delim1)
Next
ReplaceAndSplit = Split(strTemp, Delim1)
End Function

How can I sort a VBA MatchCollection by value of SubMatches(n)?

I am relatively new to programming, and I wrote a Microsoft Word VBA macro that extracts a "parts list" from a patent description (the text of the active document), where each part reference in the list is identified in a rudimentary way as anything that looks like a numeric or all-caps alpha identifier of a part or feature preceded by up to four words in the same sentence.
What I have succeeded in doing so far is automatically opening a new Word document and inserting all unique part references line by line, in a format like
"10: providing a sewing machine 10," or "Q: of a heat flux Q."
I repeat the identifier at the beginning of each line so that the identifiers appear aligned at the left margin.
I also would like them to be sorted by identifier, which is m.SubMatches(2) of my regular expression MatchCollection m. First the numbers in numerical order, then the alpha references in alphabetical order would be nice.
Any suggestions on how to go about this? Here is a code snippet that sorts by the entire m.Value using a simple bubble-sort algorithm, without bothering to convert numeric identifiers to Long values:
Sub ExtractPartsList()
Dim re As VBScript_RegExp_55.RegExp
Set re = New VBScript_RegExp_55.RegExp
re.pattern = "((?:[A-Z]*[a-z]+[\s\n]+){0,3})(?=[A-Z]*[a-z]+[\s\n]+(?:\d+\b|[A-Z]+\b))" + _
"(\b[A-Z]*[a-z]+[\s\n]+)(\b\d+\b'*|[A-Z]+\b'*)" + _
"((?:\,[\s\n]+(?:\d+|[A-Z]+\b))+(?:\,?[\s\n]+and[\s\n+](?:\d+|[A-Z]+\b))?)?(?:[\s\n]+and[\s\n]+(?:\d+|[A-Z]+\b))?"
' m.Value is the whole matched string
' m.SubMatches(1) is the word immediately preceding the part number / alpha reference
' m.SubMatches(2) is the part number / alpha reference
re.IgnoreCase = False
re.Global = True
Dim txt As String
Dim bigString As String
bigString = ""
Dim allLongMatches As MatchCollection, m As Match
Dim partNameLastWord As String
Dim partReference As String
Dim partNameAndReference As String
Dim partsColl As New Collection
Dim partsList() As String
Dim i As Long
txt = ActiveDocument.Range.text
If re.Test(txt) Then
Set allLongMatches = re.Execute(txt)
Documents.Add DocumentType:=wdNewBlankDocument
For Each m In allLongMatches
Debug.Print m.Value, "Sbm 1 = " + m.SubMatches(1), "Sbm 2 = " + m.SubMatches(2), "Sbm 3 = " + m.SubMatches(3)
If InStr(bigString, LCase(m.SubMatches(1) + m.SubMatches(2))) = 0 _
And InStr(LCase(m.Value), "of claim " + m.SubMatches(2)) = 0 _
And InStr(LCase(m.SubMatches(2)), "fig") = 0 Then
bigString = bigString + LCase(m.Value)
partsColl.Add m.SubMatches(2) + ": " + m.Value
End If
Next m
End If
ReDim partsList(1 To partsColl.Count)
For i = 1 To partsColl.Count
partsList(i) = partsColl(i)
Next i
' BubbleSort (partsList())
' Instead of calling BubbleSort (partsList())
' I apparently still have to learn how to properly call methods I
' have written - for now I am just embedding it here:
Dim strTemp As String
' Dim i As Long
Dim j As Long
Dim lngMin As Long
Dim lngMax As Long
lngMin = LBound(partsList())
lngMax = UBound(partsList())
For i = lngMin To lngMax - 1
For j = i + 1 To lngMax
If partsList(i) > partsList(j) Then
strTemp = partsList(i)
partsList(i) = partsList(j)
partsList(j) = strTemp
End If
Next j
Next i
For i = 1 To partsColl.Count
Selection.InsertAfter (partsList(i))
Selection.InsertParagraphAfter
Next i
End Sub
Sub BubbleSort(arr)
Dim strTemp As String
Dim i As Long
Dim j As Long
Dim lngMin As Long
Dim lngMax As Long
lngMin = LBound(arr)
lngMax = UBound(arr)
For i = lngMin To lngMax - 1
For j = i + 1 To lngMax
If arr(i) > arr(j) Then
strTemp = arr(i)
arr(i) = arr(j)
arr(j) = strTemp
End If
Next j
Next i
End Sub
Sample input from U.S. Pat. No. 6,293,874:
"The second post 44 is positioned a sufficient distance from the first post 24 to permit the user to require the user to bend forward at the waist in a stooped position between the posts 24, 44. The user is thus positioned to predominantly present his or her buttocks B toward the plurality of rotating arms 56 that are detachably mounted on the second post 44 at a height generally level with the user's buttocks. The second post 44 is mountable on the surface of the platform 12 by a detachable collar 46 and connector bolts or screws."
Output (only works nicely because the numbers are the same length - I imagine it is really sorting "alphabetically," where "2" would come after "19," for example):
' 12: surface of the platform 12
' 24: from the first post 24
' 24: position between the posts 24, 44
' 44: The second post 44
' 46: by a detachable collar 46
' 56: plurality of rotating arms 56
' B: his or her buttocks B
I made a klunky solution that works by creating a separate array of the part identifiers by themselves and sorting the partsList() array in parallel with the identifier array id() As Long. Setting alpha identifiers to zero for now and letting them percolate to the top unsorted; there are not usually enough of them to worry about sorting alphabetically. I hesitate to mark this as an answer, as I would still like to see if someone will chime in with a more elegant/direct solution.
Sub ExtractPartsList()
Dim re As VBScript_RegExp_55.RegExp
Set re = New VBScript_RegExp_55.RegExp
re.pattern = "((?:[A-Z]*[a-z]+[\s\n]+){0,3})(?=[A-Z]*[a-z]+[\s\n]+(?:\d+\b|[A-Z]+\b))" + _
"(\b[A-Z]*[a-z]+[\s\n]+)(\b\d+\b'*|[A-Z]+\b'*)" + _
"((?:\,[\s\n]+(?:\d+|[A-Z]+\b))+(?:\,?[\s\n]+and[\s\n+](?:\d+|[A-Z]+\b))?)?(?:[\s\n]+and[\s\n]+(?:\d+|[A-Z]+\b))?"
' m.Value is the whole matched string
' m.SubMatches(1) is the word immediately preceding the part number / alpha reference
' m.SubMatches(2) is the part number / alpha reference
re.IgnoreCase = False
re.Global = True
Dim txt As String
Dim bigString As String
bigString = ""
Dim allLongMatches As MatchCollection, m As Match
Dim partNameLastWord As String
Dim partReference As String
Dim partNameAndReference As String
Dim partsColl As New Collection
Dim idColl As New Collection
' for now not using this variable:
' Dim referenceTextColl As New Collection
Dim partsList() As String
Dim id() As Long
' Dim referenceText() As String
' Dim partsListSorted() As String
Dim i As Long
txt = ActiveDocument.Range.text
If re.Test(txt) Then
Set allLongMatches = re.Execute(txt)
Documents.Add DocumentType:=wdNewBlankDocument
For Each m In allLongMatches
Debug.Print m.Value, "Sbm 1 = " + m.SubMatches(1), "Sbm 2 = " + m.SubMatches(2), "Sbm 3 = " + m.SubMatches(3)
If InStr(bigString, LCase(m.SubMatches(1) + m.SubMatches(2))) = 0 _
And InStr(LCase(m.Value), "of claim " + m.SubMatches(2)) = 0 _
And InStr(LCase(m.SubMatches(2)), "fig") = 0 Then
bigString = bigString + LCase(m.Value)
partsColl.Add m.SubMatches(2) + ": " + m.Value
idColl.Add (m.SubMatches(2))
' referenceTextColl.Add (m.Value)
' Selection.InsertAfter (m.SubMatches(2) + ": ")
' Selection.InsertAfter (m.Value)
' Selection.InsertParagraphAfter
End If
Next m
End If
ReDim partsList(1 To partsColl.Count)
ReDim id(1 To partsColl.Count)
' ReDim referenceText(1 To partsColl.Count)
For i = 1 To partsColl.Count
partsList(i) = partsColl(i)
id(i) = 0
' Deal with "prime" symbols #' and convert numeric identifiers to Long:
If IsNumeric(Replace(idColl(i), "'", "")) Then id(i) = CLng(Replace(idColl(i), "'", ""))
referenceText(i) = referenceTextColl(i)
Next i
'
' I apparently still have to learn how to properly call methods I
' have written - I am just embedding a bubble sort algorithm here instead:
Dim idTemp As String
Dim referenceTemp As String
Dim partsListLineTemp As String
' Dim i As Long
Dim j As Long
Dim lngMin As Long
Dim lngMax As Long
lngMin = LBound(partsList())
lngMax = UBound(partsList())
For i = lngMin To lngMax - 1
For j = i + 1 To lngMax
If id(i) > id(j) Then
idTemp = id(i)
partsList(i) = partsList(j)
id(i) = id(j)
partsList(j) = partsListLineTemp
id(j) = idTemp
End If
Next j
Next i
For i = 1 To partsColl.Count
Selection.InsertAfter (partsList(i))
Selection.InsertParagraphAfter
Next i
partsListLineTemp = partsList(i)
End Sub

Find text between two static strings

I parse message data into a CSV file via Outlook rules.
How can I take the example below and store the text under "Customer Log Update:" into a string variable?
[Header Data]
Description: Problem: A2 - MI ERROR - R8036
Customer Log Update:
I'm having trouble with order #458362. I keep getting Error R8036, can you please assist?
Thanks!
View problem at http://...
[Footer Data]
Desired result to be stored into the string variable (note that the result may contain newlines):
I'm having trouble with order #458362. I keep getting Error R8036, can you please assist?
Thanks!
I haven't attempted to code anything pertaining to my question.
Function RegFind(RegInput, RegPattern)
Dim regEx As New VBScript_RegExp_55.RegExp
Dim matches, s
regEx.Pattern = RegPattern
regEx.IgnoreCase = True
regEx.Global = False
s = ""
If regEx.Test(RegInput) Then
Set matches = regEx.Execute(RegInput)
For Each Match In matches
s = Match.Value
Next
RegFind = s
Else
RegFind = ""
End If
End Function
Sub CustomMailMessageRule(Item As Outlook.MailItem)
MsgBox "Mail message arrived: " & Item.Subject
Const FileWrite = file.csv `file destination
Dim FF1 As Integer
Dim subj As String
Dim bod As String
On Error GoTo erh
subj = Item.Subject
'this gets a 15 digit number from the subject line
subj = RegFind(subj, "\d{15}")
bod = Item.Body
'following line helps formatting, lots of double newlines in my source data
bod = Replace(bod, vbCrLf & vbCrLf, vbCrLf)
'WRITE FILE
FF1 = FreeFile
Open FileWrite For Append As #FF1
Print #FF1, subj & "," & bod
Close #FF1
Exit Sub
erh:
MsgBox Err.Description, vbCritical, Err.Number
End Sub
While I would also go the more direct route like Jean-François Corbett did as the parsing is very simple, you could apply the Regexp approach as below
The pattern
Update:([\S\s]+)view
says match all characters between "Update" and "view" and return them as a submatch
This piece [\S\s] says match all non-whitespace or whitespace characters - ie everything.
In vbscript a . matches everything but a newline, hence the need for the [\S\s] workaround for this application
The submatch is then extracted by
objRegM(0).submatches(0)
Function ExtractText(strIn As String)
Dim objRegex As Object
Dim objRegM As Object
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.ignorecase = True
.Pattern = "Update:([\S\s]+)view"
If .test(strIn) Then
Set objRegM = .Execute(strIn)
ExtractText = objRegM(0).submatches(0)
Else
ExtractText = "No match"
End If
End With
End Function
Sub JCFtest()
Dim messageBody As String
Dim result As String
messageBody = "Description: Problem: A2 - MI ERROR - R8036" & vbCrLf & _
"Customer Log Update:" & _
"I 'm having trouble with order #458362. I keep getting Error R8036, can you please assist?" & vbCrLf & _
"Thanks!" & vbCrLf & _
"View problem at http://..."
MsgBox ExtractText(messageBody)
End Sub
Why not something simple like this:
Function GetCustomerLogUpdate(messageBody As String) As String
Const sStart As String = "Customer Log Update:"
Const sEnd As String = "View problem at"
Dim iStart As Long
Dim iEnd As Long
iStart = InStr(messageBody, sStart) + Len(sStart)
iEnd = InStr(messageBody, sEnd)
GetCustomerLogUpdate = Mid(messageBody, iStart, iEnd - iStart)
End Function
I tested it using this code and it worked:
Dim messageBody As String
Dim result As String
messageBody = "Description: Problem: A2 - MI ERROR - R8036" & vbCrLf & _
"Customer Log Update:" & vbCrLf & _
"I 'm having trouble with order #458362. I keep getting Error R8036, can you please assist?" & vbCrLf & _
"Thanks!" & vbCrLf & _
"View problem at http://..."
result = GetCustomerLogUpdate(messageBody)
Debug.Print result