How to output a range of lines from a text file using findstr

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
shodan
Posts: 89
Joined: 01 May 2023 01:49

How to output a range of lines from a text file using findstr

#1 Post by shodan » 09 Apr 2024 22:54

You might have seen a method using findstr, where you want to output chosen lines of text from a text file.

Code: Select all

type "myfile.txt" | %SystemRoot%\System32\findstr /N /R /C:".*" | %SystemRoot%\System32\findstr /B /C:"5785:" /C:"5786:" /C:"5787:" /C:"5788:"
And that certainly works but it gets unwieldly if you want to include thousands of lines.

So I asked chatgpt if I could use regex to select a range of lines

First it tried to do every group of 10 lines

Code: Select all

type myfile.txt | %SystemRoot%\System32\findstr /N "^" | findstr /R "^453[3-9]: ^454[0-9]: ^455[0-9]: ^456[0-9]: ^457[0-9]: ^458[0-9]: ^459[0-9]: ^460[0-9]: ^461[0-9]: ^462[0-9]: ^463[0-9]: ^464[0-9]: ^465[0-9]: ^466[0-9]: ^467[0-9]: ^468[0-9]: ^469[0-9]: ^470[0-9]: ^471[0-9]: ^472[0-9]: ^473[0-9]: ^474[0-9]: ^475[0-9]: ^476[0-9]: ^477[0-9]: ^478[0-9]: ^479[0-9]: ^480[0-9]: ^481[0-9]: ^482[0-9]: ^483[0-9]: ^484[0-9]: ^485[0-9]: ^486[0-9]: ^487[0-9]: ^488[0-9]: ^489[0-9]: ^490[0-9]: ^491[0-9]: ^492[0-9]: ^493[0-9]: ^494[0-9]: ^495[0-9]: ^496[0-9]: ^497[0-9]: ^498[0-9]: ^499[0-9]: ^500[0-9]: ^501[0-9]: ^502[0-9]: ^503[0-9]: ^504[0-9]: ^505[0-9]: ^506[0-9]: ^507[0-9]: ^508[0-9]: ^509[0-9]: ^510[0-9]: ^511[0-9]: ^512[0-9]: ^513[0-9]: ^514[0-9]: ^515[0-9]: ^516[0-9]: ^517[0-9]: ^518[0-9]: ^519[0-9]: ^520[0-9]: ^521[0-9]: ^522[0-9]: ^523[0-9]: ^524[0-9]: ^525[0-9]: ^526[0-9]: ^527[0-9]: ^528[0-9]: ^529[0-9]: ^530[0-9]: ^531[0-9]: ^532[0-9]: ^533[0-9]: ^534[0-9]: ^535[0-9]: ^536[0-9]: ^537[0-9]: ^538[0-9]: ^539[0-9]: ^540[0-9]: ^541[0-9]: ^542[0-9]: ^543[0-9]: ^544[0-9]: ^545[0-9]: ^546[0-9]: ^547[0-9]: ^548[0-9]: ^549[0-9]: ^550[0-9]: ^551[0-9]: ^552[0-9]: ^553[0-9]: ^554[0-9]: ^555[0-9]: ^556[0-9]: ^557[0-9]: ^558[0-9]: ^559[0-9]: ^560[0-9]: ^561[0-9]: ^562[0-9]: ^563[0-9]: ^564[0-9]: ^565[0-9]: ^566[0-9]: ^567[0-9]: ^568[0-9]: ^569[0-9]: ^570[0-9]: ^571[0-9]: ^572[0-9]: ^573[0-9]: ^574[0-9]: ^575[0-9]: ^576[0-9]: ^577[0-9]: ^578[0-9]: ^579[0-9]: ^580[0-9]: ^581[0-9]: ^582[0-9]: ^583[0-9]: ^584[0-9]: ^585[0-9]: ^586[0-9]: ^587[0-9]: ^588[0-9]: ^589[0-9]: ^590[0-9]: ^591[0-9]: ^592[0-9]: ^593[0-9]: ^594[0-9]: ^595[0-9]: ^596[0-9]: ^597[0-9]: ^598[0-9]: ^599[0-9]: ^600[0-9]: ^601[0-9]: ^602[0-9]: ^603[0-9]: ^604[0-9]: ^605[0-9]: ^606[0-9]: ^607[0-9]: ^608[0-9]: ^609[0-9]: ^610[0-9]: ^611[0-9]: ^612[0-9]: ^613[0-9]: ^614[0-9]: ^615[0-9]: ^616[0-9]: ^617[0-9]: ^618[0-9]: ^619[0-9]:"
And that almost works except I had asked 4533 to 6219

