Fast and robust :GetSubstringIndex :ReplaceString :CreateRandomStringPS functions !
Posted: 29 Apr 2024 05:48
See latest version at viewtopic.php?p=69907#p69907
------------------------------------
This was a lot of work !
If it wasn't so late, I think this directly leads to a fast and robust :split function as well !
This substring uses the similar mechanism as the :len function
That is, split the input variable in two, then search which first half has the match
And so one until it is narrowed down to twice the lenght of the search term
Then I have a "sliding window" find the exact location
It gets slower with longer strings, both input and search string
could not test past 4079 characters
see below for the other functions, but this one is the star
(does not actually have an output value yet, only %errorlevel% therefore it does not take multiple argument series as stated above)
Simple replace function, similar to a Mid VB function, you have to specify then len you want to take out of the input string
I just noticed I swapped the position of input and output, this will get harmonized later, but I am too tried right now. Spent all weekend and all night on this !
replace string currently fails at 4079 characters of input string
There is a demo function that comes with :GetSubstringIndex
I needed it to generate large and varied test strings
It is fast, but uses the cursed scripting language
I wonder if it works with powershell 1.0
Here is the test file, it is ready to go , will execute GetSubstringIndex-DEMO function automatically
It will generate various string arrays
Then apply a "TEST" pattern to each string, at a different index
Then it will read this index and display it.
You know it works when index and result are the same value
Each test is 4 steps
string creation
array population (here result will always read zero)
then SubstringIndex runs with expanded variables
then it runs with byref variable
If you want to see what that looks like
see this video https://youtu.be/jgFjzF_ODGE
------------------------------------
This was a lot of work !
If it wasn't so late, I think this directly leads to a fast and robust :split function as well !
This substring uses the similar mechanism as the :len function
That is, split the input variable in two, then search which first half has the match
And so one until it is narrowed down to twice the lenght of the search term
Then I have a "sliding window" find the exact location
It gets slower with longer strings, both input and search string
could not test past 4079 characters
see below for the other functions, but this one is the star
Code: Select all
::Usage Call :GetSubstringIndex InputString SearchString StartIndex ... InputStringN SearchStringN StartIndexN
Code: Select all
::Usage Call :GetSubstringIndex InputString SearchString StartIndex ... InputStringN SearchStringN StartIndexN
:GetSubstringIndex
set "_GetSubstringIndex_prefix=_GSSI"
set /a _GSSI_StartIndex=%~3
if defined %~1 call set _GSSI_Input=%%%~1%%
if not defined _GSSI_Input set _GSSI_Input=%~1
if defined %~2 call set _GSSI_Search=%%%~2%%
if not defined _GSSI_Search set _GSSI_Search=%~2
Call :len _GSSI_Input _GSSI_Input_len
Call :len _GSSI_Search _GSSI_Search_len
set /a _GSSI_Input_len-=1
set /a _GSSI_min_search=(%_GSSI_Search_len%*2)
setlocal enabledelayedexpansion
:GetSubstringIndex-loop
set /a _GSSI_HalfInputLen=%_GSSI_Search_len%+(%_GSSI_Input_len%/2)
if %_GSSI_HalfInputLen% LEQ %_GSSI_min_search% ( set /a _GSSI_max_search=%_GSSI_StartIndex%+%_GSSI_min_search%+1 & GoTo :GetSubstringIndex-loop2 )
set /a _GSSI_Middle=%_GSSI_StartIndex%+%_GSSI_HalfInputLen%
set /a _GSSI_HalfInputLenPLUSSearchLenMINUSOne=%_GSSI_HalfInputLen%+%_GSSI_Search_len%-1
set "_GSSI_FirstHalf=!_GSSI_Input:~%_GSSI_StartIndex%,%_GSSI_HalfInputLenPLUSSearchLenMINUSOne%!
set "_GSSI_SecondHalf=!_GSSI_Input:~%_GSSI_Middle%,%_GSSI_HalfInputLen%!
set "_GSSI_FirstResult=!_GSSI_FirstHalf:%_GSSI_Search%=!"
set "_GSSI_SecondResult=!_GSSI_SecondHalf:%_GSSI_Search%=!"
if !_GSSI_FirstHalf! NEQ !_GSSI_FirstResult! ( set /a _GSSI_Input_len=%_GSSI_HalfInputLen% & GoTo :GetSubstringIndex-loop )
if !_GSSI_SecondHalf! NEQ !_GSSI_SecondResult! ( set /a _GSSI_StartIndex=%_GSSI_Middle% & GoTo :GetSubstringIndex-loop )
set /a _GSSI_StartIndex=-1 & GoTo :GetSubstringIndex-end
:GetSubstringIndex-loop2
set "_GSSI_FinalSearch=!_GSSI_Input:~%_GSSI_StartIndex%,%_GSSI_Search_len%!
set /a "_GSSI_debug_remain_len=%_GSSI_max_search%-%_GSSI_StartIndex%"+1
if !_GSSI_FinalSearch! EQU !_GSSI_Search! GoTo :GetSubstringIndex-end
set /a _GSSI_StartIndex+=1
if %_GSSI_StartIndex% LEQ %_GSSI_max_search% GoTo :GetSubstringIndex-loop2
set /a _GSSI_StartIndex=-1
:GetSubstringIndex-end
endlocal & Call :ClearVariablesByPrefix %_GetSubstringIndex_prefix% _GetSubstringIndex_prefix & exit /b %_GSSI_StartIndex%
I just noticed I swapped the position of input and output, this will get harmonized later, but I am too tried right now. Spent all weekend and all night on this !
replace string currently fails at 4079 characters of input string
Code: Select all
::Usage Call :ReplaceString SourceString ReplaceString OutputString StartIndex len
Code: Select all
::Usage Call :ReplaceString SourceString ReplaceString OutputString StartIndex len
:ReplaceString
set "_ReplaceString_prefix=_RS"
setlocal enabledelayedexpansion
if defined %~1 call set _RS_Input=!%~1!
if not defined _RS_Input set _RS_Input=%~1
if defined %~2 call set _RS_Replace=!%~2!
if not defined _RS_Replace set _RS_Replace=%~2
if defined %~3 call set _RS_Output=!%~3!
if not defined _RS_Output set _RS_Output=%~3
set /a _RS_StartIndex=%~4
set /a _RS_len=%~5
set /a _RS_RemainIndex=%_RS_StartIndex%+%_RS_len%
set _RS_Intermetiate=!_RS_Input:~,%_RS_StartIndex%!!_RS_Replace!!_RS_Input:~%_RS_RemainIndex%!
for /f "tokens=1* delims=" %%a in ('echo.!_RS_Intermetiate!') do endlocal & set %_RS_Output%=%%a
Call :ClearVariablesByPrefix %_ReplaceString_prefix% _ReplaceString_prefix & GoTo :EOF
I needed it to generate large and varied test strings
It is fast, but uses the cursed scripting language
I wonder if it works with powershell 1.0
Code: Select all
Call :CreateRandomStringPS [NONUMBERS] [NOUPPERCASE] [NOUPPERCASE] [SPACE] [PUNCTUATION] [NOPOISON] [POISON] [EXTENDED] [CONTROL] [RESET] StringLenght1 OutputVariable1 StringLenght2 OutputVariable2 ... StringLenghtN OutputVariableN
Code: Select all
::Usage Call :CreateRandomStringPS [NONUMBERS] [NOUPPERCASE] [NOUPPERCASE] [SPACE] [PUNCTUATION] [NOPOISON] [POISON] [EXTENDED] [CONTROL] [RESET] StringLenght1 OutputVariable1 StringLenght2 OutputVariable2 ... StringLenghtN OutputVariableN
::You can your CLEAR in front of the switches to clear them
:CreateRandomStringPS
set "_CreateRandomStringPS_prefix=_CRS"
set "_CRS_SetList=NONUMBERS NOUPPERCASE NOLOWERCASE SPACE PUNCTUATION NOPOISON EXTENDED CONTROL NOPOISON POISON"
if "[%~1]" EQU "[RESET]" ( for %%a in (%_CRS_SetList%) do ( set "_CRS_%%a=" ) & shift & GoTo :CreateRandomStringPS )
for %%a in (%_CRS_SetList%) do if "[%%a]" EQU "[%~1]" ( set "_CRS_%%a=true" & shift & GoTo :CreateRandomStringPS ) else ( if "[CLEAR%%a]" EQU "[CLEAR%~1]" ( set "_CRS_%%a=" & shift & GoTo :CreateRandomStringPS ) )
set /a _CRS_Len=%~1
set "_CRS_Output=%~2"
set "_CRS_CurrentSet="
if not defined _CRS_NONUMBERS set "_CRS_CurrentSet=%_CRS_CurrentSet% + 48..57"
if not defined _CRS_NOUPPERCASE set "_CRS_CurrentSet=%_CRS_CurrentSet% + 65..90"
if not defined _CRS_NOLOWERCASE set "_CRS_CurrentSet=%_CRS_CurrentSet% + 97..122"
if defined _CRS_SPACE set "_CRS_CurrentSet=%_CRS_CurrentSet% + 32"
if defined _CRS_PUNCTUATION if not defined _CRS_NOPOISON ( set "_CRS_CurrentSet=%_CRS_CurrentSet% + 33..47 + 58..64 + 91..96 + 123..126" ) else ( set "_CRS_CurrentSet=%_CRS_CurrentSet%+ 35..36 + 39 + 42..47 + 58..59 + 61 + 63..64 + 91..93 + 95..96 + 123 + 125 + 126" )
if defined _CRS_POISON if not defined _CRS_PUNCTUATION set "_CRS_CurrentSet=%_CRS_CurrentSet% + 33 + 34 + 37 + 38 + 40 + 41 + 60 + 62 + 94 + 124"
if defined _CRS_EXTENDED set "_CRS_CurrentSet=%_CRS_CurrentSet% + 128..255"
if defined _CRS_CONTROL set "_CRS_CurrentSet=%_CRS_CurrentSet% + 0..31 + 127"
for /f "tokens=1* delims=" %%a in ('powershell -command "-join (1..%_CRS_Len% | ForEach-Object { %_CRS_CurrentSet:~3% | Get-Random } | ForEach-Object { [char]$_ })" 2^>nul') do set %_CRS_Output%=%%a
if "[%~3]" NEQ "[]" if "[%~4]" NEQ "[]" ( shift & shift & GoTo :CreateRandomStringPS )
Call :ClearVariablesByPrefix %_CreateRandomStringPS_prefix% _CreateRandomStringPS_prefix & GoTo :EOF
Here is the test file, it is ready to go , will execute GetSubstringIndex-DEMO function automatically
It will generate various string arrays
Then apply a "TEST" pattern to each string, at a different index
Then it will read this index and display it.
You know it works when index and result are the same value
Each test is 4 steps
string creation
array population (here result will always read zero)
then SubstringIndex runs with expanded variables
then it runs with byref variable
If you want to see what that looks like
see this video https://youtu.be/jgFjzF_ODGE