VBA code should capitalize first letter in sentence, but doesn't - excel

I'm unsure why the following line of code is failing to capitalize the first letter of every first sentence in a cell:
chars.Characters(1, 1).Text = UCase(chars.Characters(1, 1).Text)
It does work on most of the cells, but about 1 in 6 do not.
Basic troubleshooting I attempted: 1) Made sure no spaces before the first letter, 2) Used regular UPPER formula successfully on same cell, 3) Re-typed the word, 4) Tried a different word.
Full code in case it helps:
Sub ColorWords()
Dim sh As Worksheet
Dim cell As Range
Dim chars As Range
Dim defRng As Range
Dim wordRng As Range
Dim posB As Long
Dim posE As Long
Dim posQ As Long
Dim color As Long
Dim word As String
Dim def As String
Set sh = Sheets(1)
Set defRng = sh.Range("D3", sh.Range("D1048576").End(xlUp).Address)
Set wordRng = sh.Range("C3", sh.Range("C1048576").End(xlUp).Address)
Application.ScreenUpdating = False
For Each cell In wordRng
word = LCase(cell.Value)
If IsNull(cell.Font.color) Or cell.Font.color = 0 Then GoTo NextIter
color = cell.Font.color
For Each chars In defRng
chars.Characters(1, 1).Text = UCase(chars.Characters(1, 1).Text)
def = LCase(chars.Value2)
posB = InStr(1, def, word)
If InStr(posB + Len(word), def, "s") = posB + Len(word) Then
posE = Len(word) + 1
Else
posE = Len(word)
End If
Do Until posB = 0
chars.Characters(posB, posE).Font.color = color
chars.Characters(posB, posE).Font.Bold = True
posB = InStr(posB + 1, def, word)
If InStr(posB + Len(word), def, "s") = 1 Then
posE = Len(word) + 1
Else
posE = Len(word)
End If
Loop
Next chars
NextIter:
Next cell
Application.ScreenUpdating = True
End Sub`

I found the logic of your code inconsistent, at times to the point of confusing, and added comments to my substitute to explain. The code below will loop through all phrases in column D, capitalize their first word and remove any trailing or leading blanks.
It will then, one phrase after the other, look for existence of each word in column C. If found, the word will be given the colour it has in column C, including any trailing "s". I hope that's largely what you wanted. However, if there are small differences you may like to take the syntax I provide and move it to the location in the code where it is needed.
Sub ColorWords()
' 124
Dim wordRng As Range ' words to look for
Dim defRng As Range ' subject phrases
Dim wordCell As Range
Dim defCell As Range
Dim posB As Long ' start of word
Dim posE As Long ' length of word
Dim Color As Long
Dim Word As String
Dim Def As String
With Worksheets(1) ' change to suit (suggest use of a name)
' syntax for specifying a cell: Cells([Row number], [Column number or name])
' syntax for specifying a range: Range([First wordCell], [last wordCell])
Set wordRng = .Range(.Range("C3"), .Cells(.Rows.Count, "C").End(xlUp))
Set defRng = .Range(.Range("D3"), .Cells(.Rows.Count, "D").End(xlUp))
End With
Application.ScreenUpdating = False
' All colours disappear upon capitalisation.
' Therefore call each defCell only once.
For Each defCell In defRng
With defCell
Def = LCase(Trim(.Value2)) ' remove leading/trailing blanks
Def = UCase(Left(Def, 1)) & Mid(Def, 2)
' your code first capitalises the first letter, then
' makes the entire phrase lower case, including its first letter
.Value = Def
.Font.Color = 0 ' remove existing colors
End With
For Each wordCell In wordRng
With wordCell
Word = Trim(LCase(.Value)) ' remove leading/trailing blanks
Color = .Font.Color ' can't be Null
' Font.Color is of Long data type, which is 0 if not set
End With
If Color <> 0 Then ' 0 is the default font color (usually black)
' skip if Word's colour is the default
' vbTextCompare = case insensitive, else vbBinaryCompare
posB = InStr(1, Def, Word, vbTextCompare)
posE = Len(Word)
If Mid(Def, posB + posE, 1) = "s" Then posE = posE + 1
With defCell.Characters(Start:=posB, Length:=posE).Font
.Color = Color
.Bold = True
End With
End If
Next wordCell
Next defCell
Application.ScreenUpdating = True
End Sub
Although the code looks at the character following the "word" I refrained from adding action to avoid colouring the word "text" in "textbook". This might easily be added if you prefer to either skip "textbook" or expand the colour to include the entire word.
Note also that I added a line of code to remove all colours from a phrase before making changes to it. This enables running of the same program on the same phrases repeatedly when words or colours in column C are modified.

I'm not sure what you are doing with the colors, but to capitalize the first letter of each sentence with in a cell, you can use Regular Expressions to determine where that letter is, and the Characters property to change the case to Upper.
eg:
Note that I used early-binding so as to make use of intellisense, and it's a bit more efficient, but you can convert to late-binding if you need to
'Set Reference (Tools/References) to Microsoft VBScript Regular Expressions 5.5
Option Explicit
Sub capFirst()
Dim R As Range, C As Range
Dim RE As RegExp, MC As MatchCollection, M As Match
Dim ch As String
Const sPat As String = "^.*$"
With ThisWorkbook.Worksheets("sheet1")
Set R = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
Set RE = New RegExp
With RE
.Pattern = sPat
.MultiLine = True
.Global = True
For Each C In R
If .Test(C.Value2) Then
Set MC = .Execute(C.Value2)
For Each M In MC
C.Characters(M.FirstIndex + 1, 1).Text = UCase(C.Characters(M.FirstIndex + 1, 1).Text)
Next M
End If
Next C
End With
End Sub
I imagine that whatever is going on with your colors could be integrated into the above.

Related

Concatenate strings from cells using WHILE/UNTIL LOOP?

I have strings stored in cells of a column in Excel that I would like to concatenate in several pieces, like sentences, with VBA. Here is an example:
Column A
Jack
learns
VBA
Jack
sits
on
a
couch
Jack
wants
chocolate
cake
I finally found a way to concatenate all strings and save the sentences to a cell:
Sub JACK()
Dim MP() As String
Dim Str As String
Dim i As Integer
For i = 2 To 10
ReDim Preserve MP(i)
MP(i) = Cells(i, 1).Value
Next i
Str = Join(MP)
Cells(1, 2).Value = Str
End Sub
But I would like to have the sentences that start with "Jack" and end with the row "Jack - 1", each saved in seperate cells.
Could anyone help me???
Thank you so much!
This is the code snippet that will do what you want:
Sub test_func()
' this is the starting cell (keep in mind that the first word in the cell is 'Jack' so the start cell is actually starting at C2)
Dim startCell As range
Set startCell = ThisWorkbook.ActiveSheet.range("B2")
' reading all the cells in the range
Dim wordRange As range
Set wordRange = ThisWorkbook.ActiveSheet.range("A2:A13")
' creating two variables row and col
Dim row As Long
Dim col As Long
' for each word in wordRange
Dim word As Variant
For Each word In wordRange
' as soon as we find the word 'Jack'
If word.Value = "Jack" Then
' move the cursor to row 0
row = 0
' move the cursor one cell to the right
col = col + 1
End If
' else if the word is not 'Jack', put the word on the cursor cell
startCell.Offset(row, col) = word
' then move the cursor one cell down
row = row + 1
Next
End Sub
The function is:
reading all the words from the column A into a range.
dumping the elements from the range (word) starting on B2, one by one
as soon as it finds the word 'Jack', it will start at row 0, move to the right and continue
The outcome looks like this:
This is the output of the script
Note that the words are starting on C2 even though you chose B2 to be the starting cell; this is because the first word in the list is 'Jack', so it is moving one cell to the right as soon as it starts.
EDIT:
Here might be the function that you are looking for:
Sub test_func()
' this is the starting cell (keep in mind that the first word in the cell is 'Jack' so the start cell is actually starting at C2)
Dim startCell As range
Set startCell = ThisWorkbook.ActiveSheet.range("B2")
' reading all the cells in the range
Dim wordRange As range
Set wordRange = ThisWorkbook.ActiveSheet.range("A2:A13")
' creating two variables row and col
Dim row As Long
Dim col As Long
' string that holds each sentence
Dim sentence As String
' for each word in wordRange
Dim word As Variant
For Each word In wordRange
' as soon as we find the word 'Jack' and the sentence is not empty, the sentence is complete
If word.Value = "Jack" And sentence <> "" Then
'printing out the whole sentence
startCell.Offset(row, col) = sentence
' emptying the sentence when 'Jack' is found
sentence = ""
' move the cursor one cell down
row = row + 1
End If
' else if the word is not 'Jack', concatenates the word into the sentence
sentence = sentence & " " & word
Next
' adding this again at the end of the loop because the last sentence is not outputted otherwise
startCell.Offset(row, col) = sentence
End Sub
This function differs from the previous one because it concatenates the words into a sentence before dumping it out. In this function, the start cell is correct and is not moving down or right when the program starts. This is because we can check whether the sentence that it is about to dump out is empty or not, if it is; then it means we did not finish our sentence.
Hope this helps!
This is the result screenshot of the second version of the code
Extract Sentences From Column
Sub JACK()
Const JackStart As String = "Jack"
Const JackEnd As String = "."
Const Delimiter As String = " "
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim slCell As Range: Set slCell = ws.Cells(ws.Rows.Count, "A").End(xlUp)
Dim srg As Range: Set srg = ws.Range("A2", slCell)
Dim dCell As Range: Set dCell = ws.Range("B2")
Dim sCell As Range
Dim JackString As String
Dim FoundFirst As Boolean
For Each sCell In srg.Cells
If sCell.Value = JackStart Then
If FoundFirst Then
dCell.Value = JackString & JackEnd
Set dCell = dCell.Offset(1) ' next row
Else
FoundFirst = True
End If
JackString = JackStart
Else
If FoundFirst Then JackString = JackString & Delimiter & sCell.Value
End If
Next sCell
dCell.Value = JackString & JackEnd
MsgBox "Jacks extracted.", vbInformation
End Sub

How to color a specific line (condition is present) of a comment in Excel?

Dear experts in Excel and VBA!
Could you tell me how you can color a certain line (condition - the presence of a certain word) in a Comments?
Comment consists of several lines, separated by Chr (10).
Example in picture1:
the comment has 4 lines, the second line contains the word "VBA", so this line should be highlighted in red.
The main problem is that the test word "VBA" can be in any line, there can be from 1 to 10+ lines.
I assumed that:
can move data from comment to cell
replace Chr (10) with some character, for example, "_"
distribute the text of the cell into columns through the "column distribution wizard"
search for the desired word "VBA" in the received cells
determine the cell number and understand that this is the number of the required line in the comment
based on the cell number, paint over the line number in the comment
Can you please tell me if my action logic is correct? Am I heading in the right direction?
If so, what is the correct way to carry out points 4-6?
enter image description here
would this help?
"test" is the codename for the sheet I have set, change it according to your situation.
"i" will give you the line number, starting from 0. So in your example it would be 1.
Edit: Added Exit For in the if check.
Option Explicit
Sub test_note()
Dim strNote As String
Dim arrNote As Variant
Dim number_of_lines As Integer
strNote = test.Range("A5").NoteText
number_of_lines = Len(strNote) - Len(Replace(strNote, Chr(10), "")) + 1
ReDim arrNote(1 To number_of_lines) As String
arrNote = Split(strNote, Chr(10))
Dim i As Long
For i = LBound(arrNote) To UBound(arrNote)
If InStr(arrNote(i), "VBA") > 0 Then
Debug.Print i, arrNote(i)
Exit For 'If you are sure there won't be any other occurrence of VBA in there, why check the rest of the lines? Speeds code depending on circumstance.
End If
Next i
End Sub
Edit 2: Revised code to change the color of the comment line.
Sub test_note()
Dim strNote As String
Dim arrNote As Variant
Dim number_of_lines As Integer
strNote = test.Range("B5").NoteText
number_of_lines = Len(strNote) - Len(Replace(strNote, Chr(10), "")) + 1
ReDim arrNote(1 To number_of_lines) As String
arrNote = Split(strNote, Chr(10))
Dim i As Long
Dim startPos As Integer
Dim number_of_chars As Integer
startPos = 1
' Reset comment font color
test.Range("B5").Comment.Shape.TextFrame.Characters.Font.Color = 0
For i = LBound(arrNote) To UBound(arrNote)
If InStr(arrNote(i), "VBA") > 0 Then
number_of_chars = Len(arrNote(i))
test.Range("B5").Comment.Shape.TextFrame.Characters(startPos, number_of_chars).Font.Color = vbRed
Debug.Print i, arrNote(i), "startPos: " & startPos, "numChars: " & number_of_chars
Else
startPos = startPos + Len(arrNote(i)) + 1
End If
Next i
End Sub
Check this. Just running this VBA copies your comments to the cells
and highlights the lines containing "VBA", however, it does this for
all comments on all sheets
credit: https://martinbosanacvba.blogspot.com/2021/08/copying-comments-to-cells-and.html
Sub Demo()
Dim tnahqb1 As Range
Dim tnahqb2 As Range
Dim tnahqb3 As Workbook
Dim tnahqb4 As Worksheet
Dim tnahqb5 As Variant
Dim tnahqb6 As Integer
Dim tnahqb7 As Integer
Dim tnahqb8 As Integer
Dim tnahqb9 As Integer
For Each tnahqb10 In ActiveWorkbook.Worksheets
Set tnahqb1 = tnahqb10.Cells.SpecialCells(xlCellTypeComments)
If tnahqb1 Is Nothing Then
MsgBox "No comments in the sheet"
Else
For Each cell In tnahqb1
cell.Value = cell.Comment.Text
tnahqb5 = Split(cell.Comment.Text, Chr(10))
tnahqb6 = UBound(tnahqb5) - LBound(tnahqb5) + 1
For I = LBound(tnahqb5) To UBound(tnahqb5)
If InStr(tnahqb5(I), "VBA") > 0 Then
tnahqb8 = Len(tnahqb5(I))
With cell
tnahqb7 = InStr(cell.Comment.Text, tnahqb5(I))
tnahqb9 = tnahqb7 + tnahqb8
.Characters(tnahqb7, tnahqb8).Font.Color = vbRed
End With
End If
Next I
Next cell
End If
Next tnahqb10
End Sub

Search and replace with wildcards in Excel VBA

I use comma as decimal separator, but sometimes I receive files where values are below a set limit, and then the file uses point as "<2.5". Sometimes there is one digit before the decimal separator, and sometimes there are two digits.
I need to be able to replace the point with a comma in cells with begin with the "less than" symbol, but retain the actual numbers, so that "<2.5" is replaced with "<2,5" and "<10.0" is replaced with "<10,0". This needs to be done in Excel VBA.
I can't do a general search for "." and replace with ",", since there are places where I need to keep the point as it is.
Anyone have an idea of how to achieve this?
Approach via Replace function
You could read in data to a datafield array, replace the mentioned "<" data via Replace function and write them back in one statement by the following code. - Of course it's possible to use RegEx, too as mentioned in above comment.
Notes
a) I assume you are using data in column A:A via Set rng = ws.Range("A1:A" & n); this can easily changed to any other range.
b) Assigning values to a variant datafield array automatically creates a one based 2-dim array, which you address in case of one column only e.g. via v(1,1), v(2,1), v(3,1) etc. to v(n,1).
Example Code
Option Explicit
Sub replaceLowerThan()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("MySheet") ' << Change to your sheet name
Dim n As Long, i As Long
Dim rng As Range
Dim v
' get last row number and define data range
n = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("A1:A" & n)
' write data to 1-based 2-dim datafield array
v = rng.Value2
' replace "<..." values
For i = 1 To n
If Not IsError(v(i, 1)) Then ' omit cells with errors like #DIV/0!
If v(i, 1) Like "<*" Then v(i, 1) = Replace(v(i, 1), ".", ",")
End If
Next i
' write values back
rng.Value2 = v
End Sub
This worked:
Dim strPattern As String: strPattern = "(<[0-9]+)[\.]"
Dim strReplace As String: strReplace = "$1,"
Dim myreplace As Long
Dim strInput As String
Dim Myrange As Range
Set RegEx = CreateObject("VBScript.RegExp")
Set Myrange = ActiveSheet.UsedRange
For Each cell In Myrange
If strPattern <> "" Then
strInput = cell.Value
With RegEx
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = strPattern
End With
If RegEx.Test(strInput) Then
cell.Value = (RegEx.Replace(strInput, strReplace))
End If
End If
Next

VB Code to remove HTML code keeps crashing excel for ~40k dataset

this is my VBA code:
Sub RemoveTags()
Dim r As Range
Selection.NumberFormat = "#" 'set cells to text numberformat
With CreateObject("vbscript.regexp")
.Pattern = "\<.*?\>"
.Global = True
For Each r In Selection
r.Value = .Replace(r.Value, "")
Next r
End With
End Sub
It does remove all the Markup language from my cells but crashes when i run it for ~40k records. Is there something wrong with my code or should I change excel settings?
My guess is that Excel craches while trying to write the text back to the cell.
Here is a couple of things you can try:
Use .Value2 rather than .Value to work with the raw value.
Add a single quote in front of the text. It won't appear, but it will ensure the text format
Use a not followed by pattern rather than a non greedy to be sure to handle line breaks.
Sub RemoveTags()
Dim values(), r As Long, c As Long, re As Object
' load the values in an array
values = Selection.Value2
' create the regex
Set re = CreateObject("vbscript.regexp")
re.pattern = "<[^>]*>"
re.Global = True
' remove the tags for each value
For r = 1 To UBound(values, 1)
For c = 1 To UBound(values, 2)
values(r, c) = "'" & re.replace(values(r, c), vbNullString)
Next
Next
' write the values back to the sheet
Selection.Value2 = values
End Sub

Highlight words in a cell (not the cell) by matching WHOLE WORDS only?

I am trying to make code for an excel document that will allow me to accomplish the following:
Search a worksheet for a listed set of words (as defined by the name manager)
Search for the listed words as whole words only, while taking into account, case sensitivity, words that are preceded/followed by punctuation, etc.
Format the listed words within their cells (not the cell itself) to a new font color (ideally I would like it to be highlighted, but I am not sure if Excel allows this).
I currently have the code listed below, which is highlighting the cell yellow and turning the listed words red - but it matches occurrences within words. How can I make it match only whole words?
Sub ColorCertainWords()
Dim Z As Long, Position As Long, Words As Variant, Cell As Range
Words = Range("LIST") 'LIST defined by name manager as list of words that cannot be used
For Each Cell In Sheets("Sheet1").Range("A1:AA6000") 'Range of cells to be checked
If Len(Cell.Value) Then
For Z = 1 To UBound(Words)
Position = InStr(1, Cell.Value, Words(Z, 1), vbTextCompare)
Do While Position
Cell.Characters(Position, Len(Words(Z, 1))).Font.ColorIndex = 3 'Red
Cell.Interior.ColorIndex = 6 ' Yellow
Position = InStr(Position + 1, Cell.Value, Words(Z, 1), vbTextCompare)
Loop
Next
End If
Next
End Sub
Here is your modified code which will help you keep going.
Sub ColorCertainWords()
Dim Z As Long, Position As Long, Words As Variant
Dim Cell As Range, x As Integer, j As Integer
Dim tempWords As Variant
Words = Range("LIST")
x = 1
For Each Cell In Sheets("Sheet6").Range("A1:A6") 'Range of cells to be checked
If Len(Cell.Value) Then
tempWords = Split(Cell.Value, " ") 'Splitting cell value by space
For i = LBound(tempWords) To UBound(tempWords) 'Looping through splitted values
j = InStr(x, Cell.Value, " ") + 1
For Z = 1 To UBound(Words)
If tempWords(i) = Words(Z, 1) Then 'Checking is words are matching
For k = 1 To Len(tempWords(i))
Cell.Characters(x, Len(tempWords(i))).Font.ColorIndex = 3 'Red
Cell.Interior.ColorIndex = 6 ' Yellow
Next
End If
Next
x = j
Next
x = 1
End If
Next
End Sub
And here is the test data with the resulted format that I've used:
Let me know if this will help.

Resources