I am trying to change a case of particular character in a cell but i think i am missing very small thing in it.
Eg.
The cell has value 'A for Apple and GORILLA wears pajama'
so i am trying to change case of individual 'A' into lower case 'a'.
Now there are 3 scenarios:
A can be in Starting of cell.
A can be in Middle of cell.
A can be in End of cell.
for the middle one I have solution:
Activecell.value = Replace(Activecell.value," A "," a ",vbTextCompare)
but for other 2 when I'm trying to do change other A's also changing, for example:
Activecell.value = Replace(Activecell.value,"A ","a ",vbTextCompare)
this is giving answer 'a for Apple and GORILLa wears pajama'
wherein I'm trying to get 'a for Apple and GORILLA wears pajama'
my code is
Do Until ActiveCell.Offset(0, -cnt).Value = "" And ActiveCell.Offset(0, -cnt1).Value = ""
actc = ActiveCell.Value
If actc = "" Then
ActiveCell.Offset(1, 0).Select
Else
ActiveCell.Value = Replace(ActiveCell.Value, " m ", " m ", , , vbTextCompare)
ActiveCell.Value = Replace(ActiveCell.Value, " cm ", " cm ", , , vbTextCompare)
ActiveCell.Value = Replace(ActiveCell.Value, " dm ", " dm ", , , vbTextCompare)
ActiveCell.Value = Replace(ActiveCell.Value, " mm ", " mm ", , , vbTextCompare)
ActiveCell.Value = Replace(ActiveCell.Value, " mg ", " mg ", , , vbTextCompare)
End If
Loop
To replace only the "A " in the beginning check this in particular.
If Left$(ActiveCell.Value, 2) = "A " Then
ActiveCell.Value = "a" & Right$(ActiveCell.Value, Len(ActiveCell.Value) - 1))
End If
Accordingly for the " A" in the end (Even if I don't know about sentences ending with " A")
If Right$(ActiveCell.Value, 2) = " A" Then
ActiveCell.Value = Left$(ActiveCell.Value, Len(ActiveCell.Value) - 1)) & "a"
End If
The VBA Replace function will replace ALL instances of the substring in the target string.
To specify beginning and/or end of the string, you can test for the presence of the substring and, if it is present, use a different function (I chose to use the Replace Worksheet function) to replace only that character. Examine the following code snippet for an example.
With ActiveCell
If .Value Like "A*" Then .Value = WorksheetFunction.Replace(.Value, 1, 1, "a") 'Beginning only
If .Value Like "*A" Then .Value = WorksheetFunction.Replace(.Value, Len(.Value), 1, "a") 'End only
End With
If, on the other hand, you want the sentence to start (or end) with the word A, implying there is a space after (or before) the A, then change the Like pattern to reflect that "A *" or `* A"
If all of those substrings are, in fact, words, and you want to replace them all in each ActiveCell, you can either construct multiple loops along the format of what you are doing, or you could use Regular Expressions.
For example, the following will replace all instances of the capitals you have indicated with their lower case equivalents, whether they are at the beginning, middle or end.
The macro first matches all of the instances of the different words in sPat, and then cycles through the sentence to replace them. (Note that we must go from last to first in the loop, since the lengths of each word may not be the same).
Option Explicit
Sub doit()
ActiveCell = replaceCaps(ActiveCell)
End Sub
Function replaceCaps(ByRef S As String)
Const sPat As String = "\b(?:A|M|CM|DM|MM|MG)\b"
Dim RE As Object, MC As Object
Dim I As Long
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.ignorecase = False
.Pattern = sPat
If .test(S) = True Then
Set MC = .Execute(S)
For I = MC.Count - 1 To 0 Step -1
S = WorksheetFunction.Replace(S, MC(I).firstindex + 1, Len(MC(I)), LCase(MC(I)))
Next I
replaceCaps = S
Else
replaceCaps = S
End If
End With
End Function
Related
I've already written a code that inserts a space between text and numbers, separating 'unspaced' days and months from dates, and it works as it's supposed to.
The only problem is that I'm using an If then structure to determine which Regular Expressions pattern I should use.
If the first character of the date is a number, then knowing that it is in the 'DayMonth' sequence, I use this pattern: "(.*\d)(?! )(\D.*)". Otherwise, assuming that it isn't in the 'DayMonth' sequence but rather in the 'MonthDay' sequence, I use the other pattern: "(.*\D)(?! )(\d.*)".
Is there any way to use two patterns at once for the Regular Expressions object to scan through so that I can get rid of the If Then structure?
My code below:
Sub SpaceMonthDayIf()
Dim col As Range
Dim i As Long
Set col = Application.InputBox("Select Date Column", "Obtain Object Range", Type:=8)
With CreateObject("VBScript.RegExp")
For i = 1 To Cells(Rows.Count, col.Column).End(xlUp).Row
If IsNumeric(Left(Cells(i, col.Column).Value, 1)) Then
.Pattern = "(.*\d)(?! )(\D.*)"
Cells(i, col.Column) = .Replace(Cells(i, col.Column), "$1 $2")
Else
.Pattern = "(.*\D)(?! )(\d.*)"
Cells(i, col.Column) = .Replace(Cells(i, col.Column), "$1 $2")
End If
Next
End With
End Sub
For clarity, here's what happens when I run my code:
Try this code
Sub Test()
Dim a, i As Long
With Range("A2", Range("A" & Rows.Count).End(xlUp))
a = .Value
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "(\d+)"
For i = 1 To UBound(a, 1)
a(i, 1) = Application.Trim(.Replace(a(i, 1), " $1 "))
Next i
End With
.Columns(2).Value = a
End With
End Sub
You can avoid that by inserting your space differently. Here is a Function written with early-binding, but you can change that to late-binding.
Match the junction between a letter and a number, then construct a string, inserting a space appropriately.
Option Explicit
Function InsertSpace(S As String) As String
Const sPat As String = "[a-z]\d|\d[a-z]"
Dim RE As RegExp, MC As MatchCollection
Set RE = New RegExp
With RE
.Global = False
.Pattern = sPat
.IgnoreCase = True
If .Test(S) = True Then
Set MC = .Execute(S)
With MC(0)
InsertSpace = Left(S, .FirstIndex + 1) & " " & Mid(S, .FirstIndex + 2)
End With
End If
End With
End Function
You can also accomplish this without using Regular Expressions:
EDIT Pattern change for Like operator
Option Explicit
Option Compare Text
Function InsertSpace2(S As String) As String
Dim I As Long
For I = 1 To Len(S)
If Mid(S, I, 2) Like "#[a-z]" Or Mid(S, I, 2) Like "[a-z]#" Then
InsertSpace2 = Left(S, I) & " " & Mid(S, I + 1)
Exit Function
End If
Next I
End Function
How can I remove all characters from inputbox, leaving just numbers?
I have a macro that runs down a column removing white space, shortening to 13 digits but I also need it to remove any text characters.
I still think a regexp is the way to go.
Function removeAlpha(strData As String) As String
strData = Replace(strData, " ", "")
With CreateObject("vbscript.regexp")
.Pattern = "[A-Za-z]"
.Global = True
removeAlpha = .Replace(strData, "")
End With
End Function
And to test:
Sub TestClean()
Const strTest As String = "qwerty123 456 uiops"
MsgBox removeAlpha(strTest)
End Sub
An alternate method to using a regular expression is:
Public Sub removeCharacters()
For Each RANGE_UNASSIGNED In Worksheets(1).Range("A1:A" & Worksheets(1).Range("A1").End(xlDown).Row)
STRING_OUTPUT = ""
For INTEGER_STEP = 1 To Len(RANGE_UNASSIGNED.Value)
STRING_TEMPORARY = Mid(RANGE_UNASSIGNED.Value, INTEGER_STEP, 1)
If STRING_TEMPORARY Like "[a-z.]" Or STRING_TEMPORARY Like "[A-Z.]" Then
STRING_xOUTPUT = ""
Else
STRING_xOUTPUT = STRING_TEMPORARY
End If
STRING_OUTPUT = STRING_OUTPUT & STRING_xOUTPUT
Next INTEGER_STEP
RANGE_UNASSIGNED.Value = STRING_OUTPUT
Next RANGE_UNASSIGNED
End Sub
This should remove all alpha characters from your cell(s). You can remove additional characters if required.
An approach based on IsNumeric.
Sub Keep_If_IsNumeric()
For j = 1 To Cells(Rows.Count, "A").End(xlUp).Row
Set c = Cells(j, 1)
strc = ""
For i = 1 To Len(c.Value)
n = Mid(c.Value, i, 1)
If Not IsNumeric(strc & n & "0") Then
Else
strc = strc & n
End If
Next
c.Offset(, 1) = strc
c.Offset(, 2) = Val(Replace(strc, ",", "."))
Next
End Sub
I have an Excel sheet with a column containing texts like "Hello there 2005 A" I want to split this text in between two columns, one containing 'Hello there 2005' and the other saying 'A'.
I have tried Split function in VBA, but I can't make it loop through the entire column or even come up with a delimeter which will split exactly before the letter 'A'.
Results should look something like this:
try this
Option Explicit
Sub main()
Dim cell As Range
Dim strng As Variant
Dim rightStrng As String
Dim i As Long
With Worksheets("multimanager") '<== change it as per your needs
For Each cell In .Columns("A").SpecialCells(xlCellTypeConstants, xlTextValues) 'assuming that "column containing texts" is column "A"
strng = Split(cell)
rightStrng = ""
i = UBound(strng)
Do While Not IsNumeric(strng(i)) And i > 0
rightStrng = strng(i) & " " & rightStrng
i = i - 1
Loop
If IsNumeric(strng(i)) Then
rightStrng = Application.WorksheetFunction.Trim(rightStrng)
cell.Offset(, 2) = rightStrng
cell.Offset(, 1) = Left(cell.Value, IIf(rightStrng <> "", InStrRev(cell.Value, rightStrng) - 2, Len(cell.Value)))
End If
Next cell
End With
End Sub
Instr(cellValue," ")
will give you the position of your first space
firstPos = instr(cellValue," ") ' first space
secondPos = instr(firstPos + 1, cellValue, " ") ' second space
etc..
or
followed by mid, and replace
secondColumnValue = mid(cellValue, thirdPos + 1)
firstColumnValue = replace(cellValue, secondColumnValue, "")
I've created a small macro for inserting a hidden bookmark to a numbered paragraph
Sub blablabla()
Dim BkmrkName As String
ActiveDocument.Bookmarks.ShowHidden = True
Application.ScreenUpdating = False
heanum = InputBox("Enter Heading1 number", "List paragraph", "1")
Select Case Len(heanum)
Case 1
sPos1 = "00" & Left(heanum, 1)
Case 2
sPos1 = "0" & Left(heanum, 2)
Case 3
sPos1 = Left(heanum, 3)
End Select
ActiveDocument.ConvertNumbersToText
lisnum = Left(Selection, InStr(Selection, vbTab))
ActiveDocument.Undo
If IsNumeric(Left(lisnum, 1)) = True Then
lisnum = Left(lisnum, Len(lisnum) - 2)
Select Case Len(lisnum)
Case 3
sPos2 = "00" & Right(lisnum, 1)
Case 4
If Mid(lisnum, 2, 1) = Chr(46) Then
sPos2 = "0" & Right(lisnum, 2)
ElseIf Mid(lisnum, 3, 1) = Chr(46) Then
sPos2 = "00" & Right(lisnum, 1)
End If
Case 5
If Mid(lisnum, 2, 1) = Chr(46) Then
sPos2 = Right(lisnum, 3)
ElseIf Mid(lisnum, 3, 1) = Chr(46) Then
sPos2 = "0" & Right(lisnum, 2)
ElseIf Mid(lisnum, 4, 1) = Chr(46) Then
sPos2 = "00" & Right(lisnum, 1)
End If
Case 6
If Mid(lisnum, 3, 1) = Chr(46) Then
sPos2 = Right(lisnum, 3)
ElseIf Mid(lisnum, 4, 1) = Chr(46) Then
sPos2 = "0" & Right(lisnum, 2)
End If
Case 7
sPos2 = Right(lisnum, 3)
End Select
End If
ActiveDocument.Bookmarks.Add Name:=Chr(95) & sPos1 & Chr(95) & sPos2
Application.ScreenUpdating = True
End Sub
The user select a numbered paragraph and triggers the macro. Macro runs once per trigger and inserts a hidden bookmark with the name like _001_042 if the selection begins with "any_character".42. The first "001" is meant to depict a chapter number (i.e. "Chapter 1"), but could be any number and is determined by a user input through a message box. This macro works, though with each next numbered paragraph runs slower and slower. When I get to paragraph 1.100 it takes ~5 minutes (!!!) for macro to insert a single bookmark "_001_100".
Why does such a long latency happen? Is it possible to optimize the macro to run faster?
Many thanks in advance!
Avoid making useless changes to a document. That applies to manual editing, and it applies doubly to VBA code.
Your ConvertNumbersToText / Undo is as close to completely useless as it gets. Don't do such things. Word has an Undo buffer that you stress for no reason with this pointless edit. Despite being a wasteful no-op, you also destroy the user's ability to undo their own actions with this.
First off, you solve the problem of padding a string in the worst possible way, (multiple times!). Lets fix that.
Function PadLeft(ByVal value As String, length As Integer, Optional padding As String = " ")
PadLeft = String(Max(0, length - Len(value)), padding) & value
End Function
This function will pad any string to any given length. However, it depends on another utility function that will return the greater of two numbers.
Function Max(a As Long, b As Long) As Long
If b > a Then Max = b Else Max = a
End Function
Now, how about this code:
Sub SetParagraphBookmark()
Dim para As Range, _
paraNum As Long, headerNum As String, _
prefix As String, suffix As String
Set para = Selection.Paragraphs(1).Range
paraNum = para.ListFormat.ListValue
If paraNum Then
headerNum = InputBox("Enter Heading1 number", "List paragraph", "1")
If headerNum > "" Then ' otherwise the user clicked Cancel
prefix = PadLeft(headerNum, 3, "0")
suffix = PadLeft(paraNum, 3, "0")
ActiveDocument.Bookmarks.Add "_" & prefix & "_" & suffix, para
End If
Else
MsgBox "Please click on a valid list paragraph first.", vbInformation
End If
End Sub
This sets a bookmark that spans the entire paragraph the cursor is in, without moving the cursor or making any other changes to the document.
General notes:
Why would you ever write Chr(46) instead of "."?
Indent your code properly, this increases readability.
If you find yourself copy-pasting any section of code, you are already doing something wrong. If you need something complex done in two places, write a function.
Try breaking up the work you do into the smallest possible useful unit, like I did with PadLeft and Max. This allows re-using bits of your code elsewhere. You might want to place them into a separate Utilities module as well.
Set breakpoints in your code to see what's going on.
Word has a comprehensive object model. You can find out just about anything about the document by navigating around that object model without resorting to steamroller tactics like ConvertNumbersToText. Taking some time to pick the right property from the right object pays. There will be a lot documentation-reading involved, you'll just have to deal with that. Luckily the Microsoft documentation is superb.
It's most useful to enable the "Locals Window" and the "Immediate Window" in the VBA editor. It allows you to browse the objects you work with while you are in break mode, which helps to identify the properties you are looking for.
Last, but not least: Always, always, always have Option Explicit at the top of your modules. There is a setting in the VBA IDE's options for that ("Require variable declaration"). Enable it. Manually add that line to any module that does not have it. Fix the errors you get before you do anything else. (Disable the "Auto syntax check" feature while you are at it, this feature is counter-productive.)
This macro does it's job in only few seconds:
Sub AddBkmrkSmart()
Dim Author, Year As String
ActiveDocument.Bookmarks.ShowHidden = True
heanum = InputBox("Enter Heading1 number", "Heading1", "1")
Select Case Len(heanum)
Case 1
sPos1 = "H00" & Left(heanum, 1)
Case 2
sPos1 = "H0" & Left(heanum, 2)
Case 3
sPos1 = "H" & Left(heanum, 3)
End Select
'Debug.Print "sPos1: " sPos1
lisnum = Selection.Range.ListFormat.ListValue
'Debug.Print "Iteration 1 lisnum: " & lisnum
Select Case Len(lisnum)
Case 1
sPos2 = "L00" & Left(lisnum, 1)
Case 2
sPos2 = "L0" & Left(lisnum, 2)
Case 3
sPos2 = "L" & Left(lisnum, 3)
End Select
'Debug.Print "sPos2: " sPos2
ActiveDocument.Bookmarks.Add Name:= "_" & sPos1 & "_" & sPos2
End Sub
I suggest is as answer along with Tomalak's one.
I have the code of this macro which removes leading and trailing spaces in cells with text or numbers:
Sub LIMPIAR()
Dim i As Integer
For i = 2 To 20628
Cells(i, 7).Value = Trim(Cells(i, 6).Value)
Next
End Sub
However , there are cells which its content is " ". So I would like to convert that kind of cells to "". How Can I do that?
EDIT: I'm working with scraped data.
Maybe dealing with them like this can help:
If Len(Cells(i,6).Value) <= 2 Then Cells(i, 7).Value = "" End If
OR
If Cells(i,6).Value = " " Then Cells(i, 7).Value = "" End If
Not a very elagent solution, but I would make use of the split function and then reconcatenate the elements of the resulting array. Assuming your string is in cell A1,
mystring = ""
myarray = Split(Cells(1, 1), " ")
For i = LBound(myarray) To UBound(myarray)
If Trim(myarray(i)) <> "" Then
mystring = mystring & Trim(myarray(i)) & " "
End If
Next i
MsgBox Trim(mystring)
mystring should provide a string with just one space between words. You could put this code inside your loop.