I need to loop through every line in a text file, and still have the empty lines. The common solution I could find is as follows:
for /f "tokens=1* delims=:" %%i in ('findstr /n "^" "%textfile%"') do ( call :IsLabel _return "%%b" )
This will prefix every line with the line number and colon character, example:
21:R 30 35 89jtj3 G)(#G_ 23ty9ug9dg
The problem is that, if the first character is a colon, (:
), it will get dropped due to delims=:
in the for
loop.
So 30::mylabel
becomes just mylabel
instead of :mylabel
.
Is there a way to make findstr
use another character than colon perhaps?
-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-
Since, no matter what, there will be extra characters to remove, I think that the best course of action is to dump the entire file into an array. Then remove the characters from that array.
So what is needed is a "FileToArray" function that takes a filename and array name and populates the array with the content of that file.
My first attempt, based on @Mofi's answer is as follows
::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
setlocal enabledelayedexpansion
set _FTA_localscope=true
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
set _FTA_buffer=%%I
set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer:*:=!
set /a "_FTA_ubound+=1"
)
for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do endlocal & set %%a
if defined _FTA_localscope endlocal
GoTo :EOF
(execution time, 4 second for 1600 lines on slow computer)
This version almost works, however, as @Mofi points out, is that doing this you lose all the ! characters on the line
set _FTA_buffer=%%I
@Mofi then suggests to perform the removal of the line numbers in a setlocal inside the for loop instead. In the example, this works because the data is echo'd out directly and doesn't need to be kept in a variable outside of setlocal
So, I tried this variant with a setlocal / endlocal inside the for loop
::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
set _FTA_buffer=%%I
setlocal enabledelayedexpansion
set _FTA_buffer=!_FTA_buffer:*:=!
echo set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer!
set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer!
set /a "_FTA_ubound+=1"
set _FTA_ubound=!_FTA_ubound!
endlocal & set /a "_FTA_ubound=%_FTA_ubound%" & set %_FTA_Output%[%_FTA_ubound%]=%_FTA_buffer%
)
GoTo :EOF
(same execution time)
Unfortunately, this does not set the outputarray values.
If I echo(%_FTA_buffer% , I can see the values get passed substituted properly but can't get them out of that endlocal
I tried other permutations
::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
setlocal enabledelayedexpansion
set _FTA_localscope=true
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
setlocal disabledelayedexpansion
set _FTA_buffer=%%I
setlocal enabledelayedexpansion
set /a "_FTA_ubound+=1"
endlocal & endlocal & set /a "_FTA_ubound=!_FTA_ubound!" & set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer:*:=!
)
for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do endlocal & set %%a
if defined _FTA_localscope endlocal
GoTo :EOF
(this does not set any values)
::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
setlocal enabledelayedexpansion
set _FTA_localscope=true
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
setlocal disabledelayedexpansion
set _FTA_buffer=%%I
endlocal & set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer:*:=!
set /a "_FTA_ubound+=1"
)
for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do endlocal & set %%a
if defined _FTA_localscope endlocal
GoTo :EOF
This sets every array item to *:=
example
LinesArray[999]=*:=
To launch this function, I use this test function
:SimpleFileToArray-DEMO
Call :ClearVariablesByPrefix _FTA LinesArray
echo start SimpleFileToArray %time%
Call :SimpleFileToArray LinesArray batchsample.bat
echo end SimpleFileToArray %time%
GoTo :EOF
Mmm... I read several times your question (and your answer) and I still don't understand what really is your goal... So I'm going to risk posting an answer that can be entirely useless...
This is my code:
@echo off
findstr /N "^:[^:]" test.txt | findstr /N "^"
echo/
echo Empty lines:
echo/
findstr /N "^$" test.txt
The data I used as input is your code above, in your answer.
And this is the output:
1:1::FindAllLabels-DEMO
2:14::FindAllLabelsFromFileLineArray
3:24::FindAllLabelsFromFileLineArray-loop
4:39::FindAllLabelsFromFileLineArray-skip
5:46::TrimBeforeChar
6:51::IsLabel
7:58::IsLabel-loop
8:68::IsLabel-end
9:75::GetLabel
10:83::GetLabel-loop
11:92::GetLabel-end
12:99::FileToArray
13:108::FileToArray-arguments
14:129::ClearVariablesByPrefix
Empty lines:
2:
4:
6:
8:
10:
12:
44:
49:
73:
97:
127:
In the first group the first number is a sequential number; the second one is the original line number, and the third field are the :labels
in your Batch file...
So?
EDIT 2023/08/20: New code added as per comments
This code lists all functions and allows to extract any function. Check the comments in the code.
@echo off
setlocal EnableDelayedExpansion
rem Get empty lines (plus/minus one)
for /F "delims=:" %%a in ('findstr /N "^$" test.txt') do (
set /A "p=%%a+1, n=%%a-1"
set /A "prev[!p!]=1, next[!n!]=1"
)
cls
echo Defined functions:
rem A "function" starts in a :label preceded by an empty line
for /F "tokens=1* delims=:" %%a in ('findstr /N "^:[^:]" test.txt') do (
if defined prev[%%a] echo %%a- :%%b
)
:nextFunction
echo/
echo/
set /P "start=Enter line number of function to list: "
if errorlevel 1 goto :EOF
if not defined prev[%start%] echo ERROR & goto nextFunction
echo/
rem A "function" ends in a "goto :EOF" or "exit /B" line followed by an empty line
for /F "delims=:" %%a in ('findstr /N /I /C:"goto :EOF" /C:"exit /B" test.txt') do (
if %%a gtr %start% if defined next[%%a] (
set "last=%%a"
goto break
)
)
:break
setlocal DisableDelayedExpansion
set /A start-=1
for /F "skip=%start% delims=" %%a in ('findstr /N "^" test.txt') do (
set "line=%%a"
setlocal EnableDelayedExpansion
echo(!line:*:=!
endlocal
for /F "delims=:" %%n in ("%%a") do (
if %%n == %last% goto :break
)
)
:break
goto nextFunction
NOTES:
This is the output using your large batch file as input (and with previous notes):
Defined functions:
232- :skipsection
262- :CommandToArray OutputArray CommandString
285- :HexToDecimal-DEMO
297- :HexToDecimal OutputVariable Input
305- :DecimalToHex OutputVariable Input
313- :rtrim OutputVariable Input
329- :lenByVal OutputResult %VariableName%
363- :IsNumeric Input optional Output
384- :ClearVariablesByPrefix myPrefix
389- :LoopThroughArray ArrayName optional lbound=x optional ubound=x "command 1"
426- :SampleLoopStructure
493- :GetPowerSchemeContents
504- :GetPowerSettings
685- :ListPowerSubgroups ArrayName optional lbound=x optional ubound=x "command 1"
702- :ListPowerSettings optional NOPREFIX PowerScheme(index, name or guid) PowerSubgroup(index, name or guid) optional OutputArray
720- :ListPowerSettingsInAllSubgroups PowerScheme(index, name or guid) optional OutputArray
732- :ListPowerSettingsInAllSchemes optional OutputArray
746- :ListAllPowerSubgroups optional OutputArray
762- :GetPowerSchemeIndex Powerscheme(index, name or guid) optional OutputIndex
784- :GetPowerSubgroupIndex Powerscheme(index, name or guid) PowerSubgroup(index, name or guid) optional OutputIndex
806- :GetPowerSettingIndex Powerscheme(index, name or guid) PowerSubgroup(index, name or guid) PowerSetting(index, name or guid) optional OutputIndex
826- :GetPowerSchemeGuid OutputGuid InputPowerSchemeName
831- :GetPowerSchemeName OutputName InputPowerSchemeGuid
836- :GetPowerSubgroupGuid OutputGuid InputPowerSubgroupName
841- :GetPowerSubgroupName OutputName InputPowerSubgroupGuid
846- :GetPowerSettingIndex OutputGuid InputPowerSettingName
851- :GetPowerSettingIndex OutputName InputPowerSettingGuid
857- :GetDefaultSchemeName OutputIndex OutputGuid OutputName
867- :GetPowerID [schemename.[subgroupname.[settingname]
888- :SetSettingACValue powercfg /CHANGE
895- :ShowAllPowercfgSettings
946- :DisableHibernation
954- :ShowWhatIsPreventingSleep
963- :EnableWakeByDevice
967- :GetDevicePowerProperties
969- :GetWakeProgrammableDevices
985- :GetSleepStates
1050- :GetSupportedSleepStates optional OutputArray
1061- :GetUnsupportedSleepStates optional OutputArray
1072- :GetUnsupportedSleepStatesWithReason optional OutputArray
1083- :SetMonitorTimeout optional AC-timeout-minutes optional DC-timeout-minutes
1088- :SetDiskTimeout optional AC-timeout-minutes optional DC-timeout-minutes
1103- :GetPowerSchemeElements ElementString PowerSetting PowerSubgroup PowerScheme
1107- :GetPowerSchemeValues Powerscheme(index, name or guid) OutputArray
1125- :ReadInputArgument OutputVariable Input
1131- :MakeStringLenght-DEMO
1187- :MakeStringLenght optional RIGHTALIGNED OutputString MaxLenght Input
1207- :GetConsoleDimensions-DEMO
1260- :SetConsoleDimensions-DEMO
1328- :GetConsoleWidth optional OutputVariable
1338- :GetConsoleHeight optional OutputVariable
1348- :GetConsoleBufferWidth optional OutputVariable
1358- :GetConsoleBufferHeight optional OutputVariable
1365- :GetConsoleDimensions WidthOutput HeightOutput
1377- :GetConsoleBufferDimensions WidthOutput HeightOutput
1390- :GetCodePage optional OutputVariable
1400- :GetKeyboardDelay optional OutputVariable
1410- :GetKeyboardRate optional OutputVariable
1417- :GetConsoleProperties
1441- :SetConsoleDimensions width height
1445- :StringRepeat-DEMO
1489- :StringRepeat OutputVariable Count Input
1507- :sqrt Input optional OutputVariable
1520- :SquareRoot-DEMO
1542- :GetCirclePoint Xvalue Radius optional Output
1552- :GetCirclePoint-DEMO