Reading a file with a batch with n tokens including empty ones - batch-file

I want to write a batch script which reads each line of a file. Sounds easy but the problem is that not every line has the same amount of tokens. The next thing is that every token is seperated from each other with a delimiter which I don't want to have in my output.
Here is a example of the file:
string1|string2|string3|string4
string1|||string4
When I'm reading that file, every token has to be on its right place. What I mean is when I'm reading the second line the output must be:
string1 empty empty string4
thanks for help

gawk for Windows can do the job:
awk -F"|" "{for (i=1;i<=NF;i++) $i=$i?$i:\"empty\"};7" file.in>file.out

#ECHO OFF
SETLOCAL
(
FOR /f "delims=" %%a IN (q21551753.txt) DO (
SET line=%%a
SETLOCAL ENABLEDELAYEDEXPANSION
SET line=!line:^|= ^|!
ECHO(!line!
endlocal
)
)>tempfile.txt
SET "spaces= "
FOR /f "tokens=1-7delims=|" %%a IN (tempfile.txt) DO (
SET "$a=%%a%spaces%"
SET "$b=%%b%spaces%"
SET "$c=%%c%spaces%"
SET "$d=%%d%spaces%"
SET "$e=%%e%spaces%"
SET "$f=%%f%spaces%"
SET "$g=%%g%spaces%"
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO(!$a:~0,8!!$b:~0,9!!$c:~0,8!!$d:~0,8!!$e:~0,9!!$f:~0,5!!$g:~0,6!
endlocal
)
GOTO :EOF
I used a file named q21551753.txt for my testing.
string1|string2|string3|string4
string1|||string4
|string2||string4|string5|string6
%one%|!2!||"three"|^four^|string6
Response:
string1 string2 string3 string4
string1 string4
string2 string4 string5 strin
%one% !2! "three" ^four^ strin
Note that the column-widths are defined in the ECHO(!$a... statement - your choice of the number and width of each column. Note that spaces is set to a bunch of spaces.
tempfile.txt is created. Whether you delete this, where it is created and what you call it are all up to you.

The following assumes data does not contain !, and no line begins with ;. These restrictions can be eliminated with more code.
The key trick is to use variable expansion search and replace to convert str1|str2|str3|str4 into "str1"|"str2"|"str3"|"str4". Then a second FOR /F can safely parse the tokens, even when the value is missing. The values you want are in %%~A, %%~B, %%~C, and %%~D. The line I ECHO is just to demonstrate the values of each token.
#echo off
setlocal enableDelayedExpansion
for /f "delims=" %%L in (test.txt) do (
set "ln=%%L"
set "ln="!ln:^|="|"!""
for /f "tokens=1-4 delims=|" %%A in ("!ln!") do (
echo A=%%~A B=%%~B C=%%~C D=%%~D
)
)

Thanks for your answers.
But I found out that I had another problem. The count of the tokens are not always the same and the count is often about 20-40 tokens at one line.
I created a solution with a sub-procedure:
#echo off
setlocal enabledelayedexpansion
for /f "tokens=*" %%a in (Book1.csv) DO (
set "work=%%a"
for /l %%i in (1,1,2) do set "work=!work:||=|NULL|!"
set "line=!work!"
call :processToken
)
goto eof
:processToken
for /F "tokens=1* delims=|" %%x in ("!line!") do (
echo %%x
set line=%%y
)
if not "!line!" == "" goto :processToken
goto :eof
:eof

Related

Removing carriage returns from a text file using a batch file based on a value

I have a text file that I would like to edit and therefore would like to remove the last line. I have the following code for this:
for /f "delims=" %%a in (input.txt) do (
echo/|set /p ="%%a%"
)>>output.txt
input:
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
output:
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
Now I would like to edit the data in groups for example by the first value, so that I have the following output:
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
If I replace the FOR /F "delims=" %%a in (input.txt) do … loop with an equivalent FOR %%a in … loop:
#ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
set "_gruppeName="
(
for %%a in (
"GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
) do (
for /f "tokens=1 delims=;" %%A in ("%%~a") do (
if /I NOT "%%~A"=="!_gruppeName!" (
if defined _gruppeName echo(
set "_gruppeName=%%~A"
)
)
echo/|set /p ="%%~a"
)
echo(
)>>output.txt
REM debugging output follows
type output.txt
Output:
1st run: 2>NUL del output.txt & D:\bat\CR\61816520.bat
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
Next run: D:\bat\CR\61816520.bat
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
Your question is not clear
based on ... the first value (GRUPPEA)
is it SORTed? or just write duplicates on the same line?
#echo off
SETLOCAL EnableExtensions EnableDelayedExpansion
::VARIABLES
set "in=input.txt" IN file
set "out=output.txt" OUT file
set/a"#lines=0"
set "gruppe="
set "prevContent="
::Count lines
FOR /F %%L in ('
"findstr /N "^^" "%in%" %= do not skip empty lines =%"
') do set/a"#lines+=1" %= Get the # of lines =%
::Read IN via SET /P #LINES number of times
::Bangs (!) will be lost
<"%in%" >"%out%" (FOR /L %%# in (1 1 %#lines%) do (
set "data=" ::clear DATA
set/p"data=" ::read from IN
FOR /F tokens^=1^ delims^=^;^ eol^= %%T in ("!data!") do set "gruppe=%%T"
if NOT "!prevContent!" == "!gruppe!" (
set "prevContent=!gruppe!"
echo(
)
<nul set/p"=!data!" ::does not work with leading space, tabs, or equal signs
)) %= read file by lines via SET /P =%
exit /b
The script counts the number of lines using FINDSTR /N and a FOR /F loop to count the # of lines, which is required to execute SET /P that many times.
Tips:
Use ECHO( instead of the problematic ECHO.
Using a pipe | is very slow, as described by #jeb here. Use <nul instead

Replace a matching string in each line with another string

I am trying to replace the number at the end of every line of a given file with an incremented number(+1).
My Input file looks like
C:\documents\a.txt,1988,15.01.00.0059
C:\documents\we.txt,1988,15.01.00.0059
C:\documents\gh.txt,1987,1988,15.01.00.0059
my out put file should be
C:\documents\a.txt,1988,15.01.00.0060
C:\documents\we.txt,1988,15.01.00.0060
C:\documents\gh.txt,1988,15.01.00.0060
My attempt is like
#echo off
setlocal enableextensions disabledelayedexpansion
set "search="
for /F "tokens=1-3 delims=," %%i in (%1) do (
#echo %%i %%j %%k
set "search=%%k"
)
set "replace="
REM #echo %replace%
set "textFile=%1"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
>>"%textFile%" echo(!line:%search%=%replace%!
endlocal
)
i could able to get 15.01.00.0059 from the input file but dont know how to change to 15.01.00.0060 .
replacing to the same input file is also working . I just need to know how to increment the value and assign to "replace".
Thank you
Bhargav.k
This should be flexible enough:
#echo off
setlocal EnableDelayedExpansion EnableExtensions
1>out.txt (
FOR /F tokens^=*^ delims^=.^ eol^= %%L in (input.txt) do (
set "line=%%L"
set "x=!line:.= !"
FOR %%T in (!x!) do set "lastToken=%%T"
cmd /c exit /b !lastToken!
set/a"lstrip=!ERRORLEVEL!+1"
>getlen.tmp echo(!lasttoken!
FOR %%L in (getlen.tmp) do set/a"len=%%~zL-2" %= get length of last token, minus CRLF =%
FOR %%N in (1 1 !len!) do set "lstrip=0!lstrip!" PREFIX lstrip with zeroes
FOR %%N in (!len!) do (
set "lstrip=!lstrip:~-%%N!"
echo !line:~0,-%%N!!lstrip!
)
)
)
del getlen.tmp
ENDLOCAL
This is not a straightforward approach.
The script counts the # of zeroes prefixed before the last token by the ERRORLEVEL returned by EXIT /B, compared with the original token.

count an exact character in one line - cmd

I would like write a batch file to count the number of occurrences of a specific character in each line of a text file.
For example, the count of \ in the string "aa\bb\cc\dd\" would be 4.
The find and the findstr show only the number of lines which is contains the exact character.
You might try the following script, providing the input string as (quoted) command line argument:
set "STRING=%~1$"
set STRING="%STRING:\=" "%"
set /A "COUNT=-1"
for %%E in (%STRING%) do set /A "COUNT+=1"
echo Count of `\`: %COUNT%
This replaces every character to be counted by " + SPACE + " and encloses the entire string in between "", so the input string aa\bb\cc\dd\ becomes "aa" "bb" "cc" "dd" "". The resulting string is fed into a for loop that recognises individual items to iterate through -- five in this case. The counter variable COUNT is initialised with a value of -1, so the result is not the number of iterated items but the separators, namely the \ characters present in the original string.
This approach fails if the string contains ? or * characters. It would also fail in case the character to count is one of the following: ", %, =, *, ~.
#echo off
setlocal
set "string=aa\bb\cc\dd\"
set "count=-1"
for %%a in ("%string:\=" "%") do set /A count+=1
echo %count%
This method works correctly as long as the string don't include wild-card characters: *?; if this is required, I would use the same npocmaka's method, but written in a simpler way:
#echo off
setlocal EnableDelayedExpansion
set "string=aa\bb\cc\dd\"
set "str=A%string%Z"
set "count=-1"
for /F "delims=" %%a in (^"!str:\^=^"^
% Do NOT remove this line %
^"!^") do (
set /A count+=1
)
echo %count%
While slow, you can try with this
#echo off
setlocal enableextensions disabledelayedexpansion
set "inputFile=input.txt"
set "searchChar=\"
for /f "delims=" %%a in ('
findstr /n "^" "%inputFile%"
') do for /f "delims=:" %%b in ("%%~a") do (
set "line=%%a"
for /f %%c in ('
cmd /u /v /e /q /c"(echo(!line:*:=!)"^|find /c "%searchChar%"
') do echo Line %%b has %%c characters
)
The input file is readed using findstr /n to get all the lines in the file with a number prefix (both for output "decoration" and to ensure all the lines in the file are processed). Each line is processed inside a pipe, from cmd to find. The cmd instance is started with unicode output (/u) so when the readed line is echoed, the output will be a two bytes sequence for each input character, one of them a 0x0 ASCII character. The find command sees the 0 as a line terminator, so we get each character in the input line as one separated line. Now, the find command counts in how many lines the searched character happens.
#ECHO OFF
SETLOCAL
SET "String=a\b\c\\\\d"
CALL :count "%string%" \
ECHO %tally%
GOTO :EOF
:count
SETLOCAL enabledelayedexpansion
SET /a tally=0
SET "$2=%~1"
:cloop
SET "$1=%$2%"
SET "$2=!$1:*%2=!"
IF "%$1%" neq "%$2%" SET /a tally+=1&GOTO cloop
endlocal&SET tally=%tally%
GOTO :eof
Here's a way to count particular characters in a string. It won't work for the usual suspects.
here's one way:
#echo off
:checkCountOf string countOf [rtnrVar]
:: checks count of a substring in a string
setlocal EnableDelayedExpansion
set "string=aa"
set "string=%~1"
set "checkCountOf=%~2"
if "%~1" equ "" (
if "%~3" neq "" (
endlocal & (
echo 0
set "%~3=0"
exit /b 0
)
) else (
endlocal & (
echo 0
exit /b 0
)
)
)
if "!checkCountOf!" equ "$" (
set "string=#%string%#"
set "string=!string:%checkCountOf%%checkCountOf%=#%checkCountOf%#%checkCountOf%#!"
) else (
set "string=$%string%$"
set "string=!string:%checkCountOf%%checkCountOf%=$%checkCountOf%$%checkCountOf%$!"
)
set LF=^
rem ** Two empty lines are required
set /a counter=0
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!checkCountOf!") do (
set "var=!string:%%~R%%~R=%%~L!"
set "var=!var:%%~R=%%~L!"
for /f "tokens=* delims=" %%# in ("!var!") do (
set /a counter=counter+1
)
)
)
if !counter! gtr 0 (
set /a counter=counter-1
)
if "%~3" neq "" (
endlocal & (
echo %counter%
set "%~3=%counter%"
)
) else (
endlocal & (
echo %counter%
)
)
you can call it like:
call ::checkCountOf "/aa/b/c/" "/" slashes
echo %slashes%
exit /b %errorlevel%
wont work with some special characters (",~ and !)
You can also use replacement and the :strlen function
Not tested extensively but works with your example.
#ECHO OFF
SETLOCAL disabledelayedexpansion
SET "String=\a\b\c\\\\d\\"
set "previous=%string%"
set /a count=0
:loop
set "newstg=%previous:*\=%"
IF NOT "%previous%"=="%newstg%" (
set /a count+=1
set "previous=%newstg%"
IF DEFINED previous goto loop
)
echo %count%
pause
GOTO :eof
Here is one more option. I don't think this is bullet proof with poison characters.
#ECHO OFF
SETLOCAL disabledelayedexpansion
SET "String=\\a\b\c\\\\d\\"
set i=0
set "x=%string%"
set "x=%x:\=" & set /A i+=1 & set "x=%"
echo %i%
pause

Substring length in text file using Batch

In input, I have a text file which contains numbers separated with a comma.
list.txt
111221,345,332133,66,5555, and so
I want to check the length of each string between the "," delimiter, in order to successively display the length of each word.
For example:
111221 is 6 characters long
345 is 3 characters long
332133 is 6 characters long
66 is 2 characters long
...
For this, I've written this code but it displays only the first word and the length is always "0". Without the for loop, it works fine for a single chain. Has anyone an idea to fix this?
Thank you.
#echo off
setlocal enabledelayedexpansion enableextensions
for /f "delims=," %%a in ('type list.txt') do (
set string=%%a
set temp_str=%string%
set str_len=0
:loop
if defined temp_str (
set temp_str=%temp_str:~1%
set /a str_len+=1
goto:loop )
echo !string! is !str_len! characters long
)
pause
endlocal
#echo off
setlocal enabledelayedexpansion enableextensions
for /f "delims=" %%f in ('type q35202446.txt') do (
for %%a in (%%f) do (
set "string=%%a"
set "temp_str=!string!"
set str_len=0
CALL :loop
echo !string! is !str_len! characters long
)
)
GOTO :eof
:loop
if defined temp_str (
set temp_str=%temp_str:~1%
set /a str_len+=1
GOTO loop )
GOTO :eof
I used a file named q35202446.txt containing your data for my testing.
Your problems are :
for /f ... reads a line at a time, so delims=, would provide you with just the first token in the line. Next iteration would read the next line (if it existed)
Putting the entire line ("delims=") into %%f allows you to use the default function of , (along with space, semicolon and tab) - a separator. The for ... %%a... sees a simple list of elements separated by commas.
You must use !string! to access the run-time value of string (with delayedexpansion invoked.) %string% will deliver the parse-time value of string which will be empty, hence reporting length 0.
Note the use of quotes in the string-assignment. That syntax ensures trailing spaces are not included in the string assigned.
A label terminates a "block" (parenthesised series of statements) so I've moved the length-calculator to a subroutine.
Your echo was correctly using !var! to access the run-time value of the variables.
You should use for /f to read line-by-line, then an inner for to tokenize each line on the commas. Something like this:
#echo off
setlocal
for /f "usebackq delims=" %%a in ("list.txt") do (
for %%t in (%%a) do (
call :length len %%t
setlocal enabledelayedexpansion
echo %%t is !len! characters long.
endlocal
)
)
goto :EOF
:length <return_var> <string>
setlocal enabledelayedexpansion
if "%~2"=="" (set ret=0) else set ret=1
set "tmpstr=%~2"
for %%I in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if not "!tmpstr:~%%I,1!"=="" (
set /a ret += %%I
set "tmpstr=!tmpstr:~%%I!"
)
)
endlocal & set "%~1=%ret%"
goto :EOF
Credit: the :length function is based on jeb's answer here. It's much more efficient than a goto loop.

Batch For loop array

Okay so here is what I have.
#echo off
setLocal EnableDelayedExpansion
:begin
set /a M=0
set /a number=0
set /p Input=You:
echo %Input% >> UIS
for /F "tokens=1 delims= " %%i in ("%Input%") do (
set /a M+=1
set i!M!=%%i
)
del UIS 1>nul 2>nul
:loop
set /a number+=1
set invar=!i%number%!
echo %invar%
pause > nul
goto loop
Say, for example, the Input string was "Lol this is my input string"
I want the for loop to set i!M! where M = 1 to "Lol", where M = 2 i!M! is "this" and where M = 3 i!M! is "is" and so on. Now, of course, this can't go on forever, so even if I have to stop when M = 25 or something, and say the string was only 23 words long. Then when M = 24 and 25 then i!M! is simply null or undefined.
Any help is appreciated, thank you.
for /f reads line by line, not word by word.
Here's an answer proposed at How to split a string in a Windows batch file? and modified for your situation:
#echo off
setlocal ENABLEDELAYEDEXPANSION
REM Set a string with an arbitrary number of substrings separated by semi colons
set teststring=Lol this is my input string
set M=0
REM Do something with each substring
:stringLOOP
REM Stop when the string is empty
if "!teststring!" EQU "" goto displayloop
for /f "delims= " %%a in ("!teststring!") do set substring=%%a
set /a M+=1
set i!M!=!substring!
REM Now strip off the leading substring
:striploop
set stripchar=!teststring:~0,1!
set teststring=!teststring:~1!
if "!teststring!" EQU "" goto stringloop
if "!stripchar!" NEQ " " goto striploop
goto stringloop
:displayloop
set /a number+=1
set invar=!i%number%!
echo %invar%
pause > nul
goto displayloop
endlocal
for /F command divide a line in a definite number of tokens that must be processed at once via different replaceable parameters (%%i, %%j, etc). Plain for command divide a line in an undefined number of words (separated by space, comma, semicolon or equal-sign) that are processed one by one in an iterative loop. This way, you just need to change this for:
for /F "tokens=1 delims= " %%i in ("%Input%") do (
by this one:
for %%i in (%Input%) do (
PS - I suggest you to write the array in the standard form, enclosing the subscript in square brackets; it is clearer this way:
set i[!M!]=%%i
or
set invar=!i[%number%]!

Resources