So I asked again and told it, hey you can probably do 5000 to 5999 with just one

And it replied

Code: Select all

type myfile.txt | %SystemRoot%\System32\findstr /N "^" | findstr /R "^453[3-9]: ^45[4-9][0-9]: ^4[6-9][0-9][0-9]: ^5[0-9][0-9][0-9]: ^6[0-1][0-9][0-9]: ^620[0-9]: ^621[0-9]:"

And that does actually work


So now I want to create a function which creates these regex ranges from a simple range of lines

Code: Select all

::Usage Call :GetRegexRange X-Y X1-Y1 X2-Y2 ... Xn-Yn
:: returns findstr compatible regex list describing the ranges: ^453[3-9]: ^45[4-9][0-9]: ^4[6-9][0-9][0-9]: ^5[0-9][0-9][0-9]: ^6[0-1][0-9][0-9]: ^620[0-9]: ^621[0-9]:


So what is a range, well it's probably like when you print, a series of pages, individual pages and range of pages

example

Code: Select all

4,6,12,22-38,52-55
You might have even page numbers that repeat, or range of pages that go backward

Code: Select all

55,56,1-5,31-21,17,5,5,5,5,30-1
Ranges might also go forward then backwards, having more than 2 stops

Code: Select all

20-25-17-20,10,11,99-89,56,57,59,22-24-26,1-5
But for now I want the simplest working function so, just two numbers

Code: Select all

235-11579
First thing, split that in two variables

Code: Select all

for /f "delims=- " %%a in ("%_MyRange%") do ( set /a _Range1=%%a & set /a _Range2=%%b )
Next, figure out which is the higher number

Code: Select all

if %_Range1% LSS %_Range2% ( set /a _RangeLow=%_Range1% & set /a _RangeHigh=%_Range2% ) else ( set /a _RangeLow=%_Range1% & set /a _RangeHigh=%_Range2% )

Now it gets harder

In english we have to

Figure out how many digits the higher number has

I will use the examples

Code: Select all

5-9,5-55,15-555,27-47852,25227-45319,29-2555,40000-40008,39987-40022,45315-45319
Ok, in pseudocode I think it looks like this.

Code: Select all

5-9
Then get the len of each numbers

Code: Select all

call :len _RangeHigh _RangeHigh_len
call :len _RangeLow _RangeLow_len
In this case both len is 1

Now loop %_RangeHigh_len% number of times

In this case 1

first loop

get digit _RangeHigh[1] in _RangeHigh_CurrentDigit
if _RangeLow[1] is "" when it's 0 , into _RangeLow_CurrentDigit


so _RangeLow_CurrentDigit=5 and _RangeHigh_CurrentDigit=9

Increment by one _RangeLow_CurrentDigit, decrement by one _RangeHigh_CurrentDigit

if _RangeHigh_CurrentDigit minus _RangeLow_CurrentDigit is greater than zero

create a regex, ^ for the beginning of line, then [%_RangeLow_CurrentDigit%-%_RangeHigh_CurrentDigit%] and for _RangeHigh_len minus one, add [0-9] and end regex string with :

Result should be ^[5-9]:

-------------

Next example

Code: Select all

5-55

Code: Select all

call :len _RangeHigh _RangeHigh_len
call :len _RangeLow _RangeLow_len
get digit _RangeHigh[1] in _RangeHigh_CurrentDigit
if _RangeLow[1] is "" when it's 0 , into  _RangeLow_CurrentDigit

And here is a problem, _RangeLow's first digit is 5 but in the wrong direction
What needed to happen earlier was to leftpad _RangeLow with zeroes until it has as many digits as _RangeHigh


Ok new version

Code: Select all

call :len _RangeHigh _RangeHigh_len
call :len _RangeLow _RangeLow_len
call :leftpad _RangeLow 0  %_RangeHigh_len%
get digit _RangeHigh[1] in _RangeHigh_CurrentDigit
if _RangeLow[1] is "" when it's 0 , into  _RangeLow_CurrentDigit
New state is

_RangeLow=05
_RangeHigh=55
_RangeHigh_len=2
_RangeLow[1]->_RangeLow_CurrentDigit=0
_RangeHigh[1]->_RangeHigh_CurrentDigit=5

