MattD
MattD

Reputation: 150

Windows Batch Scripting: Checking file for multiple strings

I have a batch file that processes scanned PDFs using ghostscript. One of the user prompts is for the resolution of the desired output. I wrote a crude autodetect routine like this:

for /f "delims=" %%a in ('findstr /C:"/Height 1650" %1') do set resdect=150
for /f "delims=" %%a in ('findstr /C:"/Height 3300" %1') do set resdect=300
for /f "delims=" %%a in ('findstr /C:"/Height 6600" %1') do set resdect=600
echo %resdect% DPI detected.

%1 is the filename passed to the batch script.

This should return the the highest resolution detected of some common sizes we see. My question to the community is: Is there a faster or more efficient way to do this other than search the file multiple times?

Upvotes: 0

Views: 166

Answers (4)

MattD
MattD

Reputation: 150

For the record, the final code was:

setlocal enabledelayedexpansion
set resdetc=0
for /f "delims=" %%a in ('findstr /C:"/Height " %1') do (
  set "line=%%a"
  set "line=!line:*/Height =!"
  for /f "delims=/ " %%b in ("!line!") do set "hval=!hval! %%b" 
)
for %%a in (1650,3300,6600) do @(
  echo " %hval% " | find " %%a " >nul && set /a resdetc=%%a/11
)
if %resdetc%==0   SET resDefault=3
if %resdetc%==150 SET resDefault=1
if %resdetc%==300 SET resDefault=3
if %resdetc%==600 SET resDefault=6

ECHO.
ECHO Choose your resolution
ECHO ----------------------
ECHO 1. 150    4. 400
ECHO 2. 200    5. 500
ECHO 3. 300    6. 600
ECHO.
IF NOT %RESDETC%==0 ECHO 7. Custom    (%resdetc% DPI input detected)
IF     %RESDETC%==0 ECHO 7. Custom
ECHO ----------------------
choice /c 1234567 /T 3 /D %resDefault% /N /M "Enter 1-7 (defaults to %resDefault% after 3 sec.): "
IF errorlevel==7 goto choice7
IF errorlevel==6 set reschoice=600 & goto convert
IF errorlevel==5 set reschoice=500 & goto convert
[...]

Thanks everyone for the help!

Upvotes: 0

Stephan
Stephan

Reputation: 56208

get the corresponding line to a variable and work with that instead of the whole file. Instead of your three for loops, you can use just one, when you change the logic a bit:

@echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('findstr /C:"/Height " %1') do (
  set "line=%%a"
  set "line=!line:*/Height =!"
  for /f "delims=/ " %%b in ("!line!") do set "hval=!hval! %%b" 
)
for %%a in (1650,3300,6600) do @(
  echo " %hval% " | find " %%a " >nul && set /a resdect=%%a/11
)
echo %resdect% DPI detected.

A solution with jrepl.bat could look something like:

for /f %a in ('type t.txt^|find "/Height "^|jrepl ".*/Height ([0-9]{4}).*" "$1"^|sort') do set /a dpi==%a / 11

(given, all valid Heights have 4 digits)
Note: for use in batchfiles, use %%a instead of %a
I barely scratched the surface of jrepl - I'm quite sure, there is a much more elegant (and probably faster) solution.

Upvotes: 2

Aacini
Aacini

Reputation: 67236

You may directly convert the Height value into the highest resolution in a single operation using an array. However, to do that we need to know the format of the line that contain the Height value. In the code below I assumed that the format of such a line is /Height xxxx, that is, that the height is the second token in the line. If this is not true, just adjust the "tokens=2" value in the for /F command.

EDIT: Code modified as requested in comments

In this modified code the Height value may appear anywhere in the line.

@echo off
setlocal EnableDelayedExpansion

rem Initialize "resDect" array
for %%a in ("1650=150" "3300=300" "6600=600") do (
   for /F "tokens=1,2 delims==" %%b in (%%a) do (
      set "resDect[%%b]=%%c"
   )
)

set "highResDect=0"
for /F "delims=" %%a in ('findstr "/Height" %1') do (
   set "line=%%a"
   set "line=!line:*/Height =!"
   for /F %%b in ("!line!") do set /A "thisRectDect=resDect[%%b]"
   if !thisRectDect! gtr !highResDect! set "highResDect=!thisRectDect!"
)

echo %highResDect% DPI detected.

Upvotes: 2

aschipfl
aschipfl

Reputation: 34959

Assuming that the value of RESDECT is the /Height value divided by 11, and that no line contains more than one /Height token, the following code might work for you:

@echo off
for /F delims^=^ eol^= %%A in ('findstr /R /I /C:"/Height  *[0-9][0-9]*" "%~1"') do (
    set "LINE=%%A"
    setlocal EnableDelayedExpansion
    set "RESDECT=!LINE:*/Height =!"
    set /A "RESDECT/=11"
    echo/!RESDECT!
    endlocal
)

If you only want to match the dedicated /Height values 1650, 3300, 6600, you could use this:

@echo off
for /F delims^=^ eol^= %%A in ('findstr /I /C:"/Height 1650" /C:"/Height 3300" /C:"/Height 6600" "%~1"') do (
    set "LINE=%%A"
    setlocal EnableDelayedExpansion
    set "RESDECT=!LINE:*/Height =!"
    set /A "RESDECT/=11"
    echo/!RESDECT!
    endlocal
)

To gather the greatest /Height value appearing in the file, you can use this script, respecting the aforementioned assumptions:

@echo off
set "RESDECT=0"
for /F delims^=^ eol^= %%A in ('findstr /R /I /C:"/Height  *[0-9][0-9]*" "%~1"') do (
    set "LINE=%%A"
    setlocal EnableDelayedExpansion
    set "HEIGHT=!LINE:*/Height =!"
    for /F %%B in ('set /A HEIGHT/11') do (
        if %%B gtr !RESDECT! (endlocal & set "RESDECT=%%B") else endlocal
    )
)
echo %RESDECT%

Of course you can again exchange the findstr command line like above.


Here is another approach to get the greatest /Height value, using (pseudo-)arrays, which might be faster than the above method, because there are no extra cmd instances created in the loop:

@echo off
setlocal
set "RESDECT=0"
for /F delims^=^ eol^= %%A in ('findstr /R /I /C:"/Height  *[0-9][0-9]*" "%~1"') do (
    set "LINE=%%A"
    setlocal EnableDelayedExpansion
    set "HEIGHT=!LINE:*/Height =!"
    set /A "HEIGHT+=0, RES=HEIGHT/11" & set "HEIGHT=0000000000!HEIGHT!"
    for /F %%B in ("$RESOLUTIONS[!HEIGHT:~-10!]=!RES!") do endlocal & set "%%B"
)
for /F "tokens=2 delims==" %%B in ('set $RESOLUTIONS[') do set "RESDECT=%%B"
echo %RESDECT%
endlocal

At first all heights and related resolutions are collected in an array called $RESOLUTIONS[], where the /Height values are used as indexes and the resolutions are the values. The heights become left-zero-padded to a fixed number of digits, so set $RESOLUTIONS[ return them in ascending order. The second for /F loop returns the last arrays element whose value is the greatest resolution.

I do have to admit that this was inspired by Aacini's nice answer.

Upvotes: 4

Related Questions