Add MSet() function to string library

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
BarryG
Addict
Addict
Posts: 4121
Joined: Thu Apr 18, 2019 8:17 am

Re: Add MSet() function to string library

Post by BarryG »

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. :D
Aw, shucks. :oops: But yes, I did anticipate that, which is why I had the "If Len(text$)<length" check. ;)
benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

Re: Add MSet() function to string library

Post by benubi »

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 :lol: )
Axolotl
Enthusiast
Enthusiast
Posts: 798
Joined: Wed Dec 31, 2008 3:36 pm

Re: Add MSet() function to string library

Post by Axolotl »

Hi benubi and guys,

I would like to confirm the above results.
I tested yesterday and forgot to post.
Anyway, here is the code that made it into my code library.
BTW: I just refactored a bit, so I understand the whole thing better.

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 
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
Demivec
Addict
Addict
Posts: 4257
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Add MSet() function to string library

Post by Demivec »

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?
PBJim
Enthusiast
Enthusiast
Posts: 294
Joined: Fri Jan 19, 2024 11:56 pm

Re: Add MSet() function to string library

Post by PBJim »

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 :lol: )
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.

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.

Mine does not do that, I wanted a version with minimal steps :

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
PBJim
Enthusiast
Enthusiast
Posts: 294
Joined: Fri Jan 19, 2024 11:56 pm

Re: Add MSet() function to string library

Post by PBJim »

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?
Maybe you're right Demivec, but this is a moot point!

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.

Therefore we have to do the Right() adjustment in line 3 below, to achieve what we want.

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"
Axolotl
Enthusiast
Enthusiast
Posts: 798
Joined: Wed Dec 31, 2008 3:36 pm

Re: Add MSet() function to string library

Post by Axolotl »

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?
Well, that should be easy by changing the code from above in one line:

Code: Select all

  *in  = @String$ ; (Bool(pad < 0) * pad * -1) * SizeOf(CHARACTER)   ; <==  start at the first char position 
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

Re: Add MSet() function to string library

Post by benubi »

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.
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.

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.
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.
Post Reply