Page 1 of 1

Replicate() : Repeats a string N times

Posted: 03 Jul 2017, 07:47
by Suresh

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.

Re: Replicate() : Repeats a string N times

Posted: 03 Jul 2017, 08:10
by jNizM
Alternative:

Code: Select all

MsgBox % StrRepeat("Hello", 5)

StrRepeat(string, times)
{
    loop % times
        output .= string
    return output
}

Re: Replicate() : Repeats a string N times

Posted: 03 Jul 2017, 08:59
by Helgef
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.

Re: Replicate() : Repeats a string N times

Posted: 03 Jul 2017, 10:06
by jeeswg
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.

Re: Replicate() : Repeats a string N times

Posted: 04 Jul 2017, 07:50
by feiyue
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)

Re: Replicate() : Repeats a string N times

Posted: 05 Jul 2017, 06:17
by Suresh
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..!

Re: Replicate() : Repeats a string N times

Posted: 05 Jul 2017, 06:22
by Suresh
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
}

Re: Replicate() : Repeats a string N times

Posted: 05 Jul 2017, 06:23
by Suresh
jeeswg wrote:I could do this via RegEx,
Interesting. Thanks for sharing!

Re: Replicate() : Repeats a string N times

Posted: 05 Jul 2017, 07:26
by Helgef
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.

Re: Replicate() : Repeats a string N times

Posted: 05 Jul 2017, 07:36
by jeeswg
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.

Re: Replicate() : Repeats a string N times

Posted: 12 Jul 2017, 01:44
by jeeswg
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

Re: Replicate() : Repeats a string N times

Posted: 12 Jul 2017, 02:46
by Helgef
@ 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.

Re: Replicate() : Repeats a string N times

Posted: 17 Jul 2017, 04:38
by Suresh
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 :)

Re: Replicate() : Repeats a string N times

Posted: 17 Jul 2017, 05:39
by Helgef
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)
}

Re: Replicate() : Repeats a string N times

Posted: 17 Jul 2017, 05:56
by Suresh
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 
}

Re: Replicate() : Repeats a string N times

Posted: 17 Jul 2017, 06:31
by Helgef
Good observations, thanks :thumbup:

Re: Replicate() : Repeats a string N times

Posted: 11 Sep 2017, 05:33
by jeeswg
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

Re: Replicate() : Repeats a string N times

Posted: 12 Dec 2023, 03:24
by xMaxrayx
@Suresh

thank you, it works in ahkv2.