简体   繁体   中英

count the number of certain characters

I would like to write a batch file to count the number of symbols "(" and ")" in file.txt

For example, I have file with this string: "((( ( (( ( ("

Answer would be 8.

But I have code and my answer is 3 :

@echo off 

set "f=file.txt" 
set "sk9=(" 
set "sk0=)" 

set /a "k9=0" 
set /a "k0=0" 



for /f "usebackq delims="  %%a in ("%f%") do for %%i in (%%a) do ( 
if "%%i"=="%sk9%" ( 
        set /a k9=k9+1 
      ) )

echo %k9% 

Windows command processor cmd.exe executing batch files line by line is definitely not the right application for this task. It is designed to execute commands and applications. However, here is a batch file counting parentheses/round brackets in a text file.

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "OpenBracketCount=0"
set "CloseBracketCount=0"

for /F "usebackq tokens=* eol=" %%I in ("File.txt") do (
    set "TextLine=%%I"
    call :ProcessLine
)

echo Number of opening brackets: %OpenBracketCount%
echo Number of closing brackets: %CloseBracketCount%

endlocal
goto :EOF

:ProcessLine
set "TextLine=%TextLine:"=%"
if not defined TextLine goto :EOF

set "TempLine=%TextLine:(=%"
if not defined TempLine goto BracketCount
set "TempLine=%TempLine:)=%"
if "%TempLine%" == "%TextLine%" goto :EOF

:BracketCount
if "%TextLine:~0,1%" == "(" ( set /A "OpenBracketCount+=1" ) else if "%TextLine:~0,1%" == ")" set /A CloseBracketCount+=1
set "TextLine=%TextLine:~1%"
if defined TextLine goto BracketCount
goto :EOF

Command FOR with using option /F to process lines from a text file ignores by default empty lines and lines starting with a semicolon which is the default for end of line option eol , and splits up the line into substrings (tokens) on spaces tabs. It is okay to ignore empty lines, but lines with a ; at beginning should not be ignored for counting parentheses.

Both unwanted line processing behaviors could be disabled with using usebackq^ delims^=^ eol^= to get the entire line assigned to the environment variable TextLine by specifying no delimiters and no end of line character. It is not possible to enclose this option string in double quotes in this special case which requires to escape the spaces and equal signs interpreted by default as argument string separators by caret character ^ to interpret them as literal characters.

But better is to use double quoted option string "usebackq tokens=* eol=" which defines also no end of line character, but keeps space and horizontal tab as delimiters. So assigned to loop variable I is the line after removing leading spaces/tabs. That is good here as it avoids processing lines containing only spaces/tabs and can reduce also the number of characters to process in the subroutine.

In the subroutine ProcessLine first all double quotes are removed from the line read from text file as a " could later in remaining command lines result in a syntax error on execution. It is of course possible that a line contains only one or more " resulting in environment variable TextLine is not defined anymore after removing all double quotes in which case the line definitely does not contain parentheses to count.

Next is assigned to environment variable TempLine the line read from text file (without leading spaces/tabs and without all double quotes) with all ( and ) removed from the line. The line does not contain a parenthesis if TempLine is equal TextLine and so the subroutine can be exited immediately to reduce the execution time of the batch file.

Otherwise the current line contains at least one round bracket and so it is necessary to compare each character of the remaining line with ( and ) to count them .

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • set /?
  • setlocal /?

See also Where does GOTO :EOF return to?

This solution is based on an efficient subroutine that you may use to count any character you wish:

@echo off
setlocal

call :CountChar "(" test.txt LeftParen=
echo Number of left parens = %LeftParen%
goto :EOF


:CountChar char file result=
setlocal DisableDelayedExpansion

rem Create a copy of input file with the desired characters removed

rem Process all file lines in %%a FOR replaceable parameter
(for /F "usebackq delims=" %%a in ("%~2") do (
   set "line=%%a"
   rem Enable status to expand line variable
   setlocal EnableDelayedExpansion
   rem Output the variable without the desired character
   echo(!line:%~1=!
   endlocal
rem Store previous smaller output in another file
)) > smallerFile.txt

rem Get the size difference between both files
for %%a in ("%~2") do for %%b in (smallerFile.txt) do (
   rem Store the result in third subroutine parameter
   endlocal & set /A "%~3=%%~Za-%%~Zb"
)
del smallerFile.txt
exit /b

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM