[Solved ]Multiply String / Equivalent of Space for strings?

Just starting out? Need help? Post your questions and find answers here.
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

[Solved ]Multiply String / Equivalent of Space for strings?

Post by hoerbie »

Hi,
coming from GFA Basic there was a command STRING$, so that for example STRING$(5,"123 ") produced a string "123 123 123 123 123 ", just like Space(5) produces a string of five spaces.
I know how to make this with a for/while loop, but is there a better/faster way?
I tried to use LSet() and RSet() for this, but it only accepts one char for filling and no string of multiple chars.
Greetings, Hoerbie
Last edited by hoerbie on Sun Jul 25, 2021 11:14 am, edited 1 time in total.
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Multiply String / Equivalent of Space for strings?

Post by STARGÅTE »

What about this:

Code: Select all

Debug ReplaceString(Space(5), " ", "123 ")
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Multiply String / Equivalent of Space for strings?

Post by Mijikai »

Hope this helps:

Code: Select all

EnableExplicit

Macro StringRepeat(_count_,_string_)
  ReplaceString(Space(_count_)," ",_string_)
EndMacro

Debug StringRepeat(5,"123 ")

End
Edit: Stargate was faster XD
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by hoerbie »

Thank you both, I also had the idea of using ReplaceString(Space()) before asking, but just was looking for the best/fast function.

I've now tested with a multiplies from 10 to 100000, and using ReplaceString(Space()) gets really faster than a for/while loop the more strings have to be multiplied.
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by infratec »

For speed, you can compare it to this:

Code: Select all