Increment by one _RangeLow_CurrentDigit, decrement by one _RangeHigh_CurrentDigit

_RangeLow_CurrentDigit=1
_RangeHigh_CurrentDigit=4


_RangeHigh_CurrentDigit minus _RangeLow_CurrentDigit = 3 is greater than zero
Now we create the first regex

^[%_RangeLow_CurrentDigit%-%_RangeHigh_CurrentDigit%]
or ^[1-4]

Then pad with [0-9] for _RangeHigh_len minus 1 time and end with :, so that's

Code: Select all

^[1-4][0-9]:
Loop to the next index of _RangeHigh_len (this is _RangeHigh_len_index)

Right now we have 10 to 49 covered, we need two more regex ^[5-9]: and ^5[0-5]:

I think for the rest of loop this means a low side and a high side regex needs to be created
The low side regex should take _RangeLow_CurrentDigit and rightpad with zero all remaining positions of _RangeHigh_len, then substract 1. This is the _Current_Regex_LowLimit.

Likewise, _RangeHigh_CurrentDigit, needs to be right padded with 9 and then add one, this makes _Current_Regex_LowLimit

so

call :rightpad _RangeLow_CurrentDigit 0 %_RangeHigh_len%-%_RangeHigh_len_index%
call :rightpad _RangeHigh_CurrentDigit 9 %_RangeHigh_len%-%_RangeHigh_len_index%

_RangeLow_CurrentDigit is now 10
_RangeLow_CurrentDigit is now 49

decrement _RangeLow_CurrentDigit and increment _RangeLow_CurrentDigit

_RangeLow_CurrentDigit is now 9
_RangeLow_CurrentDigit is now 50

I have to quit at this point sorry, I will pick this up later.

_RangeLow_CurrentDigit, might need to be 09, I will see

Sponge Belly
Posts: 229
Joined: 01 Oct 2012 13:32
Location: Ireland
Contact:

Re: How to output a range of lines from a text file using findstr

#2 Post by Sponge Belly » 30 Jun 2024 17:51

Hi Shodan! :)

This has been on my to-do list for a long time. Your post finally spurred me into action. Searching the web yielded nothing. There are plenty of range generators, but no code examples.

No matter, a straightforward problem… or so I thought until I tried to write the program. It proved to be as slippery as an eel. :x

The program accepts 2 inputs: the starting line number, and the finishing line number. It outputs a sequence of ranges, which can be passed to findStr. For example:

Code: Select all

$ range 253 546

25[3-9]:
2[6-9][0-9]:
[34][0-9][0-9]:
5[0-3][0-9]:
54[0-6]:

And here’s the code. Btw, apologies in advance for the total lack of formatting. And all the variables begin with the letter X because it allows me to obtain a dump of the current values of all variables simply by adding the line SET X at any point in the program :

Code: Select all

@echo off & setLocal enableExtensions enableDelayedExpansion
(call;) %= sets errorlevel to 0 =%

:: line feed
(set lf=^
%= blank line required =%
)

:: carriage return
set "cr=" & for /f "skip=1" %%C in (
'echo(^|replace.exe ? . /w /u'
) do if not defined cr set "cr=%%C"

set "hex=0 1 2 3 4 5 6 7 8 9 A B C D E F"
set "digits=0123456789"
set "dipstick=FEDCBA9876543210"

set /a x1=%1,x2=%2

