I created the "SuperTrim" function, quoted by Gary Cornell in "Visual Basic 6 from the Ground Up": the function removes excess spaces in a string.
Compared to the example found in the book, I have adapted the class, with the code by Marzo Junior (WordWrap_02 found in VbSpeed of Donald Lessau) obtaining excellent results. Here is the source code. To work requires FastString.tlb (In the Zip folder).
Regards.
Here's an alternative implementation that doesn't require a TypeLib:
Code:
Friend Function CollapseSpaces(ByVal sText As String) As String
Dim I As Long, J As Long, K As Long
K = Len(sText)
If K Then
SA1D.pvData = StrPtr(sText)
SA1D.cElements = K
I = 1&
'Trim leading spaces
If iChars(I) = 32 Then
For I = I To SA1D.cElements
If iChars(I) <> 32 Then Exit For
Next
End If
For I = I To SA1D.cElements
J = J + 1&
iChars(J) = iChars(I)
If iChars(I) = 32 Then
For K = I To SA1D.cElements
If iChars(K) <> 32 Then Exit For
Next
I = K - 1&
End If
Next
End If
'Trim trailing spaces
If J Then If iChars(J) = 32 Then J = J - 1&
CollapseSpaces = Left$(sText, J)
End Function
Is this only -
- Trimming all spaces from both ends
- Replacing multiple internal spaces with single space ?
If so, how about this -
Code:
Option Explicit
Private Sub Command1_Click()
Dim sBefore As String
Dim sAfter As String
sBefore = " Make me slimmer "
Me.Print sBefore
sAfter = SuperTrim(sBefore)
Me.Print sAfter
Me.Print "Length = " & Len(sAfter)
End Sub
Public Function SuperTrim(sIN As String) 'Remove spaces from end, and don't allow multiple spaces
Dim sWork As String
sWork = Trim(sIN) 'Get rid of spaces on both ends of the sentence
Do Until InStr(1, sWork, " ") = 0
sWork = Replace(sWork, " ", " ")
Loop
SuperTrim = sWork
End Function
Yeah, I was struggling to see what the big deal was as well. The only improvements I'd make to Bobbles's code is to use Trim$() rather than Trim() and also Replace$() rather than Replace(). This will stop all kinds of type-casting between Strings and Variants. Also, we can tighten it to remove the superfluous creation of extra strings. Bobbles, you just don't need that sWork string. In fact, we could do it all with one string if we were willing to use a Sub rather than a Function, but I'll stick with the function. Here's mine:
Code:
Private Function SuperTrim(s As String)
SuperTrim = Trim$(s)
Do While InStr(SuperTrim, " ")
SuperTrim = Replace$(SuperTrim, " ", " ")
Loop
End Function
Are we also talking about word-wrap? That wasn't clear to me. If that's so, then, yes, things will change substantially. fabel358, maybe you could outline to us what your objectives were.
Best Regards,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
The versions of "SuperTrim" presented by me and "Victor Bravo VI" - CollapseSpaces - have the advantage of being 11 to 13 times faster than these last examples.
Hmmm, I added my approach to VBVI's code and got the following:
Granted, it's the slowest, but not by a fact of 11 to 13. Sure, anytime we repeatedly use Replace$(), we're going to be shuffling strings around in memory (as oppose to swapping things around in-place. However, at some point, there's got to be a balance between doing simple tasks in an intuitive way versus speed. If we all learned C, I'm sure we could do many things faster. Also, I'm not sure if we're going to be packing spaces a great deal.
But fabel358, if someone does need maximum speed for this, they've now got it.
You take care,
Elroy
p.s. I didn't test Bobbles', but I'm sure it's slower because of all the typecasting it's doing.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.
I did not get Elroy's results in my PC; mine are very similar to those of "Victor Bravo VI". A further advantage, in addition to the higher speed of execution, is learning (a unorthodox but effective) programming techniques ...
Are you testing a compiled executable, or testing in the IDE? Running in the IDE will be much slower for many things compared to a finished executable.
@Dex: Ahhh, interesting. Since Replace$() works, I just assumed it was like Trim(), Mid(), and others (returning a Variant unless the $ was specified). I actually spent a bit of time with CopyMemory just to convince myself that Replace() is actually returning a String, and it is.
Also, @fabel358: Just as an FYI, I don't see anything fundamentally wrong with this thread, especially since you've illustrated that your method is faster. I do consider VB6 to be a high-level language though. And, as such, we should expect to occasionally pay some performance penalties. I still have memories of discussions with C programmers, where they could argue for hours about how to pick up individual click cycles. I've just never considered VB6 to be that kind of language. Rather, IMHO, it's a language to "get things out the door". However, learning how to do things faster (in any language) is never a bad thing.
Y'all Take Care,
Elroy
p.s. Here's my CopyMemory "play". With all the auto-type-conversion, I couldn't figure out another way to verify this:
Code:
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
Private Type VariantDescriptor
Type As Integer
Reserved As String * 6
Data As String * 8
End Type
Private Sub Form_Load()
Dim vd As VariantDescriptor
CopyMemory vd, Replace(" asdf ", " ", ""), 16
Debug.Print vd.Type
Stop
End Sub
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
I would definately avoid using two aliases for the same memory simultaneously. The VB6 optimiser makes the assumption that writing to one array isnt going to effect another, and vise-versa.
Under the optimization options you will see an 'assume no aliasing' option revealing that this is indeed an issue. Keeping that option turned off will cover an issue such as this:
Code:
Dim bar() As Byte
Function foo(a() As Byte)
' process a() and bar() simultaneously
End Sub
In the above case, the optimiser will assume that within foo a() and bar() *could* point to the same memory unless the 'assume no aliasing' option is checked off. After all, you might be making the call: foo(bar()) ..
The 'aliasing' idea only considers 'normal use' possibilities, tho.
When you are hacking up a safearray to point arbitrarily in memory, all bets are off. The optimiser cannot handle the issue correctly regardless of the state of the aliasing compiler switch so its best to avoid using aliases for the same memory simultaneously.
YMMV.
I only mention it because this project consistently crashes for me when this flag is set.
Code:
30 With New clsCollapseSpaces
40 Sleep 0&
50 QueryPerformanceCounter Start
60 For I = 1& To m_Iterations
70 sText = .CollapseSpaces(SrcText) ' <-- Error 91 Object variable or With block variable not set
80 Next
90 QueryPerformanceCounter Stop_
100 Stop_ = Stop_ - Start
110 End With
Apart from adding debugging code, was there anything else in my benchmark project that you've changed? Does it happen only in the IDE or EXE or both? What's your VB6 and service pack version? (mine is VB6 SP6 v9782) I've run the EXE compiled from that code in both XP 32-bit and Windows 7 64-bit (on the same machine) and apparently, it works in Windows 10 as well.
I added the debug code because it crashed. Oddly it only does it on your code, which uses the same method I use (originally from Curland).
That's why I knew about this particular issue. I'm also pretty sure Olaf was the one who tipped me off, though possibly not on this forum.
It's an old bug.
SP6 v9832 (Windows 7)
SP6 v9782 (XP)
The funny thing is if you set up exception handling - Even an On Error Goto 0, in the CollapseSpaces() method, the issue dissappears.
It'll probably be fun to trace through the differences between compiled output.
edit: optimizations only change compiled code. it doesn't crash in the IDE.
Last edited by DEXWERX; Feb 27th, 2018 at 06:00 PM.
That's strange... fabel358's class uses SAFEARRAY mapping as well but you're saying it doesn't crash unlike my code? The only difference that I can see that could possibly be the culprit is that fabel358 doesn't set any value for the SAFEARRAY.fFeatures member. I've done some quick tests to see what flags VB6 uses for that member and I've found out that for local and module-level Integer arrays, it always seems to be FADF_HAVEVARTYPE, not FADF_AUTO nor FADF_FIXEDSIZE. Also the cLocks member is 0 rather than 1. Could you please run a test again without those values?
I only get the Error 91 if I have the Favour Pentium Pro(tm) option selected and the program is compiled.
Ah, yes, now I'm getting that error too. I've never activated that option because it's pointless on modern CPUs.
I'm beginning to suspect that my algorithm is possibly the real culprit. fabel358 uses 2 SAFEARRAYs for copying characters from the source string to the (temporary) destination string while my algorithm uses only 1 SAFEARRAY. fabel358 copies character-by-character from a separate source string to a separate destination string while mine just shifts characters down on the same output string (I don't overwrite the source string because I make a copy via the ByVal parameter). I'm guessing the optimizations done to the array element shifting is somehow corrupting the (anonymous) object variable (With New clsCollapseSpaces), hence the error.
I've tested to see if removing the Assume No Aliasing optimization would negatively affect performance but I didn't notice any appreciable decrease in speed, so unselecting that option is indeed probably the safest and most reliable optimization setting. That said, SAFEARRAY hacking is inherently risky, so as noted by Rockoon in the other forum, turning off Assume No Aliasing is no guarantee compiled code will be crash-free.