Aw, shucks.PBJim wrote: Wed Feb 26, 2025 8:20 amYour own MSet() code was actually bug-free as you had anticipated the possibility of odd-numbered lengths, which Benubi hadn't.


Aw, shucks.PBJim wrote: Wed Feb 26, 2025 8:20 amYour own MSet() code was actually bug-free as you had anticipated the possibility of odd-numbered lengths, which Benubi hadn't.
Code: Select all
;/---------------------------------------------------------------------------------------------------------------------
;|
;| MSet()
;|
;| a combination of LSet() and RSet()
;|
;| Examples:
;| Debug MSet("pad this to 50 chars", 50, "-") ; => ---------------pad this to 50 chars---------------
;| Debug MSet(" Sunday ", 40, "=") ; => ================ Sunday ================
;| Debug MSet("Hello World", 5) ; => lo Wo
;|
;|
;| Author: benubi (small changes by me)
;|
;| LINK: https://www.purebasic.fr/english/viewtopic.php?p=636397&sid=b5caa0fe489da330a1eb186b6e0f4685#p636397
;|
;\---
Procedure.s MSet(String$, Length, Character$=" ") ; returns the padded string (text)
Protected result.s, pad, *in.CHARACTER, *out.CHARACTER
pad = (Length - Len(String$)) / 2 ; number of padding chars
result = LSet("", Length, Character$) ; create the output string (buffer) with padding chars
; copy the chars from input to output string
;
*in = @String$ + (Bool(pad < 0) * pad * -1) * SizeOf(CHARACTER)
*out = @result + (Bool(pad > 0) * pad * SizeOf(CHARACTER))
;
While *out\c And *in\c
*out\c = *in\c
*in + SizeOf(CHARACTER)
*out + SizeOf(CHARACTER)
Wend
ProcedureReturn result
EndProcedure
CompilerIf 10 ; <== Test Pattern Area -- activated by non-zero :)
Global Index
Macro DDebug(Text, RequestedLength)
Debug LSet(Text, 32) + " " + StringField("failed.ok.", 1+Bool(RequestedLength = Len(Text)), ".")
EndMacro
Debug #LF$+"1. Shorter than Length "
For Index = 3 To 5
DDebug(MSet("1234567890", Index, "-"), Index)
Next Index
Debug #LF$+"2. Shorter and Longer than Length "
For Index = 8 To 14
DDebug(MSet("1234567890", Index, "-"), Index)
Next Index
CompilerEndIf
It's because the odd number divided by 2 leaves a fraction, which is then lost because it's an integer calculation, hence the number of characters in either the padded result, or the string itself, can be 1 short.benubi wrote: Wed Feb 26, 2025 11:12 am Oops, true! Well, at least the last one works (Mset3), and yes I had only seen the odd length problem once the first glitch appeared. In Mset2() I try to correct it with (length & 1) and it seemed to work at first, but now with the cases PBJim showed it doesn't. I wonder why that is, it's probably something simple where you need only to count on your fingers until you see the problem ( but I haven't started with it yet)
Code: Select all
EnableExplicit
Define cnt.i
Procedure.s MSet(text$, length, char$=" ")
Define pos.i = length / 2 - Len(text$) / 2 ; Calculate left pad required
pos.i = Bool(pos.i > 0) * pos.i ; Adjust to 0 if nothing to left-pad
text$ = LSet(ReplaceString(Space(pos.i), " ", char$) + text$, length, char$) ; Left pad, append text, then left pad to total length
ProcedureReturn text$
EndProcedure
For cnt.i = 1 To 10 ; Test padding length between 1 and 10
Debug "========= Length " + cnt.i + " ========="
Debug Chr(34) + MSet("A", cnt.i, "-") + Chr(34) + " A"
Debug Chr(34) + MSet("AB", cnt.i, "-") + Chr(34) + " AB"
Debug Chr(34) + MSet("ABC", cnt.i, "-") + Chr(34) + " ABC"
Next
Maybe you're right Demivec, but this is a moot point!Demivec wrote: Wed Feb 26, 2025 12:49 pm For long strings with shorter lengths specified, both LSet() and RSet() truncate the original string from the right so shouldn't an MSet() function do the same for consistency?
Code: Select all
Debug Chr(34) + LSet("123456789.00", 10) + Chr(34) ; "123456789."
Debug Chr(34) + RSet("123456789.00", 10) + Chr(34) ; "123456789."
Debug Chr(34) + RSet(Right("123456789.00",10), 10) + Chr(34) ; "3456789.00"
Well, that should be easy by changing the code from above in one line:Demivec wrote: Wed Feb 26, 2025 12:49 pm For long strings with shorter lengths specified, both LSet() and RSet() truncate the original string from the right so shouldn't an MSet() function do the same for consistency?
Code: Select all
*in = @String$ ; (Bool(pad < 0) * pad * -1) * SizeOf(CHARACTER) ; <== start at the first char position
That was really luck and beauty of improvisation, just like if I'd unpredictively played a beautiful melody on piano by "accident". I just typed more by feeling and wanting to experiment than by very attentive calculation, even though I made a few console and drawtext based kind of similar things. I like the bool/branchless style some people posted here, emulated it and finally I also added the Character-While-loop I already use often. The bools part is fun in itself, but the While comparison is also since it simply tests for what string's ending first (target / source). This has a lot of implicit tests and functionality, just using bools & comparison logic.MSet3() is impressive. It even handles the case where, for instance, you output to max. 1 character, but the string is ABC. It therefore correctly places B as the output.
I didn't know any of those details, but it's how I also would think handling it. I never thought about testing the procedures that way, but that's also how I intuitively would do it. That's why I changed the 3rd code to truncate overlong strings on both ends, it seems logical that LSet would truncate on it's right side, and RSet on the left, but it's probably much faster to always start at the string's first character. If all Basic dialects do it the same way, it may be due to an old language convention since those kind of commands must have been present very early on or from start.I don't actually appreciate RSet() for doing that, as I come from a financial accounting standpoint where the convention is for left side truncation on large numeric figures. BASIC languages in general, such as VB and its predecessors are mostly the same and don't properly provide for this, so PB is no exception.