Below is a function built by others that changes text into sentence case (first letter of each sentence capitalized). The function works nicely except it doesn't capitalize the first letter of the first word. Another issue is that if a sentence is entered in all caps, the function does nothing. I'm looking for some assistance in tweaking the function to correct these issues.
Option Explicit
Function ProperCaps(strIn As String) As String
Dim objRegex As Object
Dim objRegMC As Object
Dim objRegM As Object
Set objRegex = CreateObject("vbscript.regexp")
strIn = LCase$(strIn)
With objRegex
.Global = True
.ignoreCase = True
.Pattern = "(^|[\.\?\!\r\t]\s?)([a-z])"
If .test(strIn) Then
Set objRegMC = .Execute(strIn)
For Each objRegM In objRegMC
Mid$(strIn, objRegM.firstindex + 1, objRegM.Length) = UCase$(objRegM)
Next
End If
End With
ProperCaps = strIn
End Function
Thanks,
Gary
I renamed the function to SentenceCase() and made a few more adjustments:
Public Function SentenceCase(ByVal str As String) As String
Dim regEx As Object, regExM As Object, indx As Object, indxs As Object
Set regEx = CreateObject("VBScript.RegExp")
str = Replace$(str, vbNullChar, vbLf)
str = Replace$(str, vbBack, vbLf)
str = LTrim$(LCase$(str))
With regEx
.IgnoreCase = True
.MultiLine = True
.Global = True
.Pattern = "(^|[\n\f\r\t\v\.\!\?]\s*)(\w)"
If .Test(str) Then
Set indxs = .Execute(str)
For Each indx In indxs
Mid$(str, indx.FirstIndex + 1, indx.Length) = UCase$(indx)
Next
End If
End With
SentenceCase = str
End Function
This is what I tested it with:
MsgBox SentenceCase(" UPPER CASE SENTENCE." & _
vbCrLf & "next line!nEXT sENTENCE" & _
vbCr & "cr ! lower case" & _
vbLf & "lf .new sentence" & _
vbNullChar & " null?null char" & _
vbNullString & "nullString spaces" & _
vbTab & "TAB CHAR.ttt" & _
vbBack & "back? back char" & _
vbFormFeed & "ff ff words" & _
vbVerticalTab & "vertical tab.| lower .case words")
Results:
You can find more details here: Microsoft - Regular Expressions
Paul thank you for taking the time to help. I gave up and searched the net some more found a workable sub, received help from another bulletin board and came up with the following:
Sub SentenceCase(rng As Range)
Dim V As Variant
Dim s As String
Dim Start As Boolean
Dim i As Long
Dim ch As String
Application.ScreenUpdating = False
Application.EnableEvents = False
ActiveSheet.Unprotect
With rng
V = .Value
If IsDate(V) Or IsNumeric(V) Then Exit Sub
s = CStr(V)
Start = True
For i = 1 To Len(s)
ch = Mid$(s, i, 1)
Select Case ch
Case "."
Start = True
Case "?"
Start = True
Case "!"
Start = True
Case "a" To "z"
If Start Then ch = UCase$(ch)
Start = False
Case "A" To "Z"
If Start Then
Start = False
Else
ch = LCase$(ch)
End If
End Select
Mid$(s, i, 1) = ch
Next i
.Value = s
End With
ActiveSheet.Protect
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
This code is doing what I needed. Again thank you for your help.
Gary
Related
I have a function that extract email addresses from a variant and I would like to put each email address to a cell but I'm always getting type mismatch error when I use something like
Range("A9").Value = Range("A9").Value & ExtractEmailAddresses(Contact) & "; "
Currently, the cell value is being updated by the last element in my array.. what I want is to concatenate each array element instead like "test1#gmail.com; test2#gmail.com". Can someone help me with this, thanks!
Here's my sample code:
Dim LArray() As String
Dim CCArray() As String
Dim Contact As Variant
Dim strCC As String
LArray = splitLineBreaks(Range("A4").Value)
If IsNull(LArray) = False Then
For i = 0 To UBound(LArray)
If InStr(LArray(i), "CC:") Then
strCC = LArray(i)
CCArray = Split(strCC, ">")
For Each Contact In CCArray
Range("A9").Value = Range("A9").Value & ExtractEmailAddresses(Contact)
Next
End If
Next i
End If
Extract Email Address function:
Public Function ExtractEmailAddresses(ByVal sInput As Variant) As Variant
On Error GoTo Error_Handler
Dim oRegEx As Object
Dim oMatches As Object
Dim oMatch As Object
Dim sEmail As String
If Not IsNull(sInput) Then
Set oRegEx = CreateObject("vbscript.regexp")
With oRegEx
'Basic pattern
'.Pattern = "([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)"
'More advanced pattern that allow accented characters
.Pattern = "([a-zA-ZF0-9\u00C0-\u017F._-]+#[a-zA-Z0-9\u00C0-\u017F._-]+\.[a-zA-Z0-9\u00C0-\u017F_-]+)"
.Global = True
.IgnoreCase = True
.MultiLine = True
Set oMatches = .Execute(sInput)
End With
For Each oMatch In oMatches
sEmail = oMatch.Value & "," & sEmail
Next oMatch
If Right(sEmail, 1) = "," Then sEmail = Left(sEmail, Len(sEmail) - 1)
ExtractEmailAddresses = Split(sEmail, ",") 'Return an array of email addresses extracted from sInput
Else
ExtractEmailAddresses = Null
End If
Error_Handler_Exit:
On Error Resume Next
If Not oMatch Is Nothing Then Set oMatch = Nothing
If Not oMatches Is Nothing Then Set oMatches = Nothing
If Not oRegEx Is Nothing Then Set oRegEx = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: ExtractEmailAddresses" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
Function splitLineBreaks(ByVal str As String) As Variant
str = Replace(str, vbCrLf, vbCr)
str = Replace(str, vbLf, vbCr)
splitLineBreaks = Split(str, vbCr)
End Function
Sample data from A4
I am working with Macros and VBA for the first time for work, I have some limited python experience.
I am trying to find and pick out all the cheques in bank data from prior periods and assign them the category "Accounts Payable Cheques". However, when I run my code below, only one cell populates and the left are left with nothing, however there should be alot of cells populated.
I am wondering where I am going wrong?
Function trim_all(s) As String
s = Trim(s)
s = Replace(s, " ", " ")
s = Replace(s, " ", " ")
s = Replace(s, " ", " ")
s = Replace(s, " ", " ")
trim_all = Replace(s, " ", " ")
End Function
Public Sub regex()
Dim regexObject As RegExp
Set regexObject = New RegExp
Dim CHQ As Boolean
Dim curr As String
Dim description As String
Range("i2").Activate
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
i = True
Do While i = True
' get values'
account = ActiveCell.Offset(0, -8).Value
curr = ActiveCell.Offset(0, -7).Value
description = trim_all(ActiveCell.Offset(0, -4).Value)
debit = ActiveCell.Offset(0, -3).Value
credit = ActiveCell.Offset(0, -2).Value
'Have to define the regex pattern as a string
Dim regexPattern As String: regexPattern = "{chq|cheque|^\d{10}$|^\d{5}$}"
'Quick check that the regex pattern is not empty (maybe supply the pattern as a function in the future?)
If regexPattern <> "" Then
'Set flags and pattern--very similar to how it's done in Python
With regexObject
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = regexPattern
End With
'Test the regex; if it matches then set CHQ=True, otherwise CHQ=False
If regexObject.Test(description) Then
CHQ = True
Else
CHQ = False
End If
End If
'Accounts Payable Cheques - Categorization'
If CHQ = True And curr = "CAD" Then
ActiveCell.Value = "Accounts Payable Cheques, CAD"
ElseIf CHQ = True And curr = "USD" Then
ActiveCell.Value = ""
End If
' continue?'
next_line = ActiveCell.Offset(1, -7).Value
If next_line = "" Then
i = False
Else
' go down 1 cell
ActiveCell.Offset(1, 0).Activate
End If
Loop
Application.Calculation = xlCalculationAutomatic
Application.Calculate
Application.ScreenUpdating = True
MsgBox ("Regex check done!")
End Sub
I'm trying to make a macro that will combine two cells, First Name and Last Name, to make Full Name.
I did find a code that does a similar thing, except with no space in between the strings. I tried to edit it to add the space but I think I'm doing it wrong. I'll paste it in in case anyone knows how to edit it to add the spaces.
Obviously if that code was not made to add spaces, please let me know if there is one that does!
Thank you!
(Here's the code for no spaces)
Sub BacsRef()
Dim rSelected As Range
Dim c As Range
Dim sArgs As String
Dim bCol As Boolean
Dim bRow As Boolean
Dim sArgSep As String
Dim sSeparator As String
Dim rOutput As Range
Dim vbAnswer As VbMsgBoxResult
Dim lTrim As Long
Dim sTitle As String
Set rOutput = ActiveCell
bCol = False
bRow = False
sSeparator = ""
sTitle = IIf(bConcat, "CONCATENATE", "Ampersand")
On Error Resume Next
Set rSelected = Application.InputBox(Prompt:= _
"Select cells to create formula", _
Title:=sTitle & " Creator", Type:=8)
On Error GoTo 0
If Not rSelected Is Nothing Then
sArgSep = IIf(bConcat, ",", "&")
If bOptions Then
vbAnswer = MsgBox("Columns Absolute? $A1", vbYesNo)
bCol = IIf(vbAnswer = vbYes, True, False)
vbAnswer = MsgBox("Rows Absolute? A$1", vbYesNo)
bRow = IIf(vbAnswer = vbYes, True, False)
sSeparator = Application.InputBox(Prompt:= _
"Type separator, leave blank if none.", _
Title:=sTitle & " separator", Type:=2)
End If
For Each c In rSelected.Cells
sArgs = sArgs & c.Address(bRow, bCol) & sArgSep
If sSeparator <> "" Then
sArgs = sArgs & Chr(34) & sSeparator & Chr(34) & sArgSep
End If
Next
lTrim = IIf(sSeparator <> "", 4 + Len(sSeparator), 1)
sArgs = Left(sArgs, Len(sArgs) - lTrim)
If bConcat Then
rOutput.Formula = "=CONCATENATE(" & sArgs & ")"
Else
rOutput.Formula = "=" & sArgs
End If
End If
End Sub
Edit: I did it myself! I added a space in the line " sSeparator = "" " within the quotation marks!
I am new to VB and have a problem.
I have a text file named data.txt. It has 1 lines in it
IamanewstudentHeisanewstudentthestudentinthisclassisveryfunnythisuniversityhave300studentthestudentisveryfriendlywithnewcommer
I write a script which reads this text file and look for the string such as "stutent" and print all the "student" we can found in cell in excel (B1,C1,D1....). In this example we have 5 "student". It will display in cell B1,C1,D1,E1,F1 in sheet.
I tried till this point but it just give me only one "student" not five.
Sub SearchTextFile()
Const strFileName = "C:\data.txt"
Const strSearch = "student"
Dim strLine As String
Dim f As Integer
Dim lngLine As Long
Dim blnFound As Boolean
Dim lPosition As Long
f = FreeFile
Open strFileName For Input As #f
Do While Not EOF(f)
lngLine = lngLine + 1
Line Input #f, strLine
If InStr(1, strLine, strSearch, vbBinaryCompare) > 0 Then
blnFound = True
lPosition = InStr(1, strLine, strSearch, vbTextCompare)
MsgBox "Search string found" & strSearch, vbInformation
Exit Do
End If
Loop
Close #f
If Not blnFound Then
MsgBox "Search string not found", vbInformation
End If
End Sub
I would use RegEx to count the number of occurences in the line with the following function
Function noInStr(line As String, pattern As String) As Long
Dim regEx As Object, matches As Object
Set regEx = CreateObject("vbscript.regexp")
With regEx
.MultiLine = False
.Global = True
.IgnoreCase = True
.pattern = pattern
End With
Set matches = regEx.Execute(line)
noInStr = matches.count
End Function
You could use it in your code like that
Sub SearchTextFile()
Const strFileName = "C:\data.txt"
Const strSearch = "student"
Dim strLine As String
Dim f As Integer
Dim lngLine As Long
Dim blnFound As Boolean
Dim lPosition As Long
f = FreeFile
Open strFileName For Input As #f
Do While Not EOF(f)
lngLine = lngLine + 1
Line Input #f, strLine
Dim count As Long
count = noInStr(strLine, strSearch)
If count > 0 Then
blnFound = True
MsgBox "Search string found " & count & "- times: " & strSearch, vbInformation
Exit Do
End If
Loop
Close #f
If Not blnFound Then
MsgBox "Search string not found", vbInformation
End If
End Sub
If you also need the positions you could retrieve them with RegEx, too.
Update: This is how you could also retrieve the positions
Function colInStr(line As String, pattern As String) As Collection
Dim regEx As Object, matches As Object
Set regEx = CreateObject("vbscript.regexp")
With regEx
.MultiLine = False
.Global = True
.IgnoreCase = True
.pattern = pattern
End With
Set matches = regEx.Execute(line)
Dim col As New Collection
Dim i As Long
For i = 0 To matches.count - 1
col.Add matches(i).FirstIndex
Next i
Set colInStr = col
End Function
You also need to modify your code, below only the relevant part
Dim count As Long, col As Collection
Set col = colInStr(strLine, strSearch)
count = col.count
If count > 0 Then
blnFound = True
MsgBox "Search string found " & count & "- times: " & strSearch, vbInformation
Exit Do
End If
The positions are stored in the collection.
This will help find all the student strings and their right positions. I have commented my changes. I run the test using your file
Sub SearchTextFile()
Const strFileName = "C:\data.txt"
Const strSearch = "student"
Const strReplaceSearch = "tneduts"
Dim strLine As String
Dim f As Integer
Dim lngLine As Long
Dim blnFound As Boolean
Dim lPosition As Long
f = FreeFile
Open strFileName For Input As #f
Do While Not EOF(f)
lngLine = lngLine + 1
Line Input #f, strLine
'' For every line retreived, loop for all occurences of student
Do While (InStr(1, strLine, strSearch, vbBinaryCompare) > 0)
blnFound = True
lPosition = InStr(1, strLine, strSearch, vbTextCompare)
MsgBox "Search string found" & strSearch, vbInformation
'' remove the string student found and search for the next, we replace the word student with tneduts, that helps us keep the lPosition right
strLine = Replace(strLine, strSearch, strReplaceSearch, 1, 1)
Loop
Loop
Close #f
If Not blnFound Then
MsgBox "Search string not found", vbInformation
End If
End Sub
In the below excel 2010 vba all the text files are opened and read by the loop and then a call to a module that is in the same sheet is made. However I am getting an Argument not optional' error on that line (Call CreateXLSXFiles`). I need some expert help in fixing this as I can not figure it out. Thank you :)
VBA
'CREATE REPORT '
MsgBox ("Please click ok to generate analysis reports, vbOKOnly")
Dim myDir As String, fn As String
myDir = "C:\Users\cmccabe\Desktop\EmArray\"
fn = Dir(myDir & "*.txt")
Do While fn <> ""
CreateXLSXFiles myDir & fn
fn = Dir
Loop
Call CreateXLSXFiles
Module
Sub CreateXLSXFiles(fn As String)
' PARSE TEXT FILE AND CREATE XLSX REPORT '
Dim txt As String, m As Object, n As Long, fp As String
Dim i As Long, x, temp, ub As Long, myList
myList = Array("Display Name", "Medical Record", "Date of Birth", _
"Order Date", "Gender", "Barcode", "Sample", "Build", _
"SpikeIn", "Location", "Control Gender", "Quality")
fp = "C:\Users\cmccabe\Desktop\EmArray\"
With Worksheets(1)
.Cells.Clear
.Name = CreateObject("Scripting.FileSystemObject").GetBaseName(fn)
On Error Resume Next
n = FileLen(fn)
If Err Then
MsgBox "Something wrong with " & fn
Exit Sub
End If
On Error GoTo 0
n = 0
txt = CreateObject("Scripting.FileSystemObject").OpenTextFile(fn).ReadAll
With CreateObject("VBScript.RegExp")
.Global = True: .MultiLine = True
For i = 0 To UBound(myList)
.Pattern = "^#(" & myList(i) & " = (.*))"
If .Test(txt) Then
n = n + 1
Sheets(1).Cells(n, 1).Resize(, 2).Value = _
Array(.Execute(txt)(0).submatches(0), .Execute(txt)(0).submatches(1))
End If
Next
.Pattern = "^[^#\r\n](.*[\r\n]+.+)+"
x = Split(.Execute(txt)(0), vbCrLf)
.Pattern = "(\t| {2,})"
temp = Split(.Replace(x(0), Chr(2)), Chr(2))
n = n + 1
For i = 0 To UBound(temp)
Sheets(1).Cells(n, i + 1).Value = temp(i)
Next
ub = UBound(temp)
.Pattern = "((\t| {2,})| (?=(\d|"")))"
For i = 1 To UBound(x)
temp = Split(.Replace(x(i), Chr(2)), Chr(2))
n = n + 1
Sheets(1).Cells(n, 1).Resize(, ub).Value = temp
Next
End With
.Copy
Application.DisplayAlerts = False
With ActiveSheet
.Columns.AutoFit
.Range("B1:B12").ClearContents
End With
ActiveWorkbook.SaveAs Filename:=fp & .Name, _
FileFormat:=xlOpenXMLWorkbook
ActiveWorkbook.Close False
End Sub
The method CreateXLSXFiles takes a String as input :
Sub CreateXLSXFiles(fn As String)
However, you're calling it without passing any string:
Call CreateXLSXFiles
In order to make it work, you need to pass the needed fn (that I guess it means "file name") :
Call CreateXLSXFiles(fn)
or with the newest syntax, simply:
CreateXLSXFiles fn