Reputation: 373
I have the following code;
setlocal EnableDelayedExpansion
:splitEncode
::Get the number of Chapters
set "cmd=FINDSTR /R /N "^.*" %~n1.txt | FIND /C ":""
for /F %%a in ('!cmd!') do set numChapters=%%a
::Cycle through this once for every chapter, getting the line and the line after it
for /L %%a in (1,1,%numChapters%) do (
set "skip="
if %%a geq 2 (
set /a skip=%%a-1
set "skip=skip=!skip!"
)
for /F "!skip! tokens=1,2" %%i in ("%~n1.txt") do (
set startTime=%%i
set chapterName=%%j
)
set "skip=skip=%%a"
for /F !skip! %%i in ("%~n1.txt") do (
set endTime=%%i
)
echo %startTime% %endTime% %chapterName%
)
First I find out how many lines are in a text file, and set that to the variable numChapters
.
I then use this to create a for loop that iterates for each chapter.
Inside the for loop, there are two further loops. The first reads a line, and the second reads the following line.
The intent of this is to read lines 1+2, 2+3, 3+4, and use those values as part of a command run the same number of times as the number of lines. This means that from a list such as this;
00:00:00 The Meeting Room/The Meeting
00:03:36 Long Distance Runaround
00:07:47 Wonderous Stories
I can end up with a command that includes the start time, end time, and chapter title.
The issue I am facing is that no matter what I do, I cannot get the nested for loops to use the skip variables. I've tried %%a
, %skip%
, !skip!
, and none of them work. The value isn't correctly substituted in any situation.
Does anyone have any way to get this variable used, or a better method of reading a specific line of a text file than a for loop?
Upvotes: 0
Views: 337
Reputation: 34899
The option string of for /F
(like the root path of for /R
) requires immediate (%
-)expansion, because for
(besides if
and rem
) is recognised by the command interpreter even before delayed expansion and also expansion of for
meta-variables occur.
A possible solution is to put each for /F
loop with the dynamic skip
options into a sub-routine, to use call
to call it and to apply %
-expansion therein (see all the additional rem
remarks for explanations):
@echo off
setlocal EnableDelayedExpansion
:splitEncode
rem Get the number of chapters
rem // To determine the number of lines in a file you do not need `findstr`:
for /F %%a in ('^< "%~n1.txt" find /C /V ""') do set "numChapters=%%a"
rem Cycle through this once for every chapter, getting the line and the line after it
for /L %%a in (1,1,%numChapters%) do (
set /A "skip=%%a-1"
call :getTwoValues startTime chapterName "%~n1.txt" "!skip!"
call :getTwoValues endTime dummy "%~n1.txt" "%%a"
rem /* For the last line, there is of course no next line containing the end time;
rem therefore, let us mark that case specifically: */
if not defined endTime set "endTime=??:??:??"
rem /* If there is no chapter specified, do not output anything; this might also
rem be quite useful in case the last line just contains a time stamp but no
rem chapter name just to provide the end time of the last one: */
if defined chapterName echo !startTime! !endTime! !chapterName!
)
goto :EOF
:getTwoValues <1st var. name> <2nd var. name> <file path/name> <lines to skip>
rem // Ensure not to return the former output, and set up `skip` option string:
set "%~1=" & set "%~2=" & set /A "skip=0, skip+=%~4" 2> nul
if %skip% gtr 0 (set "skip=skip=%skip%") else set "skip="
rem /* Added `usebackq` in order not to interprete the quoted file path/name as
rem a literal string; also changed the `tokens` option to return the first
rem token and then the whole remainder of the line: */
rem /* Remember that `for /F` regards empty lines for its `skip` option, but it
rem does not iterate through such; hence the first line it iterates over is
rem actually the first non-empty line after the number of skipped lines: */
for /F "usebackq %skip% tokens=1,*" %%i in ("%~3") do (
set "%~1=%%i"
set "%~2=%%j"
rem // Since we do not want to iterate to the last line, leave the loop here:
goto :EOF
)
rem /* This is just needed in case `skip` points beyond the end of the file, or
rem there are no more non-empty lines behind the skipped ones: */
goto :EOF
Based on your sample data, the output should be this:
00:00:00 00:03:36 The Meeting Room/The Meeting 00:03:36 00:07:47 Long Distance Runaround 00:07:47 ??:??:?? Wonderous Stories
However, the entire approach could be heavily simplified, when you avoid the file multiple times and do not read each line twice, by simply reading the file line by line once, but return the chapter information from the previous iteration, together with the end time from the current line:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~n1.txt" & rem // (path/name of file to process)
set "_FRMT=??:??:??" & rem // (dummy end time output for last chapter)
rem /* Initialise variables; loop through lines of files, augmented by
rem an additional line at the end, to alyways output last chapter: */
set "STA=" & for /F "tokens=1,*" %%K in ('
type "%_FILE%" ^& echo(%_FRMT%
') do (
rem // Output the chapter from the previous loop iteration:
set "END=%%K" & if defined STA if defined NAME (
setlocal EnableDelayedExpansion
echo(!STA! !END! !NAME!
endlocal
)
rem // Store chapter information for the next loop iteration:
set "STA=%%K" & set "NAME=%%L"
)
endlocal
exit /B
Upvotes: 1