Home |
Search |
Today's Posts |
#1
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hi,
In cells c1 to h200 I have long sentences in each cell, all the letters are in capitals. I need to write a macro which loops through each cell and changes the letters to small except: 1)The first letter 2) Any letter after a full stop 3) i e.g. THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE Becomes The day was suuny and I wore a hat.Peter was there Thanks A Million for any help |
#2
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
From a previous post:-
Sub MakeProperCase() Application.ScreenUpdating = False Application.Calculation = xlCalculationManual Dim myCell As Range Dim myRng As Range On Error Resume Next Set myRng = Intersect(Selection, _ Selection.Cells _ .SpecialCells(xlCellTypeConstants, xlTextValues)) On Error GoTo 0 If myRng Is Nothing Then MsgBox "Please select a range that contains text--no formulas!" Exit Sub End If For Each myCell In myRng.Cells myCell.Value = StrConv(myCell.Value, vbProperCase) Next myCell Application.Calculation = xlCalculationAutomatic Application.ScreenUpdating = True End Sub -- Regards Ken....................... Microsoft MVP - Excel Sys Spec - Win XP Pro / XL 97/00/02/03 ---------------------------------------------------------------------------- It's easier to beg forgiveness than ask permission :-) ---------------------------------------------------------------------------- "teresa" wrote in message ... Hi, In cells c1 to h200 I have long sentences in each cell, all the letters are in capitals. I need to write a macro which loops through each cell and changes the letters to small except: 1)The first letter 2) Any letter after a full stop 3) i e.g. THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE Becomes The day was suuny and I wore a hat.Peter was there Thanks A Million for any help |
#3
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Thanks for this KEn - acouple of things
THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE becomes.... The Day Was Sunny And I Wore A Hat.Peter Was There howevr I want: The day was sunny and I wore a hat.Peter was there "Ken Wright" wrote: From a previous post:- Sub MakeProperCase() Application.ScreenUpdating = False Application.Calculation = xlCalculationManual Dim myCell As Range Dim myRng As Range On Error Resume Next Set myRng = Intersect(Selection, _ Selection.Cells _ .SpecialCells(xlCellTypeConstants, xlTextValues)) On Error GoTo 0 If myRng Is Nothing Then MsgBox "Please select a range that contains text--no formulas!" Exit Sub End If For Each myCell In myRng.Cells myCell.Value = StrConv(myCell.Value, vbProperCase) Next myCell Application.Calculation = xlCalculationAutomatic Application.ScreenUpdating = True End Sub -- Regards Ken....................... Microsoft MVP - Excel Sys Spec - Win XP Pro / XL 97/00/02/03 ---------------------------------------------------------------------------- It's easier to beg forgiveness than ask permission :-) ---------------------------------------------------------------------------- "teresa" wrote in message ... Hi, In cells c1 to h200 I have long sentences in each cell, all the letters are in capitals. I need to write a macro which loops through each cell and changes the letters to small except: 1)The first letter 2) Any letter after a full stop 3) i e.g. THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE Becomes The day was suuny and I wore a hat.Peter was there Thanks A Million for any help |
#4
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hello Teresa,
I wrote a little UDF for you: '---------------------------------------------------------------------------------------------- Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, i As Long, i2 As Long Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 If Not i = UBound(bArr) Then Select Case bArr(i + 1) Case 33, 39, 44, 46, 58, 59, 63, 148, 160 bArr(i) = bArr(i) - 32 Case 32 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2 Exit For End Select Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function Sub testTime() MsgBox sCase("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") MsgBox sCase("no WorRies, i'm ONLY testIng! yes-no?") MsgBox sCase("mY fRiend & i") MsgBox sCase("iiiiiiiiiiiiii") MsgBox sCase("22 Years.") End Su '---------------------------------------------------------------------------------------------- I hope you like it. To date, I have only given the function a limited amount of thought/testing, so if you find it needs adjusting, please post back. Regards, Nate Oliver |
#5
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hmmm, one possible glitch, a sentence starting with a non-alpha char; I try
to handle with the following adjustment: '-------------------------------------------------------------------------------------------Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, i As Long, i2 As Long Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 If Not i = UBound(bArr) Then Select Case bArr(i + 1) Case 33, 39, 44, 46, 58, 59, 63, 148, 160 bArr(i) = bArr(i) - 32 Case 32 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2: Exit For Case Is < 32 i = i2: Exit For End Select Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function Sub testTime() MsgBox sCase("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") MsgBox sCase("no WorRies, i'm ONLY testIng! yes-no?") MsgBox sCase("mY fRiend & i") MsgBox sCase("iiiiiiiiiiiiii") MsgBox sCase("22 Years.") MsgBox sCase("How Old?! 5 Years.") MsgBox sCase("****T. %T i @") End Su '------------------------------------------------------------------------------------------- Regards, Nate Oliver |
#6
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
If you would prefer a macro to make the changes in place to constants
Tushar' Mehta's code has worked well for me, It uses regular expressions rather than checking each letter so would be faster. Like anything else you may have to make changes for words or acronyms that should be in all caps and for proper nouns. Sentence_Case, Use VBS RegExp to replace a-z with A-Z?, Tushar Mehta, programming, 2002-08-04. .See Daniel's correction if cell begins with spaces in a reply. http://google.co.uk/groups?threadm=M....microsoft.com Some notes on Regular Expressions to understand Tushar's macro. http://www.mvps.org/dmcritchie/excel...sid.htm#regexp My page on Proper and other letter case changes http://www.mvps.org/dmcritchie/excel/proper.htm I have used the google.co.uk link earlier to avoid errors that would likely be introduced by the disastrous Google Beta Groups. You can read more about that at http://www.mvps.org/dmcritchie/excel/betagroups.htm --- HTH, David McRitchie, Microsoft MVP - Excel [site changed Nov. 2001] My Excel Pages: http://www.mvps.org/dmcritchie/excel/excel.htm Search Page: http://www.mvps.org/dmcritchie/excel/search.htm "Nate Oliver" wrote ... Hmmm, one possible glitch, a sentence starting with a non-alpha char; I try to handle with the following adjustment: |
#7
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hello David,
With all due respect, I did test the function you mentioned before posting to this thread, and unless I'm missing something, it doesn't appear to work on the OP's string, i.e., €˜------------------- Sub testIt() MsgBox CapFirstLetterOfSentences( _ "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") End Sub €˜------------------- Doesnt return the desired output. Could someone fix that? Perhaps, however, it won't be me. In terms of performance, I have a couple of thoughts. 1) I would be hesitant to pass judgment on the Byte Array approach before using a hi-resolution timer. Not all linear searches are created equally, Byte Arrays are light-weight and your processor knows what to do when it comes to manipulating small, long integers. In my experience, they are extremely fast, even while they might look slow at a glance. And it raises the question in my mind, if RegExp isn't checking each character under the hood, what is it doing? I'm admittedly extremely green when it comes to RegExp and I have seen some nice implementations. 2) If you're going to call the RegExp function against multiple cells and you want to compete with the Byte Array for performance, you might have some work to do. Binding with the respective object on each call is going to cost you a fair amount. If you wanted to optimize, you'd either want to demodularize the code into an inline subroutine with a single bind or reconstruct the function to return an array, again, using a single bind. Now, I haven't time-tested the SCase() function I have posted, and I have yet to see the RegExp equivalent, so it's hard to speak to relative performance at this point. Getting a procedure to implement the algorithm is the easy part, the trick is to get an algorithm that works, as desired, up and running. I made another change after considering the possibility of ongoing punctuation (e.g., "Hmmm..."). The revision is as follows: '--------------------- Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, i As Long, i2 As Long Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 If Not i = UBound(bArr) Then Select Case bArr(i + 1) Case 33, 39, 44, 46, 58, 59, 63, 148, 160 bArr(i) = bArr(i) - 32 Case 32 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then i = i2: Exit For End If Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function Sub testTime() Debug.Print sCase$("how old?! 22 Years.") Debug.Print sCase$("how old?! twenty-two Years.") Debug.Print sCase$("hmmmm.... wOrking?!?! sam i am. yes-no? isn't i'm isn't. ") Debug.Print sCase$("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") End Sub '--------------------- Im probably missing quite a few other aspects to the language as well... Change a range? Try the following: '--------------------- Sub chngRange() Dim cl As Range Application.ScreenUpdating = False For Each cl In Range("c2:h200").SpecialCells(xlConstants, xlTextValues) cl.Value = sCase(cl.Value) Next Application.ScreenUpdating = True End Sub '--------------------- I set up c2:h200 with the following string: "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE" The above procedure converted the 1,194 cells in question in ~.1 seconds. This strikes me as relatively fast, while your results will obviously vary with the strings in question. Regards, Nate Oliver |
#8
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hi Nate,
WOW. A lot of testing. The main point was that a subroutine run only once is going to take zero time during recalculations, and I expect that the user would really prefer to change the data once and be done with it. I did not test your function for time or reliability. Yes it is easy to incorporate the user function into a SUB which you did afterward. What Excel actually does under the hood is going to run circles around you using VBA because you are not even close to machine instructions. In fact there are probably machine instructions for AND-ind and OR-ing longs stings of bits or at least to separate lowercase from uppercase letters.. I just tested your function, and it failed (#VALUE!) on the very first cell which was empty. I know the poster said he started with all caps, and your encompassing SUB would eliminate that possibility. .. I would first convert to lowercase with a separate macro myself and use a few macros as builting blocks. The second macro would be Tushar's macro. Then looking over results. I prefer running two macros to having options in macros or lots of macros that are almost the same. [If the macro is only going to be run once it isn't going to matter much whether you spend time looking for one macro that does everything, or use a couple of macros that will do the trick] Normally if there is a problem with sentence case someone typed in something and may have capitalized some names like IBM or their own name mine is McRitchie. Running Tushar's sentence_case would not destroy those words, nor will it force any Capital letter to lower case. I'm not saying that one macro is going to fit everyone's needs, but was just offering an alternative that I know works for me, and I was surprised how well regular expressions worked when I saw Tushar's solution. I immediately changed reference to Harald Staff's solution that I thought worked fine until then. I just took a look at your SUB and noticed it had a hard coded range, which is certainly not kind of thing that I would do in a general purpose macro. Was actually looking to see if someone selected only one cell if it would mess up the entire sheet. See my proper.htm page for selection. Don't remember if the poster posted a specific range, but I doubt that they would really want such an absolute restriction. --- HTH, David McRitchie, Microsoft MVP - Excel [site changed Nov. 2001] My Excel Pages: http://www.mvps.org/dmcritchie/excel/excel.htm Search Page: http://www.mvps.org/dmcritchie/excel/search.htm "Nate Oliver" wrote ... With all due respect, I did test the function you mentioned before posting to this thread, and unless I'm missing something, it doesn't appear to work on the OP's string, i.e., te Oliver |
#9
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hi Nate,
I saw the OP's poser come in and wrote a function of my own. Was about to post it, saw yours and felt confident mine would be faster. But quite the reverse! so I'll keep very quiet about mine <g. Actually I'm surprised just how fast yours is - I like it! However I think you will find this little wrapper for your function will make a speed things up further, particularly for many cells: Sub test() Dim r As Long, c As Long Dim rng As Range Dim vCells Set rng = Range("C1:H200") 'rng.Value = "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE" vCells = rng.Value For r = 1 To UBound(vCells) For c = 1 To UBound(vCells, 2) If Len(vCells(r, c)) Then 'any other pre-tests ? vCells(r, c) = sCase(vCells(r, c)) End If Next Next rng.Value = vCells End Sub Regards, Peter T "Nate Oliver" wrote in message ... Hello David, With all due respect, I did test the function you mentioned before posting to this thread, and unless I'm missing something, it doesn't appear to work on the OP's string, i.e., '------------------- Sub testIt() MsgBox CapFirstLetterOfSentences( _ "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") End Sub '------------------- Doesn't return the desired output. Could someone fix that? Perhaps, however, it won't be me. In terms of performance, I have a couple of thoughts. 1) I would be hesitant to pass judgment on the Byte Array approach before using a hi-resolution timer. Not all linear searches are created equally, Byte Arrays are light-weight and your processor knows what to do when it comes to manipulating small, long integers. In my experience, they are extremely fast, even while they might look slow at a glance. And it raises the question in my mind, if RegExp isn't checking each character under the hood, what is it doing? I'm admittedly extremely green when it comes to RegExp and I have seen some nice implementations. 2) If you're going to call the RegExp function against multiple cells and you want to compete with the Byte Array for performance, you might have some work to do. Binding with the respective object on each call is going to cost you a fair amount. If you wanted to optimize, you'd either want to demodularize the code into an inline subroutine with a single bind or reconstruct the function to return an array, again, using a single bind. Now, I haven't time-tested the SCase() function I have posted, and I have yet to see the RegExp equivalent, so it's hard to speak to relative performance at this point. Getting a procedure to implement the algorithm is the easy part, the trick is to get an algorithm that works, as desired, up and running. I made another change after considering the possibility of ongoing punctuation (e.g., "Hmmm..."). The revision is as follows: '--------------------- Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, i As Long, i2 As Long Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 If Not i = UBound(bArr) Then Select Case bArr(i + 1) Case 33, 39, 44, 46, 58, 59, 63, 148, 160 bArr(i) = bArr(i) - 32 Case 32 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then i = i2: Exit For End If Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function Sub testTime() Debug.Print sCase$("how old?! 22 Years.") Debug.Print sCase$("how old?! twenty-two Years.") Debug.Print sCase$("hmmmm.... wOrking?!?! sam i am. yes-no? isn't i'm isn't. ") Debug.Print sCase$("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") End Sub '--------------------- I'm probably missing quite a few other aspects to the language as well... Change a range? Try the following: '--------------------- Sub chngRange() Dim cl As Range Application.ScreenUpdating = False For Each cl In Range("c2:h200").SpecialCells(xlConstants, xlTextValues) cl.Value = sCase(cl.Value) Next Application.ScreenUpdating = True End Sub '--------------------- I set up c2:h200 with the following string: "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE" The above procedure converted the 1,194 cells in question in ~.1 seconds. This strikes me as relatively fast, while your results will obviously vary with the strings in question. Regards, Nate Oliver |
#10
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Does it work like this (I haven't tried it)?
Sub TestIt() MsgBox CapFirstLetterOfSentences( _ LCase$("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE")) End Sub On Thu, 17 Mar 2005 10:11:04 -0800, "Nate Oliver" wrote: Hello David, With all due respect, I did test the function you mentioned before posting to this thread, and unless I'm missing something, it doesn't appear to work on the OP's string, i.e., ‘------------------- Sub testIt() MsgBox CapFirstLetterOfSentences( _ "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") End Sub ‘------------------- Doesn’t return the desired output. Could someone fix that? Perhaps, however, it won't be me. In terms of performance, I have a couple of thoughts. 1) I would be hesitant to pass judgment on the Byte Array approach before using a hi-resolution timer. Not all linear searches are created equally, Byte Arrays are light-weight and your processor knows what to do when it comes to manipulating small, long integers. In my experience, they are extremely fast, even while they might look slow at a glance. And it raises the question in my mind, if RegExp isn't checking each character under the hood, what is it doing? I'm admittedly extremely green when it comes to RegExp and I have seen some nice implementations. 2) If you're going to call the RegExp function against multiple cells and you want to compete with the Byte Array for performance, you might have some work to do. Binding with the respective object on each call is going to cost you a fair amount. If you wanted to optimize, you'd either want to demodularize the code into an inline subroutine with a single bind or reconstruct the function to return an array, again, using a single bind. Now, I haven't time-tested the SCase() function I have posted, and I have yet to see the RegExp equivalent, so it's hard to speak to relative performance at this point. Getting a procedure to implement the algorithm is the easy part, the trick is to get an algorithm that works, as desired, up and running. I made another change after considering the possibility of ongoing punctuation (e.g., "Hmmm..."). The revision is as follows: '--------------------- Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, i As Long, i2 As Long Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 If Not i = UBound(bArr) Then Select Case bArr(i + 1) Case 33, 39, 44, 46, 58, 59, 63, 148, 160 bArr(i) = bArr(i) - 32 Case 32 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then i = i2: Exit For End If Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function Sub testTime() Debug.Print sCase$("how old?! 22 Years.") Debug.Print sCase$("how old?! twenty-two Years.") Debug.Print sCase$("hmmmm.... wOrking?!?! sam i am. yes-no? isn't i'm isn't. ") Debug.Print sCase$("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") End Sub '--------------------- I’m probably missing quite a few other aspects to the language as well... Change a range? Try the following: '--------------------- Sub chngRange() Dim cl As Range Application.ScreenUpdating = False For Each cl In Range("c2:h200").SpecialCells(xlConstants, xlTextValues) cl.Value = sCase(cl.Value) Next Application.ScreenUpdating = True End Sub '--------------------- I set up c2:h200 with the following string: "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE" The above procedure converted the 1,194 cells in question in ~.1 seconds. This strikes me as relatively fast, while your results will obviously vary with the strings in question. Regards, Nate Oliver |
#11
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Yes, it does Myrna, which David's post lead me to understand; my thanks to
the both of you. This would have a negative impact on IBM and/or McRitchie, which may be unavoidable outside of setting up an array of key terms given the all-cap starting point. And, I chose not to deal with proper nouns or acronyms, in that sense, the function is (very?) incomplete. I did a little testing, David. <g I just tested your function, and it failed (#VALUE!) on the very first cell which was empty. I know the poster said he started with all caps, and your encompassing SUB would eliminate that possibility. Quite right, a slight oversight on my part! And a quick fix: '------------------- Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, I As Long, i2 As Long If strIn = vbNullString Then Exit Function Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For I = 1 To UBound(bArr) Select Case bArr(I) Case 105 If Not I = UBound(bArr) Then Select Case bArr(I + 1) Case 33, 39, 44, 46, 58, 59, 63, 148, 160 bArr(I) = bArr(I) - 32 Case 32 If bArr(I - 1) = 32 Then _ bArr(I) = bArr(I) - 32 End Select ElseIf bArr(I - 1) = 32 Then _ bArr(I) = bArr(I) - 32 End If Case 33, 46, 58, 63 For i2 = I + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 I = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then I = i2: Exit For End If Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function '------------------- Thanks. The range I hard-coded was the specified range by the OP, and you're correct, one could very easily make this dynamic without too much effort. You could convert SCase() to an inline sub or crank out an array as Peter T has done, or disable recalculations temporarily. The algorithm itself is the most time intensive consideration I suspect. Thanks for the feedback Peter, glad to hear you like it. Byte Arrays are pretty efficient. And I didn't attempt to optimize that sub procedure, but indeed, looping through an Array should be much faster than looping through a Range if they're sized equally. The speed of using a Byte Array caught my attention one day I when I decided I was going to crop an MP3 file with Excel. So I wrote a procedure using Binary File Access and a couple of Byte Arrays: http://mrexcel.com/board2/viewtopic.php?p=306631#306631 (I'm not sure that particular code is optimized...) In any case, I thought I was going to be sitting around all day, but not at all. I didn't even need a timer to see how fast these things are. I just did it again on a 7.5 MB file, while the procedure loops 7.5 million times (used Currency variables), I cut the file in half in 16 seconds. This strikes me as fast. I'm not necessarily saying you should use RegExp or Byte arrays over one another, in fact I've seen string parsing on very large strings performed more efficiently with RegExp that I could match with the Byte Array. It's hard to get around some of the overhead with StrConv() and that VBA is a high-level and slower language. I just thought I'd write a UDF for Teresa that I knew would be pretty quick-like and fairly robust. Cheers, Nate Oliver |
#12
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
One more revision, I mishandled an 'I':
'------------------ Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, I As Long, i2 As Long If strIn = vbNullString Then Exit Function Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For I = 1 To UBound(bArr) Select Case bArr(I) Case 105 If Not I = UBound(bArr) Then Select Case bArr(I + 1) Case 32, 33, 39, 44, 46, 58, 59, 63, 148, 160 If bArr(I - 1) = 32 Then _ bArr(I) = bArr(I) - 32 End Select ElseIf bArr(I - 1) = 32 Then _ bArr(I) = bArr(I) - 32 End If Case 33, 46, 58, 63 For i2 = I + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 I = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then I = i2: Exit For End If Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function Sub testTime() Debug.Print sCase("hello? erm, i'M only testing, eh. indeed, " & _ "i am inquisitve.") Debug.Print sCase$("how old?! 22 Years.") Debug.Print sCase$("how old?! twenty-two Years.") Debug.Print sCase$("hmmmm.... wOrking?!?! sam i am. yes-no? " & _ "isn't i'm isn't.") Debug.Print sCase("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") Debug.Print sCase("no WorRies, i'm ONLY testIng! yes-no?") Debug.Print sCase("mY fRiend & i") Debug.Print sCase("iiiiiiiiiiiiii") Debug.Print sCase("****T. toast %T i @") Debug.Print sCase(" sentences.") End Sub '------------------ Regards, Nate Oliver |
#13
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Nate - for your interest I compared times calling your function and Tushar
Mehta's CapFirstLetterOfSentences with vbscript.regexp (in the link referred to earlier by David McRitchie). I called each with the routine I posted earlier, range of 1200 strings using range array loop array range. Your method was 30 x faster. But this wasn't fair, as in the function a late binding reference to regexp is set 1200 times. So I converted it to an in line sub using similar range array loop array range. Much faster but still 1.6 times slower than your function, which is probably inconsequential. I don't know enough about either method to form an overall opinion, no doubt pros/cons to each. But interesting! Tushar's function as posted only converts first character to a capital, but I think not relevant to this comparison. However purely for doing that, I found it 3 x faster to do simply: s = UCase(Left$(s, 1)) & LCase(Mid$(s, 2, Len(s))) Regards, Peter T "Nate Oliver" wrote in message ... One more revision, I mishandled an 'I': '------------------ Function sCase(ByVal strIn As String) As String Dim bArr() As Byte, I As Long, i2 As Long If strIn = vbNullString Then Exit Function Let bArr = StrConv(LCase$(strIn), vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For I = 1 To UBound(bArr) Select Case bArr(I) Case 105 If Not I = UBound(bArr) Then Select Case bArr(I + 1) Case 32, 33, 39, 44, 46, 58, 59, 63, 148, 160 If bArr(I - 1) = 32 Then _ bArr(I) = bArr(I) - 32 End Select ElseIf bArr(I - 1) = 32 Then _ bArr(I) = bArr(I) - 32 End If Case 33, 46, 58, 63 For i2 = I + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 I = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then I = i2: Exit For End If Next End Select Next sCase = StrConv(bArr, vbUnicode) End Function Sub testTime() Debug.Print sCase("hello? erm, i'M only testing, eh. indeed, " & _ "i am inquisitve.") Debug.Print sCase$("how old?! 22 Years.") Debug.Print sCase$("how old?! twenty-two Years.") Debug.Print sCase$("hmmmm.... wOrking?!?! sam i am. yes-no? " & _ "isn't i'm isn't.") Debug.Print sCase("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE") Debug.Print sCase("no WorRies, i'm ONLY testIng! yes-no?") Debug.Print sCase("mY fRiend & i") Debug.Print sCase("iiiiiiiiiiiiii") Debug.Print sCase("****T. toast %T i @") Debug.Print sCase(" sentences.") End Sub '------------------ Regards, Nate Oliver |
#14
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hello again Peter,
Thanks for testing! Ive done some comparisons on more comparable RegExp versus Byte Array functions with respect to string parsing and my findings have been similar. Much faster but still 1.6 times slower than your function, which is probably inconsequential. I dont see how it could be inconsequential. One of my goals when writing a UDF is to write an efficient function, so for me its of consequence. It also serves to alleviate confusion regarding the following untested and inaccurate testimony that found its way into this thread: It uses regular expressions rather than checking each letter so would be faster. What Excel actually does under the hood is going to run circles around you using VBA because you are not even close to machine instructions. And to clear up further confusion, its Regular Expression working on the strings under the hood, not Excel. No, no and no. The reason I say the two functions are not necessarily comparable is that the algorithm Ive posted is more robust. It handles varying punctuation and Is (which the OP specifically asked for), e.g., MsgBox CapFirstLetterOfSentences( _ LCase$("hello? hi! just me, myself and i, while i'm testing.")) MsgBox sCase("hello? hi! just me, myself and i, while i'm testing.") Surely Sentence Casing does not consider full stops alone? Could one layer in this functionality with the RegExp approach? Perhaps, but I doubt that would speed it up. So not only is sCase() as posted more efficient, its more robust, to the extent where comparing the two might not even make sense. Tushar's function as posted only converts first character to a capital, but I think not relevant to this comparison. However purely for doing that, I found it 3 x faster to do simply: s = UCase(Left$(s, 1)) & LCase(Mid$(s, 2, Len(s))) Not exactly, that function anticipates and effectively deals with multiple sentences with full stops, which the OP had and inquired about. Try the following: Const TST_STR As String = "SENTENCE ONE. SENTENCE TWO. SENTENCE THREE." MsgBox CapFirstLetterOfSentences(LCase$(TST_STR)) MsgBox UCase(Left$(TST_STR, 1)) & LCase(Mid$(TST_STR, 2, Len(TST_STR))) MsgBox sCase(TST_STR) As I mentioned prior to my recent adjustments to sCase(), its been given limited thought and testing, but heres what I see: -Its very fast -Its flexible/robust -It offers 100% compatibility for Excel users -It places no reliance on 3rd party objects/it's native -Reuse is a snap -Maintenance is straightforward It begs the question, what else does one want in String parsing function? Have a nice weekend. Regards, Nate Oliver |
#15
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hi Nate,
Thanks for your follow up, I agree with some of your points but not entirely <g. You had already sold me on the idea of the Byte Array method but I could still be a buyer of RegExp. my comment "1.6 x slower [byte vs regexp] is probably inconsequential" and your counter. I should have said "could be inconsequential", I had several things in mind - Despite my allowances for the references, my limited test was still not quite comparable, Tushar's routine also and necessarily includes some string functions. 1.6 x slower than extremely fast is still extremely fast. With small qty's of strings (say 1200) and called occasionally, to me, then it is inconsequential. That's not the same as insignificant but other factors become more important. It's a kind of Quick vs Bubble Sort choice. I suspect, but don't yet know, there may be occasions when regexp would be the preferred method. But with my limited knowledge of regexp I would elect for your Byte method. Why - I've used RegExp just few times between long intervals. Each time I need to re-learn the pattern syntax from scratch, and that takes me a very long time to write and test! Another thing - due to the delay it takes to reference regexp, it would require passing multiple strings as an array and looping within the function. Makes it slightly less portable. By contrast, once the Byte Array method is understood it can be worked out from first principals - no re-learning, also some of the other advantages you mentioned. However - and you're not going to like this! - for the task given by the OP and limited calls, I would use my string function (the one I mentioned but didn't post). It's between almost zero to 3 x slower than your method (depends on string & corrections), but has its advantages where ultimate speed is not a prerequisite: Why - I understand it well, only took a few minutes to write, and rightly or lazily to me that's a consideration. Corrections and acronyms etc are easy to include, even by a VBA novice later without needing to understand the entire function (I think), albeit at some degradation in performance. In other words easy to maintain. I suppose I ought to put up or shut up, so I'll suffer the consequences of you pointing out errors or limitations and post below. I'm very pleased you posted your Byte function, can only wonder if the OP is! Regards, Peter T If viewing in Googal proportional font, suggest switching to fixed font, or Options show original. Public Declare Function GetTickCount Lib "kernel32.dll" () As Long Sub Test_SentCap() Dim r As Long, c As Long Dim rng As Range Dim vCells Dim t1 As Long, t2 As Long Dim str As String, i As Byte Dim aTxt(1 To 12) 'As String aTxt(1) = "this is the 1ST sentence. second sentence. This " _ & "requires no caps.a sentence with no leading spaces." _ & " sentence with 10 leading spaces" aTxt(2) = "hello? erm, i'M only testing, eh. " _ & "indeed, i am inquisitve." aTxt(3) = "how old?! 22 Years." aTxt(4) = "how old?! twenty-two Years." aTxt(5) = "hmmmm.... wOrking?!?! sam i am. yes-no? isn't i'm isn't." aTxt(6) = "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE" aTxt(7) = "no WorRies, i'm ONLY testIng! yes-no?" aTxt(8) = "mY fRiend & i" aTxt(9) = "iiiiiiiiiiiiii" aTxt(10) = "****T. toast %T i @" aTxt(11) = " sentences." 'doesn't yet correct s S aTxt(12) = "HI MR OLIVER. IT'S I, PETER" Set rng = Range("C1:h200") '1200 cells For i = 1 To 12 rng.Clear rng.Value = aTxt(i) 'Stop ' and have a look? t1 = GetTickCount vCells = rng.Value t2 = GetTickCount For r = 1 To UBound(vCells) For c = 1 To UBound(vCells, 2) SentCap vCells(r, c) ', True Next Next t2 = GetTickCount - t2 rng.Value = vCells t1 = GetTickCount - t1 'Stop ' and have a look? Debug.Print aTxt(i) Debug.Print vCells(1, 1) Debug.Print "Overall " & t1 / 1000, , "Function " & t2 / 1000 Debug.Print Next Set rng = Nothing: Erase vCells End Sub Function SentCap(vs) As Boolean Dim b As Boolean Dim i As Long, k As Long, naCnt As Byte Dim n As Long, t As Long Dim nLen As Long Dim aCorrect() As String Const cSPACE As String = " " Const cDOT As String = "." 'A few simple corrections naCnt = 8 ' count of corrections ReDim aCorrect(1 To naCnt, 0 To 1) As String 'could maintain this array as Static & erase when 'done, if called as a big loop. 'or pass as an argument for special purposes aCorrect(1, 0) = " i ": aCorrect(1, 1) = " I " aCorrect(2, 0) = " i.": aCorrect(2, 1) = " I." aCorrect(3, 0) = " i,": aCorrect(3, 1) = " I," aCorrect(4, 0) = " i'": aCorrect(4, 1) = " I'" aCorrect(5, 0) = "mr": aCorrect(5, 1) = "Mr" aCorrect(6, 0) = "mrs": aCorrect(6, 1) = "Mrs" aCorrect(7, 0) = "oliver": aCorrect(7, 1) = "Oliver" aCorrect(8, 0) = "peter": aCorrect(8, 1) = "Peter" ' careful of names/acronyms that could be suffix or prefix ' or common words, eg "miss", "nate" vs = CStr(vs) nLen = Len(vs) n = 1: t = 1 ReDim va(0) 'array for each sentance 'split each sentance ending in DOT ' (probably easier to use vba's Split function) Do While n 0 n = InStr(n, vs, cDOT) If n Then 'include any more spaces after the DOT b = True Do While (b) b = Mid$(vs, n + 1, 1) = cSPACE If b Then n = n + 1 End If Loop If cnt Then ReDim Preserve va(cnt) va(cnt) = Mid(vs, t, n - t + 1) cnt = cnt + 1 n = n + 1 t = n End If Loop If t - 1 < nLen Then 'in case last sentance doesn't end with a DOT If cnt Then ReDim Preserve va(cnt + 1) va(cnt) = Mid$(vs, t, nLen - t + 1) End If For i = 0 To UBound(va) 'process each sentance, starting with initial capital va(i) = UCase$(Left$(va(i), 1)) & LCase$(Mid$(va(i), 2, Len(va(i)))) 'now the corrections For k = 1 To naCnt If InStr(1, va(i), aCorrect(k, 0)) Then #If VBA6 Then va(i) = Replace(va(i), aCorrect(k, 0), aCorrect(k, 1)) #Else 'for Excel 97 va(i) = Application.Substitute(va(i), " i ", " I ") #End If End If Next 'any more corrections, eg trailing "I" without punctuation If Right$(va(i), 2) = " i" Then va(i) = Left$(va(i), Len(va(i)) - 2) & " I" End If Next 're-join the sentances 'this loop seems as fast as "vs = Join(va)" & OK for Excel 97 For i = 1 To UBound(va) va(0) = va(0) & va(i) Next vs = va(0) End Function |
#16
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Here is Nate's code converted to a just a macro which would
be much faster. I really can't see someone wanting to convert one cell on a workbook based on another cell for this kind of thing. Though I think the usual requirement would be that the first letter and/or first letter after a period (or ? or !) would all that would need to be changed so I would leave out the initial conversion to lowercase, except that that was a requirement of the poster. For which I previously indicated I would have used a separate macro to convert everything to lowercase first if really needed Rather than destroying existing proper nouns. As a straight macro it will run much faster than the macro calling the function for each cell. Time comparisons at end at end. Don't know what was to be tested with checking for an ending (close) double quotes. Sub sCase_mac() ' Sentence case, Nate Oliver, 2005-03-17 ' http://groups.google.co.uk/groups?th...0microsoft.com Application.ScreenUpdating = False Application.Calculation = xlCalculationManual Dim bArr() As Byte, i As Long, i2 As Long Dim cell As Range For Each cell In Intersect(Selection, _ Selection.SpecialCells(xlConstants, xlTextValues)) If cell.Value = vbNullString Then GoTo nextcell Let bArr = StrConv(LCase$(cell.Value), vbFromUnicode) Select Case bArr(0) Case 97 To 122 'lowercase a-z bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 'lowercase i, going for I'm If Not i = UBound(bArr) Then Select Case bArr(i + 1) ' space ! ' , . : ; ? rdquo nbsp Case 32, 33, 39, 44, 46, 58, 59, 63, 148, 160 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 '-- ! . : ? For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then i = i2: Exit For End If Next End Select Next i cell.Value = StrConv(bArr, vbUnicode) nextcell: Next cell Application.Calculation = xlCalculationAutomatic Application.ScreenUpdating = True End Sub Timing for calling the RegExpr included the following between the begin and end timing points. But no attempt was made to handle end of sentences, by question mark, and explanation point, which Nate's macros has covered. Selection.Replace what:=" i'm ", replacement:=" I'm ", _ lookat:=xlPart, searchorder:=xlByRows, MatchCase:=False Testing: (about 700 cells each in each test.) Latest test at top, averages at bottom. RegExpr calls Funct. strictly Macro 0.341 secs., 0.280 secs., 0.200 secs., 0.341 secs., 0.280 secs., 0.210 secs., 0.351 secs., 0.280 secs., 0.201 secs., 0.351 secs., 0.280 secs., 0.201 secs., 0.350 secs., 0.290 secs., 0.221 secs., 0.360 secs., 0.281 secs., 0.210 secs., 0.350 secs., 0.301 secs., 0.210 secs., 0.350 secs., 0.291 secs., 0.220 secs., 0.360 secs., 0.291 secs., 0.200 secs., 0.390 secs., 0.301 secs., 0.210 secs., 0.481 secs., 0.401 secs., 0.220 secs., 0.481 secs., 0.410 secs., 0.211 secs., 0.491 secs., 0.400 secs., 0.200 secs., 0.480 secs., 0.411 secs., 0.210 secs., 0.481 secs., 0.411 secs., 0.200 secs., 0.491 secs., 0.410 secs., 0.211 secs., 0.490 secs., 0.411 secs., 0.210 secs., 0.501 secs., 0.421 secs., 0.200 secs., 0.491 secs., 0.410 secs., 0.211 secs., 0.490 secs., 0.421 secs., 0.200 secs., 0.491 secs., 0.421 secs., 0.210 secs., averages-------------------------------------------------- 0.424 secs., 0.352 secs., 0.208 secs. --- HTH, David McRitchie, Microsoft MVP - Excel [site changed Nov. 2001] My Excel Pages: http://www.mvps.org/dmcritchie/excel/excel.htm Search Page: http://www.mvps.org/dmcritchie/excel/search.htm "Peter T" <peter_t@discussions wrote in message ... Hi Nate, Thanks for your follow up, I agree with some of your points but not entirely <g. You had already sold me on the idea of the Byte Array method but I could still be a buyer of RegExp. my comment "1.6 x slower [byte vs regexp] is probably inconsequential" and your counter. I should have said "could be inconsequential", I had several things in mind - Despite my allowances for the references, my limited test was still not quite comparable, Tushar's routine also and necessarily includes some string functions. 1.6 x slower than extremely fast is still extremely fast. With small qty's of strings (say 1200) and called occasionally, to me, then it is inconsequential. That's not the same as insignificant but other factors become more important. It's a kind of Quick vs Bubble Sort choice. I suspect, but don't yet know, there may be occasions when regexp would be the preferred method. But with my limited knowledge of regexp I would elect for your Byte method. Why - I've used RegExp just few times between long intervals. Each time I need to re-learn the pattern syntax from scratch, and that takes me a very long time to write and test! Another thing - due to the delay it takes to reference regexp, it would require passing multiple strings as an array and looping within the function. Makes it slightly less portable. By contrast, once the Byte Array method is understood it can be worked out from first principals - no re-learning, also some of the other advantages you mentioned. However - and you're not going to like this! - for the task given by the OP and limited calls, I would use my string function (the one I mentioned but didn't post). It's between almost zero to 3 x slower than your method (depends on string & corrections), but has its advantages where ultimate speed is not a prerequisite: Why - I understand it well, only took a few minutes to write, and rightly or lazily to me that's a consideration. Corrections and acronyms etc are easy to include, even by a VBA novice later without needing to understand the entire function (I think), albeit at some degradation in performance. In other words easy to maintain. I suppose I ought to put up or shut up, so I'll suffer the consequences of you pointing out errors or limitations and post below. I'm very pleased you posted your Byte function, can only wonder if the OP is! Regards, Peter T If viewing in Googal proportional font, suggest switching to fixed font, or Options show original. Public Declare Function GetTickCount Lib "kernel32.dll" () As Long Sub Test_SentCap() Dim r As Long, c As Long Dim rng As Range Dim vCells Dim t1 As Long, t2 As Long Dim str As String, i As Byte Dim aTxt(1 To 12) 'As String aTxt(1) = "this is the 1ST sentence. second sentence. This " _ & "requires no caps.a sentence with no leading spaces." _ & " sentence with 10 leading spaces" aTxt(2) = "hello? erm, i'M only testing, eh. " _ & "indeed, i am inquisitve." aTxt(3) = "how old?! 22 Years." aTxt(4) = "how old?! twenty-two Years." aTxt(5) = "hmmmm.... wOrking?!?! sam i am. yes-no? isn't i'm isn't." aTxt(6) = "THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE" aTxt(7) = "no WorRies, i'm ONLY testIng! yes-no?" aTxt(8) = "mY fRiend & i" aTxt(9) = "iiiiiiiiiiiiii" aTxt(10) = "****T. toast %T i @" aTxt(11) = " sentences." 'doesn't yet correct s S aTxt(12) = "HI MR OLIVER. IT'S I, PETER" Set rng = Range("C1:h200") '1200 cells For i = 1 To 12 rng.Clear rng.Value = aTxt(i) 'Stop ' and have a look? t1 = GetTickCount vCells = rng.Value t2 = GetTickCount For r = 1 To UBound(vCells) For c = 1 To UBound(vCells, 2) SentCap vCells(r, c) ', True Next Next t2 = GetTickCount - t2 rng.Value = vCells t1 = GetTickCount - t1 'Stop ' and have a look? Debug.Print aTxt(i) Debug.Print vCells(1, 1) Debug.Print "Overall " & t1 / 1000, , "Function " & t2 / 1000 Debug.Print Next Set rng = Nothing: Erase vCells End Sub Function SentCap(vs) As Boolean Dim b As Boolean Dim i As Long, k As Long, naCnt As Byte Dim n As Long, t As Long Dim nLen As Long Dim aCorrect() As String Const cSPACE As String = " " Const cDOT As String = "." 'A few simple corrections naCnt = 8 ' count of corrections ReDim aCorrect(1 To naCnt, 0 To 1) As String 'could maintain this array as Static & erase when 'done, if called as a big loop. 'or pass as an argument for special purposes aCorrect(1, 0) = " i ": aCorrect(1, 1) = " I " aCorrect(2, 0) = " i.": aCorrect(2, 1) = " I." aCorrect(3, 0) = " i,": aCorrect(3, 1) = " I," aCorrect(4, 0) = " i'": aCorrect(4, 1) = " I'" aCorrect(5, 0) = "mr": aCorrect(5, 1) = "Mr" aCorrect(6, 0) = "mrs": aCorrect(6, 1) = "Mrs" aCorrect(7, 0) = "oliver": aCorrect(7, 1) = "Oliver" aCorrect(8, 0) = "peter": aCorrect(8, 1) = "Peter" ' careful of names/acronyms that could be suffix or prefix ' or common words, eg "miss", "nate" vs = CStr(vs) nLen = Len(vs) n = 1: t = 1 ReDim va(0) 'array for each sentance 'split each sentance ending in DOT ' (probably easier to use vba's Split function) Do While n 0 n = InStr(n, vs, cDOT) If n Then 'include any more spaces after the DOT b = True Do While (b) b = Mid$(vs, n + 1, 1) = cSPACE If b Then n = n + 1 End If Loop If cnt Then ReDim Preserve va(cnt) va(cnt) = Mid(vs, t, n - t + 1) cnt = cnt + 1 n = n + 1 t = n End If Loop If t - 1 < nLen Then 'in case last sentance doesn't end with a DOT If cnt Then ReDim Preserve va(cnt + 1) va(cnt) = Mid$(vs, t, nLen - t + 1) End If For i = 0 To UBound(va) 'process each sentance, starting with initial capital va(i) = UCase$(Left$(va(i), 1)) & LCase$(Mid$(va(i), 2, Len(va(i)))) 'now the corrections For k = 1 To naCnt If InStr(1, va(i), aCorrect(k, 0)) Then #If VBA6 Then va(i) = Replace(va(i), aCorrect(k, 0), aCorrect(k, 1)) #Else 'for Excel 97 va(i) = Application.Substitute(va(i), " i ", " I ") #End If End If Next 'any more corrections, eg trailing "I" without punctuation If Right$(va(i), 2) = " i" Then va(i) = Left$(va(i), Len(va(i)) - 2) & " I" End If Next 're-join the sentances 'this loop seems as fast as "vs = Join(va)" & OK for Excel 97 For i = 1 To UBound(va) va(0) = va(0) & va(i) Next vs = va(0) End Function |
#17
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hi David,
It was good of you to post your adaptation of Nate's Byte Array function and test comparisons. I really can't see someone wanting to convert one cell on a workbook based on another cell for this kind of thing. Afraid I don't follow, as I understand it the function and your sub only convert existing cells. Possibly I've missed something from earlier in the thread. As a straight macro it will run much faster than the macro calling the function for each cell. I couldn't replicate your time difference's, straight macro vs called function. In my testing only minimal difference. Whatever, the sub or function could still be "in line" with the entire range or array is passed as an argument, and looped within. Would avoid hardcoding the range within the sub/func, or restricting to a user selection, to give greater flexibility of usage. I found by far the greatest consumption of time is the read write from cells with "For each Cell", much more than the actual processing. Which is why I favour range array loop array range with larger volumes. I'm starting to use StrConv & Byte array for some other related purposes. FWIW reading help suggests it won't work in Mac. Regards, Peter T "David McRitchie" wrote in message ... Here is Nate's code converted to a just a macro which would be much faster. I really can't see someone wanting to convert one cell on a workbook based on another cell for this kind of thing. Though I think the usual requirement would be that the first letter and/or first letter after a period (or ? or !) would all that would need to be changed so I would leave out the initial conversion to lowercase, except that that was a requirement of the poster. For which I previously indicated I would have used a separate macro to convert everything to lowercase first if really needed Rather than destroying existing proper nouns. As a straight macro it will run much faster than the macro calling the function for each cell. Time comparisons at end at end. Don't know what was to be tested with checking for an ending (close) double quotes. Sub sCase_mac() ' Sentence case, Nate Oliver, 2005-03-17 ' http://groups.google.co.uk/groups?th...3E9-2CD1F94431 CB%40microsoft.com Application.ScreenUpdating = False Application.Calculation = xlCalculationManual Dim bArr() As Byte, i As Long, i2 As Long Dim cell As Range For Each cell In Intersect(Selection, _ Selection.SpecialCells(xlConstants, xlTextValues)) If cell.Value = vbNullString Then GoTo nextcell Let bArr = StrConv(LCase$(cell.Value), vbFromUnicode) Select Case bArr(0) Case 97 To 122 'lowercase a-z bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 'lowercase i, going for I'm If Not i = UBound(bArr) Then Select Case bArr(i + 1) ' space ! ' , . : ; ? rdquo nbsp Case 32, 33, 39, 44, 46, 58, 59, 63, 148, 160 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 '-- ! . : ? For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 Then i = i2: Exit For End If Next End Select Next i cell.Value = StrConv(bArr, vbUnicode) nextcell: Next cell Application.Calculation = xlCalculationAutomatic Application.ScreenUpdating = True End Sub Timing for calling the RegExpr included the following between the begin and end timing points. But no attempt was made to handle end of sentences, by question mark, and explanation point, which Nate's macros has covered. Selection.Replace what:=" i'm ", replacement:=" I'm ", _ lookat:=xlPart, searchorder:=xlByRows, MatchCase:=False Testing: (about 700 cells each in each test.) Latest test at top, averages at bottom. RegExpr calls Funct. strictly Macro 0.341 secs., 0.280 secs., 0.200 secs., 0.341 secs., 0.280 secs., 0.210 secs., 0.351 secs., 0.280 secs., 0.201 secs., 0.351 secs., 0.280 secs., 0.201 secs., 0.350 secs., 0.290 secs., 0.221 secs., 0.360 secs., 0.281 secs., 0.210 secs., 0.350 secs., 0.301 secs., 0.210 secs., 0.350 secs., 0.291 secs., 0.220 secs., 0.360 secs., 0.291 secs., 0.200 secs., 0.390 secs., 0.301 secs., 0.210 secs., 0.481 secs., 0.401 secs., 0.220 secs., 0.481 secs., 0.410 secs., 0.211 secs., 0.491 secs., 0.400 secs., 0.200 secs., 0.480 secs., 0.411 secs., 0.210 secs., 0.481 secs., 0.411 secs., 0.200 secs., 0.491 secs., 0.410 secs., 0.211 secs., 0.490 secs., 0.411 secs., 0.210 secs., 0.501 secs., 0.421 secs., 0.200 secs., 0.491 secs., 0.410 secs., 0.211 secs., 0.490 secs., 0.421 secs., 0.200 secs., 0.491 secs., 0.421 secs., 0.210 secs., averages-------------------------------------------------- 0.424 secs., 0.352 secs., 0.208 secs. --- HTH, David McRitchie, Microsoft MVP - Excel [site changed Nov. 2001] My Excel Pages: http://www.mvps.org/dmcritchie/excel/excel.htm Search Page: http://www.mvps.org/dmcritchie/excel/search.htm |
#18
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
What I mean is I can't see anyone wanting to use a function
on the workbook to return the value of a cell large enough to include an entire sentence. I would think someone would only want to use a macro and change the values of the selected cells in place. A function only returns a value. The point is I can't see using a function like that within the worksheet. It usually means copying the result, pasting back as values and then deleting the original column -- waste of time. If you want I can email you my test. --- "Peter T" <peter_t@discussions wrote in message Afraid I don't follow, as I understand it the function and your sub only convert existing cells. Possibly I've missed something from earlier in the thread. |
#19
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hi David,
I see what you meant now. I hadn't envisaged this being used as a UDF, though of course it could. Rather, as a function (or sub with arguments) in tandem with another sub or macro, when the "cell" or an element from an array can be passed ByRef. This is how I tested and I guess accounts for the differences in our comparisons. I noticed only about 2% longer between using single macro and multiple calls to the function. However if I've misunderstood I would be very pleased to see your test and understand our different results. It's a shame to waste all the efforts of an efficient routine by using it inefficiently. Thanks, Peter T pmbthornton at gmail dot com "David McRitchie" wrote in message ... What I mean is I can't see anyone wanting to use a function on the workbook to return the value of a cell large enough to include an entire sentence. I would think someone would only want to use a macro and change the values of the selected cells in place. A function only returns a value. The point is I can't see using a function like that within the worksheet. It usually means copying the result, pasting back as values and then deleting the original column -- waste of time. If you want I can email you my test. --- "Peter T" <peter_t@discussions wrote in message Afraid I don't follow, as I understand it the function and your sub only convert existing cells. Possibly I've missed something from earlier in the thread. |
#20
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hi Peter,
Yes, I think we agree. I was a bit surprised that the there wasn't a lot of overhead in calling a function for each cell in a macro, which you also indicate to be the case. Don't know if it makes much of a difference but the function is in the same module as the macro. More information about the timing in on my slowresp.htm Excel page. Actually I meant to indicate to email me if you wanted the workbook. But since we now appear to be in agreement was better that you replied here. I've sent my test workbook to you. Data of interest is below the timing rows, but all the rows go through the testing including previous timings. I used the avg on the task bar to show averages in my posting. --- HTH, David McRitchie, Microsoft MVP - Excel [site changed Nov. 2001] My Excel Pages: http://www.mvps.org/dmcritchie/excel/excel.htm Search Page: http://www.mvps.org/dmcritchie/excel/search.htm |
#21
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Of course it is difficult to send without a *valid* email address
so if you want my test workbook, email me before I forget which Excel workbook. |
#22
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Hello again,
Though I think the usual requirement would be that the first letter and/or first letter after a period (or ? or !) would all that would need to be changed so I would leave out the initial conversion to lowercase, except that that was a requirement of the poster. For which I previously indicated I would have used a separate macro to convert everything to lowercase first if really needed Rather than destroying existing proper nouns. Thanks for testing David. Actually, David, other than the fact that I hard-coded LCase() in the function (once), my function pretty much does that. See the following: '------------------- Function sCase2(ByRef strIn As String) As String Dim bArr() As Byte, i As Long, i2 As Long If strIn = vbNullString Then Exit Function Let bArr = StrConv(strIn, vbFromUnicode) Select Case bArr(0) Case 97 To 122 bArr(0) = bArr(0) - 32 End Select For i = 1 To UBound(bArr) Select Case bArr(i) Case 105 If Not i = UBound(bArr) Then Select Case bArr(i + 1) Case 32, 33, 39, 44, 46, 58, 59, 63, 148, 160 If bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End Select ElseIf bArr(i - 1) = 32 Then _ bArr(i) = bArr(i) - 32 End If Case 33, 46, 58, 63 For i2 = i + 1 To UBound(bArr) Select Case bArr(i2) Case 97 To 122 bArr(i2) = bArr(i2) - 32 i = i2: Exit For End Select If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 And bArr(i2) < 160 Then i = i2: Exit For End If Next End Select Next sCase2 = StrConv(bArr, vbUnicode) End Function Sub testTime() Debug.Print sCase2(LCase$("hello? erm, i'M only testing, eh. indeed, " & _ "i am inquisitve.")) Debug.Print sCase2(LCase$("how old?! 22 Years.")) Debug.Print sCase2(LCase$("how old?! twenty-two Years.")) Debug.Print sCase2(LCase$("hmmmm.... wOrking?!?! sam i am. yes-no? " & _ "isn't i'm isn't.")) Debug.Print sCase2(LCase$("THE DAY WAS SUNNY AND I WORE A HAT.PETER WAS THERE")) Debug.Print sCase2(LCase$("no WorRies, i'm ONLY testIng! yes-no?")) Debug.Print sCase2(LCase$("mY fRiend & i")) Debug.Print sCase2(LCase$("iiiiiiiiiiiiii")) Debug.Print sCase2(LCase$("***T. toast %T i @")) Debug.Print sCase2(LCase$(" sentences.")) Debug.Print sCase2("hello? thought i'd test this for David McRitchie. NOTHING.") End Sub '------------------- Doesn't help the OP much, unless they know to use LCase$() as I have. I'm going to leave this as a function on my end. It helps distinguish the two different processes going on he String parsing and Object Property manipulation. And, it makes resuse a lot easier than cutting up the in-line procedure. This says nothing to in-line procedures being more efficient, I'll agree with you on this as it's true. Peter, Thanks for your follow up, I agree with some of your points but not entirely <g. You had already sold me on the idea of the Byte Array method but I could still be a buyer of RegExp. I'm not sure I'm all out anti RegExp, I just wanted to help Teresa out and this made sense to me for a variety of reasons. Despite my allowances for the references, my limited test was still not quite comparable, Tushar's routine also and necessarily includes some string functions. 1.6 x slower than extremely fast is still extremely fast. With small qty's of strings (say 1200) and called occasionally, to me, then it is inconsequential. That's not the same as insignificant but other factors become more important. It's a kind of Quick vs Bubble Sort choice. As does mine. Well there are other factors. Does everyone know RegExp? Does everyone have RegExp? What about those pesky question marks, etc... There are more considerations. 0 X? Can SentCap return a String? I wouldn't mind testing that function in its own right without having to rework it (I've had my share of reworking for this thread <g). Why - I understand it well, only took a few minutes to write, and rightly or lazily to me that's a consideration. Corrections and acronyms etc are easy to include, even by a VBA novice later without needing to understand the entire function (I think), albeit at some degradation in performance. In other words easy to maintain. Common argument. However, I tend to stay away from the 'let's program to the lowest common denominator so that everyone gets it' point of view or practice. We can talk about execution time, # of code lines, compatibility, disk footprint, but I find the latter to be too relative and gray to make any sense of, I don't know what the base is, so I don't do it. Here's what I will say however, byte arrays aren't as scary as they may seem. I'd recommend learning more about them and perhaps commenting your code. Bruce McKinney's Hardcore Visual Basic might be as good a place to start as any: http://vb.mvps.org/hardcore/html/whatisunicode.htm http://brucem.mystarband.net/mckinney2b.htm#12 Once you get a handle on them, you can crank them out pretty quickly, not as quick as they execute, but my first pass at SCase() didn't take too long, really. And you can cover a lot of ground with a fairly short function. But, I'm agreeable to disagreeing. Funny, I had never seen the second link above before. For international issues (although I'd wonder if we should capitalize Is at this point), it might make more sense to use the API calls Bruce flags for us. E.g., '----------------- Private Declare Function IsCharLower Lib "user32" Alias "IsCharLowerA" ( _ ByVal cChar As Byte) As Long Declare Function CharUpperBuffB Lib "user32" Alias "CharUpperBuffA" ( _ lpsz As Byte, _ ByVal cchLength As Long) As Long Function sCase3(ByVal strIn As String) As String Dim bArr() As Byte, I As Long, i2 As Long If strIn = vbNullString Then Exit Function Let bArr = StrConv(strIn, vbFromUnicode) CharUpperBuffB bArr(0), 1 For I = 1 To UBound(bArr) Select Case bArr(I) Case 105 If Not I = UBound(bArr) Then Select Case bArr(I + 1) Case 32, 33, 39, 44, 46, 58, 59, 63, 148, 160 If bArr(I - 1) = 32 Then _ CharUpperBuffB bArr(I), 1 End Select ElseIf bArr(I - 1) = 32 Or bArr(I - 1) = 160 Then _ CharUpperBuffB bArr(I), 1 End If Case 33, 46, 58, 63 For i2 = I + 1 To UBound(bArr) If IsCharLower(bArr(i2)) Then CharUpperBuffB bArr(i2), 1 I = i2: Exit For End If If bArr(i2) < 32 And bArr(i2) < 33 And bArr(i2) < 46 _ And bArr(i2) < 63 And bArr(i2) < 160 Then I = i2: Exit For End If Next End Select Next sCase3 = StrConv(bArr, vbUnicode) End Function Sub testTime44() Debug.Print sCase3(LCase$("À LA CARTE")) 'Something Debug.Print LCase$("À LA CARTE") 'Something Debug.Print sCase3("À LA CARTE") 'Nothing End Sub '----------------- The original SCase() while faster, can't handle 'Ã*'. Have a good evening, Nate Oliver |
#23
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Why - I understand it well, only took a few minutes to write, and rightly
or lazily to me that's a consideration. Corrections and acronyms etc are easy to include, even by a VBA novice later without needing to understand the entire function (I think), albeit at some degradation in performance. In other words easy to maintain. Common argument. However, I tend to stay away from the 'let's program to the lowest common denominator so that everyone gets it' point of view or practice. We can talk about execution time, # of code lines, compatibility, disk footprint, but I find the latter to be too relative and gray to make any sense of, I don't know what the base is, so I don't do it. Pretty serious typo, I meant former, not latter. Sorry about that. Regards, NPO |
#24
![]()
Posted to microsoft.public.excel.programming
|
|||
|
|||
![]()
Of course it is difficult to send without a *valid* email address
so if you want my test workbook, email me before I forget which Excel workbook. The thinly disguised address in my previous message is valid (again copied below). Also I emailed you direct yesterday. Not sure if you received it, if not please advise. Regards, Peter T pmbthornton at gmail dot com |
Reply |
Thread Tools | Search this Thread |
Display Modes | |
|
|
![]() |
||||
Thread | Forum | |||
Replacing capital letters | Excel Discussion (Misc queries) | |||
how to change small letters to capital letters | Excel Discussion (Misc queries) | |||
how do i turn all letters into capital letters? | Excel Discussion (Misc queries) | |||
Capital Letters Only | Excel Discussion (Misc queries) | |||
Capital Letters | Excel Worksheet Functions |