Replicate() : Repeats a string N times

Post your working scripts, libraries and tools
Suresh
Posts: 35
Joined: 03 May 2016, 18:58

Replicate() : Repeats a string N times

03 Jul 2017, 07:47

Code: Select all

Replicate( Str, Count ) { ; By SKAN / CD: 01-July-2017 | goo.gl/U84K7J
Return StrReplace( Format( "{:0" Count "}", "" ), 0, Str )
}

/*
; Older version
Replicate( Str, Count ) { ; By SKAN / CD:12-04-2011
; www.autohotkey.com/community/viewtopic.php?p=435990#435990
 VarSetCapacity( S, Count * ( A_IsUnicode ? 2:1 ), 1 )
 StringReplace, S, S, % SubStr( S,1,1 ), %Str%, All
Return SubStr( S, 1, Count * StrLen(Str) )
}
Most of the time, we wouldn't need a wrapper function, as it is only a single nested call.
Also the above function would fail if SetFormat is used to set Hex mode. See post by feiyue
The function explained:

To create N spaces, for eg. 12 spaces

Code: Select all

Spaces := Format( "{:12}", "" )
MsgBox % "[" Spaces "]"
To create N zeroes instead, prepend a 0 to the count, eg. 012 instead of 12

Code: Select all

Zeroes := Format( "{:012}", "" )
MsgBox % "[" Zeroes "]"
To repeat any other character / string,
We may use StrReplace() with one of the above results

Code: Select all

Str := StrReplace( Format( "{:012}", "" ), 0, "Hello" )
MsgBox % "[" Str "]"  
I chose to use the "zero version" because 0 is shorter than " " or A_Space :P
Don't know if one is faster than the other.
Last edited by Suresh on 05 Jul 2017, 06:29, edited 1 time in total.
User avatar
jNizM
Posts: 2560
Joined: 30 Sep 2013, 01:33
GitHub: jNizM
Contact:

Re: Replicate() : Repeats a string N times

03 Jul 2017, 08:10

Alternative:

Code: Select all

MsgBox % StrRepeat("Hello", 5)

StrRepeat(string, times)
{
    loop % times
        output .= string
    return output
}
[AHK] 1.1.30.03 x64 Unicode | [WIN] 10 Pro (Version 1909) x64 | [GitHub] Profile
Donations are appreciated if I could help you
Helgef
Posts: 4067
Joined: 17 Jul 2016, 01:02
Contact:

Re: Replicate() : Repeats a string N times

03 Jul 2017, 08:59

Hi Suresh ( and jNizM :wave: )
The old version seems slightly faster according to some brief testing.

For your reference, the old version was mentioned here. I came up with this function at that time,

Code: Select all

strRep(str,n){
	if (n<1)
		return
	len:=strlen(str), VarSetCapacity(nStr,n*(A_IsUnicode?2:1)*len), nStr:=str
	Loop, % floor(log(n)/log(2))
		nStr.=nStr	
	return nStr . SubStr(nStr,1,(n-2**floor(log(n)/log(2)))*len)
}
iirc, it can be faster than the old version under some circumstances; short string + very high count (like >2**16).

I prefer jNizMs version for readabillity, the need for speed is probably rare.

Cheers and thanks for sharing.
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Replicate() : Repeats a string N times

03 Jul 2017, 10:06

I'd been wondering for some time if I could do this via RegEx, and I was working on it when you originally posted this function.

Code: Select all

q::
;repeat a string between 1 and 19 times
MsgBox, % RegExReplace(10**1-1,".","a")
MsgBox, % RegExReplace(10**3-1,".","abc ")
MsgBox, % RegExReplace(10**5-1,".","a")
MsgBox, % RegExReplace(10**10-1,".","a")
MsgBox, % RegExReplace(A_Now,".","a") ;14 times
MsgBox, % RegExReplace(10**15-1,".","a")
MsgBox, % RegExReplace(10**19-1,".","a")

;works up to 20 times with extra round brackets
;(we reach the limit of how big a number AHK can handle)
MsgBox, % RegExReplace(10**(20-1),".","a")

MsgBox, % RegExReplace(A_Now A_Now,".","a") ;28 times

MsgBox, % RegExReplace(10**9,".","=====") ;50 equals signs

;turn 14 chars into 10 strings (turn lead into gold)
;if have seen 9 chars already, replace remaining chars with string
MsgBox, % vText := RegExReplace(A_Now,"(?<=^.{9}).*|.","a")
MsgBox, % StrLen(vText)
MsgBox, % vText := RegExReplace(A_Now,"(?<=^.{9}).*|.","abc ")
MsgBox, % StrLen(vText)

;==============================

;using the fact that RegExReplace can operate on blanks (e.g. padding each character):

vText := "abcdefghij"
MsgBox, % RegExReplace(vText,"","_") ;left/middle/right
MsgBox, % RegExReplace(vText,"(?<=.)(?=.)","_") ;middle
MsgBox, % RegExReplace(vText,"(?=.)","_") ;left/middle
MsgBox, % RegExReplace(vText,"(?<=.)","_") ;middle/right

MsgBox, % vText := RegExReplace(A_Now,"(?<=.{8}).*|.","a")
MsgBox, % StrLen(vText)
MsgBox, % vText := RegExReplace(A_Now,"(?<=.{8}).*|.","abc ")
MsgBox, % StrLen(vText)

;turn 14 into 15
MsgBox, % vText := RegExReplace(A_Now,".|","a")
MsgBox, % StrLen(vText)

;turn 14 into 29
MsgBox, % vText := RegExReplace(A_Now,"|.","a")
MsgBox, % StrLen(vText)

;turn 14 into 28
MsgBox, % vText := RegExReplace(A_Now,"(?=.)|.","a")
MsgBox, % StrLen(vText)
MsgBox, % vText := RegExReplace(A_Now,"(?<=.)|.","a")
MsgBox, % StrLen(vText)

;turn 14 into 27
MsgBox, % vText := RegExReplace(A_Now,"(?<=.)(?=.)|.","a")
MsgBox, % StrLen(vText)
return
I also collected some notes on repeating a string here:
jeeswg's strings tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=32985

I might do some benchmark tests at some point. Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
feiyue
Posts: 196
Joined: 08 Aug 2014, 04:08

Re: Replicate() : Repeats a string N times

04 Jul 2017, 07:50

I did this before:

Code: Select all

str:="abcd", n:=0xFF  ;---> n=255
MsgBox, % StrReplace(StrGet(VarSetCapacity(@,n,Asc("x"))*0+&@,n,0),"x",str)
I think Format() needs to consider the number of times sixteen decimal.

Code: Select all

str:="abcd", n:=0xFF  ;---> n=255
MsgBox, % StrReplace(Format("{:0" . Format("{:d}",n) . "}",""),0,str)
Suresh
Posts: 35
Joined: 03 May 2016, 18:58

Re: Replicate() : Repeats a string N times

05 Jul 2017, 06:17

feiyue wrote:I did this before:

Code: Select all

str:="abcd", n:=0xFF  ;---> n=255
MsgBox, % StrReplace(StrGet(VarSetCapacity(@,n,Asc("x"))*0+&@,n,0),"x",str)
Nice! :)
feiyue wrote: I think Format() needs to consider the number of times sixteen decimal.

Code: Select all

str:="abcd", n:=0xFF  ;---> n=255
MsgBox, % StrReplace(Format("{:0" . Format("{:d}",n) . "}",""),0,str)
Oh! Thank you..!
Suresh
Posts: 35
Joined: 03 May 2016, 18:58

Re: Replicate() : Repeats a string N times

05 Jul 2017, 06:22

Helgef wrote:Hi Suresh ( and jNizM :wave: )
Greetings Helgef :)
The old version seems slightly faster according to some brief testing.
I know, hence I've included it.
iirc, it can be faster than the old version under some circumstances; short string + very high count (like >2**16).
I wonder under what circumstance one would need this kind of large text.
]I prefer jNizMs version for readabillity, the need for speed is probably rare.
That piece of code has been in existence for about a decade, AFAIK. :)
It will throw an error with #warn enabled


Here is one interesting use for replicating spaces

To Initialize static var with text capacity of 256 TCHARS
the regular code would be

Code: Select all

Func() {
Static Var
IF not VarSetCapacity( Var )
 VarSetCapacity( Var,  256 * (A_IsUnicode ? 2 : 1 ) )

DllCall( ..., "Str",Var, .. )
Return Var
}
while it can be simplified to

Code: Select all

Func() {
Static Var := Format( "{:256}", "")
DllCall( ..., "Str",Var, .. )
Return Var
}
Suresh
Posts: 35
Joined: 03 May 2016, 18:58

Re: Replicate() : Repeats a string N times

05 Jul 2017, 06:23

jeeswg wrote:I could do this via RegEx,
Interesting. Thanks for sharing!
Helgef
Posts: 4067
Joined: 17 Jul 2016, 01:02
Contact:

Re: Replicate() : Repeats a string N times

05 Jul 2017, 07:26

Suresh wrote: I wonder under what circumstance one would need this kind of large text.
When testing replicate-string functions ofc! :lol:
Suresh wrote:

Code: Select all

Func() {
Static Var
IF not VarSetCapacity( Var )
 VarSetCapacity( Var,  256 * (A_IsUnicode ? 2 : 1 ) )

DllCall( ..., "Str",Var, .. )
Return Var
}
while it can be simplified to

Code: Select all

Func() {
Static Var := Format( "{:256}", "")
DllCall( ..., "Str",Var, .. )
Return Var
}
That is nice, although, you don't really need the if not ... for the varsetcapacity version, eg,

Code: Select all

Func() {
	static Var, a := VarSetCapacity( Var,  256 * (A_IsUnicode ? 2 : 1 ) ) 
	; ...
}

@jeeswg, regarding regex. Interesting approach, I gave it a try, I don't think it is very good, only for unicode I guess,

Code: Select all

regExRep(str,n){
	varsetcapacity(hay,(l:=strlen(str))*n*2,1)
	return regexreplace(strget(&hay,l*n), "\x{101}{" . l . "}",str)
}

@feiyue, that is very compact 8-)

Cheers.
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Replicate() : Repeats a string N times

05 Jul 2017, 07:36

The idea behind the RegExReplace approach, was to have very readable, very short one-liners for certain limited situations (not a general approach), that only used one existing function, not a custom function, that directly returned a value for use in an expression. Also a bit of time travel, for the times when AHK didn't have Format or StrReplace.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Replicate() : Repeats a string N times

12 Jul 2017, 01:44

I moved some content out to a new post:
string hacks - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=34390

This is the best I have for 'StrRept', but none of them is pretty:

Code: Select all

q::
;hardcoded:
MsgBox, % StrReplace(Format("{:010}",0),0,"=")
MsgBox, % StrReplace(Format("{:10}","")," ","=")

;[looks like the best method but it's still quite long]
;variable:
vNum := 50
MsgBox, % StrReplace(Format("{:0" vNum "}",0),0,"=")
MsgBox, % StrReplace(Format("{:" vNum "}","")," ","=")

;to take care of hex numbers (as feiyue mentioned):
vNum := 0x32 ;50
MsgBox, % StrReplace(Format("{:0" vNum+0 "}",0),0,"=")
MsgBox, % StrReplace(Format("{:" vNum+0 "}","")," ","=")

;for numbers between 1 and 18
MsgBox, % StrReplace(10**1-1,9,"a") ;(1 char)
MsgBox, % StrReplace(10**10-1,9,"a") ;(10 chars)
MsgBox, % StrReplace(10**18-1,9,"a") ;(18 chars)

;VarSetCapacity (division by 0 to give a blank string)
;note: running these 3 lines on AHK ANSI, the 3rd line gives an unexpected result
MsgBox, % VarSetCapacity(var,10,1)/0 StrReplace(var,Chr(1),"a")
MsgBox, % VarSetCapacity(var,10*2,1)/0 StrReplace(var,Chr(257),"a")
MsgBox, % VarSetCapacity(var,10*(!!A_IsUnicode+1),1)/0 StrReplace(var,Chr(A_IsUnicode?257:1),"a")

;prepare a big string and apply SubStr
var := StrReplace(Format("{:01000}",0),0,"=")
MsgBox, % SubStr(var,1,50)

;a theoretical StrRept function
;MsgBox, % StrRept(vText, vNum)
;MsgBox, % StrRept(vText, vNum)
return
Last edited by jeeswg on 12 Jul 2017, 15:02, edited 6 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4067
Joined: 17 Jul 2016, 01:02
Contact:

Re: Replicate() : Repeats a string N times

12 Jul 2017, 02:46

@ jeeswg
I never though of using strsplit to count occurances, nifty, but I'd imagine StrReplace(...OutputVarCount) would perform better (did you benchmark?), and to me, it is the more natural choise.
Instead of Object(...).HasKey, maybe you find this more readable,

Code: Select all

var:="hello"
Msgbox, % {hello:1,jeeswg:1}.haskey(var)
Ofc, for the object() method you can do object(var*).haskey(...).

I think we are sort of off-topic here.
@ Topic (sort of).
I think I improved my earlier version of a replicate function,

Code: Select all

strRep(str,n,ByRef out){
	static WA:=A_IsUnicode?2:1
	len:=strlen(str), VarSetCapacity(out,n*WA*(len+1)), out:=str
	Loop, % flog:=floor(log(n)/log(2))
		out.=out
	out.=strget(&out,(n-2**flog)*len)
	return
}
I also think the old version (see the first post by Suresh) might be sligthly improved by this,

Code: Select all

Replicate( Str, Count, byref out ) { ; By SKAN / CD:12-04-2011
; www.autohotkey.com/community/viewtopic.php?p=435990#435990
 VarSetCapacity( S, Count * ( A_IsUnicode ? 2:1 ), 1 )
 StringReplace, out, S, % SubStr( S,1,1 ), %Str%, All
Return
}
Cheers.
Suresh
Posts: 35
Joined: 03 May 2016, 18:58

Re: Replicate() : Repeats a string N times

17 Jul 2017, 04:38

Helgef wrote:I also think the old version (see the first post by Suresh) might be sligthly improved by this,

Code: Select all

Replicate( "Hello", 2, Out )
MsgBox % Out
The minimum capacity for VasrSetCapacity() is 3 :)
Helgef
Posts: 4067
Joined: 17 Jul 2016, 01:02
Contact:

Re: Replicate() : Repeats a string N times

17 Jul 2017, 05:39

Hello Suresh.
I didn't know, it's weird I didn't test for such a simple example :oops: Quick reading the manual on VarSetCapacity I didn't see it mentioned. It seems to be 3 for ANSI and 6 for unicode :think:. Anyways, I try to correct my mistake,

Code: Select all

Replicate( Str, Count, byref out ) {
	static WA:=A_IsUnicode ? 2:1
	static replaceChar:= A_IsUnicode ? chr(0x101):chr(1)
	VarSetCapacity( S, Count * WA, 1 )
	out:=rtrim(StrReplace(S,replaceChar,str,,count),replaceChar)
}
Suresh
Posts: 35
Joined: 03 May 2016, 18:58

Re: Replicate() : Repeats a string N times

17 Jul 2017, 05:56

Helgef wrote:It seems to be 3 for ANSI and 6 for unicode
True. VarSetCapacity() works differently in ANSI and UNICODE.
Also, it differs with existing vs new variable

Try this in ANSI and UNICODE:

Code: Select all

Loop 1000
{
 Cap := VarSetCapacity( Var, A_Index, 0 )
 If ( Cap <> A_Index )
  MsgBox % A_Index " = " Cap 
}
Helgef
Posts: 4067
Joined: 17 Jul 2016, 01:02
Contact:

Re: Replicate() : Repeats a string N times

17 Jul 2017, 06:31

Good observations, thanks :thumbup:
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Replicate() : Repeats a string N times

11 Sep 2017, 05:33

I have a similar function, and it gave me a surprise when the number was 0 or below, so I fixed my function as follows:

Code: Select all

JEE_StrRept(vText, vNum)
{
	if (vNum <= 0)
		return
	return StrReplace(Format("{:" vNum "}","")," ",vText)
	;return StrReplace(Format("{:0" vNum "}",0),0,vText)
}
This example illustrates the problem:

Code: Select all

q:: ;test Format, used to repeat a string
vNum := 4
Loop, 7
{
	vNum--
	vOutput1 := "[" Format("{:" vNum "}","") "]"
	vOutput2 := "[" Format("{:0" vNum "}",0) "]"
	MsgBox, % vNum "`r`n" vOutput1 "`r`n" vOutput2
}
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Scripts and Functions”

Who is online

Users browsing this forum: Google [Bot], swagfag and 47 guests