if !x1! gtr !x2! (
2>&1 echo(start greater than end & goto die
)

set /a xEXP1=xEXP2=10,xOff1=xOff2=1
set /a xDiv1=x1/xEXP1,xRem1=x1%%xEXP1,xDiv2=x2/xEXP2,xRem2=x2%%xEXP2
set /a xInterRange1=xEXP1-xRem1,xInterRange2=xRem2+1
set /a xDiff=x2-(x1-1),xRem0=1

for %%A in (%hex%) do for %%B in (%hex%) ^
do for %%C in (%hex%) do for %%D in (%hex%) do if !xDiff! gtr !xInterRange1! (

if defined xRem0 (
if !xRem1! equ 0 (
set /a xEXP1*=10,xOff1+=1
set /a xDiv1=x1/xEXP1,xRem1=x1%%xEXP1
set /a xInterRange1+=xEXP1
) else set "xRem0="
)

if not defined xRem0 (
if !xDiv1! equ 0 set "xDiv1="
for %%I in (!xOff1!) do (
for %%J in ("!x1:~-%%I,1!") do call :genRange "!digits:~%%~J!" xRangeTmp
) %= for =%
if !xOff1! gtr 1 call :genRange0-9 xOff1 xRangesTmp
set "xStr1=!xStr1!!xDiv1!!xRangeTmp!!xRangesTmp!:!cr!!lf!"
set "xRangeTmp=" & set "xRangesTmp="
set /a "x1+=xEXP1-Xrem1,xDiff=x2-(x1-1)"
set /a xEXP1*=10,xOff1+=1,xDiv1=x1/xEXP1,xRem1=x1%%xEXP1
set /a xInterRange1=xEXP1-xRem1,xRem0=1
)

) else if !xDiff! geq !xInterRange2! (

if defined xRem0 (
set /a "xRemTmp=(xRem2+1)%%xEXP2"
if !xRemTmp! equ 0 (
set /a xEXP2*=10,xOff2+=1,xDiv2=x2/xEXP2,xRem2=x2%%xEXP2
set /a "xInterRange2=xRem2+1
) else set "xRem0="
)

if not defined xRem0 (
for %%I in (!xOff2!) do (
set /a "xLen=!x2:~-%%I,1!+1"
for %%L in (!xLen!) do call :genRange "!digits:~0,%%L!" xRangeTmp
) %= for =%
if !xOff2! gtr 1 call :genRange0-9 xOff2 xRangesTmp
set "xStr3=!xDiv2!!xRangeTmp!!xRangesTmp!:!cr!!lf!!xStr3!"
set "xRangeTmp=" & set "xRangesTmp="
set /a "x2-=xRem2+1,xDiff=x2-(x1-1)"
set /a xEXP2*=10,xOff2+=1,xDiv2=x2/xEXP2,xRem2=x2%%xEXP2
set /a "xInterRange2=xRem2+1,xRem0=1
if !x2! lss !x1! goto break
)

) else if !x2! gtr !x1! (

    set /a "x2+=1,xEXP2/=10,xPos=x1/xEXP2,xLen=(x2/xEXP2)-xPos"
if !xLen! gtr 1 (
for %%P in (!xPos!) do (
for %%L in (!xLen!) do call :genRange "!digits:~%%P,%%L!" xRangeTmp
) %= for =%
) else set "xRangeTmp=!xPos!"
if !xOff2! gtr 1 call :genRange0-9 xOff2 xRangesTmp
set "xStr2=!xRangeTmp!!xRangesTmp!:!cr!!lf!"
goto break

) else (

set "xStr1=!x1!:!cr!!lf!" & goto break

) %= "while" =%

:break
echo(!xStr1!!xstr2!!xStr3!
goto end

:die
(call) %= sets errorlevel to 1 =%

:end
endLocal & goto :EOF

:: Jeb's "dipstick" technique for finding length of numbers up to 16 digits long
:numLen %number% result=
setLocal enableDelayedExpansion
set "numLen=!%1!"

set "numLen=%numLen%%dipstick%"
set /a "numLen=0x%numLen:~15,1%"

endLocal & set "%2=%numLen%" & exit /b 0

:: converts seq of nums into reg expr range
:genRange %range% result=
setLocal enableDelayedExpansion
set "range=%~1" & set "rangeLen="

call :numLen range rangeLen
if !rangeLen! geq 2 if !rangeLen! leq 3 (set "range=[!range!]"
) else if !rangeLen! gtr 3 set "range=[!range:~0,1!-!range:~-1!]"

endLocal & set "%2=%range%" & exit /b 0

:: returns N-1 instances of [0-9]
:genRange0-9 offset= result=
setLocal enableDelayedExpansion
set /a "offset=!%1!-1" & set "ranges="

if !offset! gtr 0 for /l %%I in (1 1 !offset!) do set "ranges=!ranges![0-9]"

endLocal & set "%2=%ranges%" & exit /b 0

To use the program, first create a file with numbered lines by typing in this command at the prompt:

Code: Select all

$ findStr /n "^" long-file.txt >long-file-with-numbered-lines.txt

Next, generate the range file:

Code: Select all

$ range 201 3409 >rangeFile.tmp

And finally:

Code: Select all

$ findStr /brg:rangeFile.tmp long-file-with-numbered-lines.txt

FindStr will output lines 201 to 3409 (inclusive) of the file. You can redirect the output to a file, or pipe it to another command if you wish.

I’m pretty sure I squashed all the bugs, but if you find any, please let me know.

Cheers! 8)

- SB

Aacini
Expert
Posts: 1900
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: How to output a range of lines from a text file using findstr

#3 Post by Aacini » 04 Jul 2024 04:49

I missed this thread at first. The problem is very interesting! :D

This is my solution:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem Generate findstr regex-es for ranges of numbers
rem Antonio Perez Ayala

if "%~1" neq "" goto begin
echo Enter a list of ranges separated by commas:
echo     "%~n0" 123-4567,235-11579
goto :EOF

Examples:
5-9,5-55,15-555,27-47852,25227-45319,29-2555,40000-40008,39987-40022,45315-45319

:begin
for /F "tokens=1,2 delims=-" %%a in ("%~1") do set /A "begin=%%a,end=%%b" 2> NUL
if not defined end goto error
if not defined begin goto error
if %begin% leq %end% goto continue
   :error
   echo ERROR: invalid range: %1
   shift
if "%~1" neq "" goto begin
goto :EOF

:continue

rem Get number of digits ("levels") in the values
rem using jeb's "dipstick" technique
set "dipstick=FEDCBA9876543210"
set "levelBegin=%begin%%dipstick%" & set "levelEnd=%end%%dipstick%"
set /A "levelBegin=0x%levelBegin:~15,1%, levelEnd=0x%levelEnd:~15,1%"

rem Cut begin of numbers when they starts the same
rem and create a prefix accordingly

set "prefix="  &  set "prefixBegin="  &  set "forward="  &  set "backward="
if %levelBegin% lss %levelEnd% goto numbersOK
:prefix
if %begin:~0,1% neq %end:~0,1% goto beginPrefix
   set "prefix=%prefix%%begin:~0,1%"
   set "begin=%begin:~1%"
   set "end=%end:~1%"
   set /A levelBegin-=1, levelEnd-=1
   if %levelEnd% equ 0 set "forward=%prefix%" & goto regexEnd
goto prefix
:beginPrefix
if %begin:~0,1% neq 0 goto numbersOK
   set "prefixBegin=%prefixBegin%0"
   set "begin=%begin:~1%"
   set /A levelBegin-=1
if defined begin goto beginPrefix
set "forward=%prefix%%prefixBegin%"
set "prefixBegin=%prefixBegin:~0,-1%"
set /A "begin=1, levelBegin=1"

:numbersOK

rem Generate regex-es in forward order from begin up to the number of digits in end, 
rem adjusting the last digit if required

set /A "notOneOne=^!(^!(levelBegin-1)*^!(levelEnd-1))"
set /A "levelBegin=0, next=%begin:~-1%"
set "prefixDigit=%begin:~0,-1%"
set "tail="
:nextBegin
   set /A "levelBegin+=1"
   if %next% leq 9 (
      if %levelBegin% lss %levelEnd% (
         set "forward=%forward% %prefix%%prefixBegin%%prefixDigit%[%next%-9]%tail%"
      ) else (
         set /A "lastDigit=%end:~0,1%-notOneOne"
         if %next% leq !lastDigit! (
            set "forward=%forward% %prefix%%prefixBegin%[%next%-!lastDigit!]%tail%"
         )
      )
   )
   set "tail=%tail%[0-9]"
   if defined prefixBegin set "prefixBegin=%prefixBegin:~0,-1%"
   if defined begin set "begin=%begin:~0,-1%"
if defined begin set /A "next=%begin:~-1%+1" & set "prefixDigit=%begin:~0,-1%" & goto nextBegin
if %levelBegin% lss %levelEnd% set "next=1" & goto nextBegin


rem Generate regex-es in backward order from end until one before the last digit

set "tail="
set "prev=%end:~-1%"
:prevEnd
   if "%end:~1%" equ "" goto regexEnd
   if %prev% geq 0 set "backward=%prefix%%end:~0,-1%[0-%prev%]%tail% %backward%"
   set "tail=%tail%[0-9]"
   set "end=%end:~0,-1%"
   set /A "prev=%end:~-1%-1"
goto prevEnd

:regexEnd
echo %1  =  %forward% %backward%

shift
if "%1" neq "" goto begin
Output example:

Code: Select all

5-9  =   [5-9] 
5-55  =   [5-9] [1-4][0-9] 5[0-5] 
15-555  =   1[5-9] [2-9][0-9] [1-4][0-9][0-9] 5[0-4][0-9] 55[0-5] 
27-47852  =   2[7-9] [3-9][0-9] [1-9][0-9][0-9] [1-9][0-9][0-9][0-9] [1-3][0-9][0-9][0-9][0-9] 4[0-6][0-9][0-9][0-9] 47[0-7][0-9][0-9] 478[0-4][0-9] 4785[0-2] 
25227-45319  =   2522[7-9] 252[3-9][0-9] 25[3-9][0-9][0-9] 2[6-9][0-9][0-9][0-9] [3-3][0-9][0-9][0-9][0-9] 4[0-4][0-9][0-9][0-9] 45[0-2][0-9][0-9] 453[0-0][0-9] 4531[0-9] 
29-2555  =   2[9-9] [3-9][0-9] [1-9][0-9][0-9] [1-1][0-9][0-9][0-9] 2[0-4][0-9][0-9] 25[0-4][0-9] 255[0-5] 
40000-40008  =  40000 4000[1-8] 
39987-40022  =   3998[7-9] 399[9-9][0-9] 400[0-1][0-9] 4002[0-2] 
45315-45319  =   4531[5-9] 
Antonio

T3RRY
Posts: 249
Joined: 06 May 2020 10:14

Re: How to output a range of lines from a text file using findstr

#4 Post by T3RRY » 05 Jul 2024 01:15

Some interesting Approaches here, but it seems to me it would be simpler to parse the output of findstr with the /V switch and perform conditional checks on the first token token to determine if the line corresponds to the desired ranges - unless the intent is specifically to to support Regex expressions.

Code: Select all

@Echo off & CD /D "%~dp0"

:Range usage: "path\to\INPUT.ext" "path\to\OUTPUT.ext" <RangeStart-RangeEnd> [RangeStart-RangeEnd]
Set "input.file=%~1"
Set "output.file=%~2"
Setlocal EnableDelayedExpansion
Set "ranges=%*"
Set "ranges=!Ranges:%1 %2=!"
REM Example Ranges
REM Set "ranges=5-9,5-55,15-177,27-47852,25227-45319,29-2555,40000-40008,39987-40022,45315-45319"
Endlocal & Set "Ranges=%Ranges%"


Set "i=0"
>"%output.file%" (
  For /f "tokens=1,* Delims=:" %%I in ('Findstr.exe /NV "``%~f0``" "%input.file%"')Do (
    Set "in.Range="
    For %%G in (%Ranges%)Do if not defined in.Range (
      For /f "tokens=1,2 Delims=-" %%i in ("%%~G")Do (
        IF %%I GEQ %%i If %%I LEQ %%j (
          Set "in.Range=true"
          Echo(%%J
) ) ) ) )
type "%output.file%"
Pause

Aacini
Expert
Posts: 1900
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: How to output a range of lines from a text file using findstr

#5 Post by Aacini » 05 Jul 2024 08:58

T3RRY wrote:
05 Jul 2024 01:15
Some interesting Approaches here, but it seems to me it would be simpler to parse the output of findstr with the /V switch and perform conditional checks on the first token token to determine if the line corresponds to the desired ranges - unless the intent is specifically to to support Regex expressions.
Mmmm... In such a case a more efficient way would be to use the method explained at this thread in order to test if a value is between several ranges in a fast way:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

::Usage: %0 file list,of,comma,separated,ranges

rem Get the list of ranges and assemble the testing expression
set "ranges=%*"
set "ranges=!ranges:%1 =!"
set "expr=1"
for %%b in (%ranges%) do (
   for /F "tokens=1,2 delims=-" %%c in ("%%b") do (
      if "%%d" equ "" (
         rem Individual value: multiply previous expr by direct subtract
         set "expr=!expr!*(%%c-n)"
      ) else (
         rem Range value pair: use range expression at this point, then continue
         set "expr=!expr!,a=r,r=0,b=(%%c-n)*(n-%%d),r=(b-1)/b*a"
      )
   )
)

rem Process file lines
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" "%~1"') do (
   set /A "n=%%a, r=%expr%" 2> NUL
   if !r! equ 0 echo(%%b
)

Although I think that the fun part is to generate the regex-es! :D

Antonio

shodan
Posts: 89
Joined: 01 May 2023 01:49

Re: How to output a range of lines from a text file using findstr

#6 Post by shodan » 01 Aug 2024 20:47

Thanks Aacini !
That looks like exactly what I needed.

T3RRY,
I am curious if the pipe of two finstr command ends up being faster than doing the filtering with this method.
Assuming we only need a few hundred lines out of say 10000 lines input text ?

Post Reply