Procedure.s StringRepeat(String$, Count.i)
  
  Protected StrLen.i, *Buffer, *Ptr, Result$, i.i
  
  StrLen = StringByteLength(string$)
  If StrLen
    *Buffer = AllocateMemory(StrLen * Count, #PB_Memory_NoClear)
    If *Buffer
      *Ptr = *Buffer
      Count - 1
      CopyMemoryString(@String$, @*Ptr)
      For i = 1 To Count
        CopyMemoryString(@String$)
      Next i
      Result$ = PeekS(*Buffer)
      FreeMemory(*Buffer)
    EndIf
  EndIf
  
  ProcedureReturn Result$
  
EndProcedure
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by STARGÅTE »

@infratec: Your code gives a "Overflow in a dynamically allocated memory block." in line 13.
You have to allocate 2 additional bytes for the null-character:

Code: Select all

*Buffer = AllocateMemory(StrLen * Count + SizeOf(Character), #PB_Memory_NoClear)
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by infratec »

You are right! :oops:

But in my tests it worked :(
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by hoerbie »

Thanks again! As I've thought of another method reading the help for ReplaceString, I made this little test:

Code: Select all

otxt.s = "123 "
ocnt.i = 100000

Procedure.s StringRepeat1(txt.s, cnt.i)
  ProcedureReturn ReplaceString(Space(cnt)," ",txt)
EndProcedure

Procedure.s StringRepeat2(txt.s, cnt.i)
  Protected txl.i,new.s
  txl = Len(txt)
  new = Space(txl*cnt)
  ReplaceString(new,Space(txl),txt,#PB_String_InPlace)
  ProcedureReturn new
EndProcedure

Procedure.s StringRepeat3(String$, Count.i)
  Protected StrLen.i, *Buffer, *Ptr, Result$, i.i
  StrLen = StringByteLength(string$)
  If StrLen
    *Buffer = AllocateMemory(StrLen * Count + SizeOf(Character), #PB_Memory_NoClear)
    If *Buffer
      *Ptr = *Buffer
      Count - 1
      CopyMemoryString(@String$, @*Ptr)
      For i = 1 To Count
        CopyMemoryString(@String$)
      Next i
      Result$ = PeekS(*Buffer)
      FreeMemory(*Buffer)
    EndIf
  EndIf
  ProcedureReturn Result$
EndProcedure

start.i = ElapsedMilliseconds()
ret.s = StringRepeat1(otxt, ocnt)
Debug Str(ElapsedMilliseconds()-start)

start.i = ElapsedMilliseconds()
ret.s = StringRepeat2(otxt, ocnt)
Debug Str(ElapsedMilliseconds()-start)

start.i = ElapsedMilliseconds()
ret.s = StringRepeat3(otxt, ocnt)
Debug Str(ElapsedMilliseconds()-start)
On my Mac and in debug-compile-mode the Memory StringRepeat3 of infratec () seems slower than the ReplaceString methods 1 and 2, and between them sometimes 1 is better and sometimes 2, getting to higher counts 1 seems the fastest.

But all are really faster then a classic loop of adding strings together!
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by hoerbie »

Thanks again! As I've thought of another method reading the help for ReplaceString, I made this little test:

Code: Select all

otxt.s = "123 "
ocnt.i = 100000

Procedure.s StringRepeat1(txt.s, cnt.i)
  ProcedureReturn ReplaceString(Space(cnt)," ",txt)
EndProcedure

Procedure.s StringRepeat2(txt.s, cnt.i)
  Protected txl.i,new.s
  txl = Len(txt)
  new = Space(txl*cnt)
  ReplaceString(new,Space(txl),txt,#PB_String_InPlace)
  ProcedureReturn new
EndProcedure

Procedure.s StringRepeat3(String$, Count.i)
  Protected StrLen.i, *Buffer, *Ptr, Result$, i.i
  StrLen = StringByteLength(string$)
  If StrLen
    *Buffer = AllocateMemory(StrLen * Count + SizeOf(Character), #PB_Memory_NoClear)
    If *Buffer
      *Ptr = *Buffer
      Count - 1
      CopyMemoryString(@String$, @*Ptr)
      For i = 1 To Count
        CopyMemoryString(@String$)
      Next i
      Result$ = PeekS(*Buffer)
      FreeMemory(*Buffer)
    EndIf
  EndIf
  ProcedureReturn Result$
EndProcedure

start.i = ElapsedMilliseconds()
ret.s = StringRepeat1(otxt, ocnt)
Debug Str(ElapsedMilliseconds()-start)

start.i = ElapsedMilliseconds()
ret.s = StringRepeat2(otxt, ocnt)
Debug Str(ElapsedMilliseconds()-start)

start.i = ElapsedMilliseconds()
ret.s = StringRepeat3(otxt, ocnt)
Debug Str(ElapsedMilliseconds()-start)
On my Mac and in debug-compile-mode the Memory StringRepeat3 of infratec () seems slower than the ReplaceString methods 1 and 2, and between them sometimes 1 is better and sometimes 2, getting to higher counts 1 seems the fastest.

But all are really faster then a classic loop of adding strings together!
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by STARGÅTE »

Please disable the debugger when doing speed measurements:

Code: Select all

otxt.s = "123 "
ocnt.i = 100000

Procedure.s StringRepeat1(txt.s, cnt.i)
  ProcedureReturn ReplaceString(Space(cnt)," ",txt)
EndProcedure

Procedure.s StringRepeat2(txt.s, cnt.i)
  Protected txl.i,new.s
  txl = Len(txt)
  new = Space(txl*cnt)
  ReplaceString(new,Space(txl),txt,#PB_String_InPlace)
  ProcedureReturn new
EndProcedure

Procedure.s StringRepeat3(String$, Count.i)
  Protected StrLen.i, *Buffer, *Ptr, Result$, i.i
  StrLen = StringByteLength(string$)
  If StrLen
    *Buffer = AllocateMemory(StrLen * Count + SizeOf(Character), #PB_Memory_NoClear)
    If *Buffer
      *Ptr = *Buffer
      Count - 1
      CopyMemoryString(@String$, @*Ptr)
      For i = 1 To Count
        CopyMemoryString(@String$)
      Next i
      Result$ = PeekS(*Buffer)
      FreeMemory(*Buffer)
    EndIf
  EndIf
  ProcedureReturn Result$
EndProcedure

OpenConsole()
Define I

start.i = ElapsedMilliseconds()
For I = 1 To 100
	ret.s = StringRepeat1(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

start.i = ElapsedMilliseconds()
For I = 1 To 100
	ret.s = StringRepeat2(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

start.i = ElapsedMilliseconds()
For I =1 To 100
	ret.s = StringRepeat3(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

Input()
On my system:
303
401
282
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by infratec »

You can speed it up, by copy the string only once:

Code: Select all

Procedure.s StringRepeat3(String$, Count.i)
  Protected *Buffer, *Ptr, i.i
  *Buffer = AllocateMemory(StringByteLength(string$) * Count + SizeOf(Character), #PB_Memory_NoClear)
  If *Buffer
    *Ptr = *Buffer
    Count - 1
    CopyMemoryString(@String$, @*Ptr)
    For i = 1 To Count
      CopyMemoryString(@String$)
    Next i
  EndIf
  ProcedureReturn PeekS(*Buffer)
EndProcedure
It is still save, since at the end of the procedure, the memeory will be released automatically.

My Resuilts:
271
349
144
With Debugger on, my version needs 3 times longer as the others.
But your created executable at the end has Debugger off :wink:
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by STARGÅTE »

infratec wrote: Sun Jul 25, 2021 9:40 pm You can speed it up, by copy the string only once:
It is still save, since at the end of the procedure, the memeory will be released automatically.
No! Memory, which you allocated by yourself, is not freed at the end of the procedure!
You have to free this memory before leaving the procedure.

An alternative could be, to recycle the memory each time with the Static keyword.
But then once a huge buffer is allocated, it will keep allocated until the program ends.

Code: Select all

Procedure.s StringRepeat4(String$, Count.i)
	Protected StrLen.i, *Ptr, i.i
	Static *Buffer
	StrLen = StringByteLength(string$)
	If StrLen
		If *Buffer = 0 Or StrLen * Count + SizeOf(Character) > MemorySize(*Buffer)
			*Buffer = ReAllocateMemory(*Buffer, StrLen * Count + SizeOf(Character), #PB_Memory_NoClear)
		EndIf
		If *Buffer
			*Ptr = *Buffer
			Count - 1
			CopyMemoryString(@String$, @*Ptr)
			For i = 1 To Count
				CopyMemoryString(@String$)
			Next i
		EndIf
	EndIf
	ProcedureReturn PeekS(*Buffer)
EndProcedure
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by wilbert »

STARGÅTE wrote: Mon Jul 26, 2021 9:46 amAn alternative could be, to recycle the memory each time with the Static keyword.
But then once a huge buffer is allocated, it will keep allocated until the program ends.
Another alternative is to use a local array as a buffer.

Code: Select all

Procedure.s StringRepeat(String$, Count.i)
  Protected Dim Buffer.c(Len(String$) * Count)
  Protected *Ptr = @Buffer(0)
  CopyMemoryString(#Empty$, @*Ptr)
  While Count
    CopyMemoryString(@String$)
    Count - 1
  Wend
  ProcedureReturn PeekS(@Buffer(0))
EndProcedure
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by STARGÅTE »

wilbert wrote: Mon Jul 26, 2021 1:40 pm
STARGÅTE wrote: Mon Jul 26, 2021 9:46 amAn alternative could be, to recycle the memory each time with the Static keyword.
But then once a huge buffer is allocated, it will keep allocated until the program ends.
Another alternative is to use a local array as a buffer.
Very elegant solution :thumbsup: .
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
NicTheQuick
Addict
Addict
Posts: 1224
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: [Solved ]Multiply String / Equivalent of Space for strings?

Post by NicTheQuick »

In case you are interested. These are my results. I inreased the loop count.

Code: Select all

788
807
481
351
377
The difference gets even bigger if you also increase ocnt to ten times its value:

Code: Select all

8488
9142
4978
3512
4192
And this is the code:

Code: Select all

otxt.s = "123 "
ocnt.i = 100000;0

Procedure.s StringRepeat1(txt.s, cnt.i)
	ProcedureReturn ReplaceString(Space(cnt)," ",txt)
EndProcedure

Procedure.s StringRepeat2(txt.s, cnt.i)
	Protected txl.i,new.s
	txl = Len(txt)
	new = Space(txl*cnt)
	ReplaceString(new,Space(txl),txt,#PB_String_InPlace)
	ProcedureReturn new
EndProcedure

;infratec: https://www.purebasic.fr/english/viewtopic.php?p=572925#p572925
Procedure.s StringRepeat3(String$, Count.i)
	Protected *Buffer, *Ptr, i.i
	*Buffer = AllocateMemory(StringByteLength(string$) * Count + SizeOf(Character), #PB_Memory_NoClear)
	If *Buffer
		*Ptr = *Buffer
		Count - 1
		CopyMemoryString(@String$, @*Ptr)
		For i = 1 To Count
			CopyMemoryString(@String$)
		Next i
	EndIf
	ProcedureReturn PeekS(*Buffer)
EndProcedure

;STARGÅTE: https://www.purebasic.fr/english/viewtopic.php?p=572933#p572933
Procedure.s StringRepeat4(String$, Count.i)
	Protected StrLen.i, *Ptr, i.i
	Static *Buffer
	StrLen = StringByteLength(string$)
	If StrLen
		If *Buffer = 0 Or StrLen * Count + SizeOf(Character) > MemorySize(*Buffer)
			*Buffer = ReAllocateMemory(*Buffer, StrLen * Count + SizeOf(Character), #PB_Memory_NoClear)
		EndIf
		If *Buffer
			*Ptr = *Buffer
			Count - 1
			CopyMemoryString(@String$, @*Ptr)
			For i = 1 To Count
				CopyMemoryString(@String$)
			Next i
		EndIf
	EndIf
	ProcedureReturn PeekS(*Buffer)
EndProcedure

;wilbert: https://www.purebasic.fr/english/viewtopic.php?p=572936#p572936
Procedure.s StringRepeat5(String$, Count.i)
	Protected Dim Buffer.c(Len(String$) * Count)
	Protected *Ptr = @Buffer(0)
	CopyMemoryString(#Empty$, @*Ptr)
	While Count
		CopyMemoryString(@String$)
		Count - 1
	Wend
	ProcedureReturn PeekS(@Buffer(0))
EndProcedure

OpenConsole()
Define I

start.i = ElapsedMilliseconds()
For I = 1 To 1000
	ret.s = StringRepeat1(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

start.i = ElapsedMilliseconds()
For I = 1 To 1000
	ret.s = StringRepeat2(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

start.i = ElapsedMilliseconds()
For I =1 To 1000
	ret.s = StringRepeat3(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

start.i = ElapsedMilliseconds()
For I =1 To 1000
	ret.s = StringRepeat4(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

start.i = ElapsedMilliseconds()
For I =1 To 1000
	ret.s = StringRepeat5(otxt, ocnt)
Next
PrintN( Str(ElapsedMilliseconds()-start) )

Input()
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Post Reply