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.
Related
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
Sorry I can't word this correctly but I need a command to create a text file that dumps the file names in numeric order. Here is the command that does what I want but everything that has 1 at the start is bunched, 2, 3 etc.....
(for %i in (*.flv) do #echo file '%i') > file.txt
Most cmd/batch file commands, like dir, sort and set, for instance, do pure alphabetic sorting, so any numeric parts are not treated particularly. For example, string12 appears before string3, because the character 1 appears before 2 with the used way of sorting. To change the behaviour so that alpha-numeric sorting is applied, meaning that string3 appears before string12, you have to write your own code for accomplishing that. The following script does exactly that, by padding every numeric part appearing in strings/file names to a fixed amount of digits, in which case alpha-numeric and alphabetic sort orders match. Below is the code, including explanatory remarks. As you can see, complex code is required to accomplish the task and to make the script secure against all characters that have special meanings to cmd (like SPACE, ,, ;. = as well as ^, &, (, ), %, !):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_TEMPFILE=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (temporary file used for sorting)
set /A "_DIGITS=12" & rem // (number of digits every numeric part is padded for sorting)
rem // Build string containing enough zeros for padding:
set "$PAD=" & setlocal EnableDelayedExpansion
for /L %%D in (1,1,%_DIGITS%) do set "$PAD=!$PAD!0"
endlocal & set "$PAD=%$PAD%"
rem // Prepare temporary file containing data to sort:
> "%_TEMPFILE%" (
rem // Gather and resolve all command line arguments:
for %%I in (%*) do (
rem // Get pure file name:
set "ITEM=%%~nxI"
rem // Extend all numeric parts in file name to certain number of digits:
call :PROCESS AUGM ITEM || >&2 echo ERROR: potential problem sorting "%%~nxI"!
rem // Write extended and original file name into temporary file:
setlocal EnableDelayedExpansion
echo(!AUGM!^|!ITEM!
endlocal
)
)
rem // Return content of temporary file in ascendingly sorted order:
for /F "tokens=2 delims=| eol=|" %%J in ('sort "%_TEMPFILE%"') do (
rem // Simply return each item:
echo(%%J
)
rem // Delete temporary file:
del "%_TEMPFILE%"
endlocal
exit /B
:PROCESS rtn_augmented_string ref_string
rem /* Routine to augment a string so that every numeric part is padded with leading
rem zeros to the left to hold a predefined number of digits: */
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "#ARG=%~2"
rem // Initialise required variables:
set "COLL="
set "ERRL=0"
setlocal EnableDelayedExpansion
for /F delims^=^ eol^= %%B in (^""!%#ARG%!"^") do (
endlocal
set "PSTR=%%~B"
setlocal EnableDelayedExpansion
)
rem // Entry point for loop to handle one numeric string part:
:REPEAT
rem // Extract the string portions before and after the first numeric part:
for /F "tokens=1,* delims=0123456789 eol=0" %%A in ("+!PSTR!") do (
endlocal
set "PART=%%A"
set "NEXT=%%B"
rem // Determine length of string portion before first numeric part:
call :LENGTH PLEN PART
set /A "PLEN-=1"
setlocal EnableDelayedExpansion
set "PART=!PART:~1!"
rem // Split off string portion before first numeric part from total string:
for %%C in (!PLEN!) do (
if defined PSTR set "PSTR=!PSTR:~%%C!"
)
rem /* Splitt off string portion after first numeric part from remaining string;
rem this is nothing but extracting the first numeric part itself: */
call :SPLIT PNUM NEXT PSTR
rem // Determine the actual length of the numeric part:
call :LENGTH NLEN PNUM
rem // Do the actual padding with leading zeros of the numeric part:
if defined PNUM (
set "PNUM=%$PAD%!PNUM!"
set "PNUM=!PNUM:~-%_DIGITS%!"
)
rem // Store the part after the first numeric part:
for /F delims^=^ eol^= %%C in (^""!NEXT!"^") do (
rem /* Build string with the string portion before the current numeric part
rem and the padded current numeric part itself: */
for /F delims^=^ eol^= %%D in (^""!COLL!!PART!!PNUM!"^") do (
rem // Check whether the predefined number of padding digits is sufficient:
for /F %%E in ("!NLEN!") do (
endlocal
set "PSTR=%%~C"
set "COLL=%%~D"
if %%E GTR %_DIGITS% set "ERRL=1"
setlocal EnableDelayedExpansion
)
)
)
)
rem // Repeat the whole approach while there is still a remaining string portion:
if defined PSTR goto :REPEAT
rem // Return the string with padded numeric parts:
for /F delims^=^ eol^= %%R in (^""!COLL!"^") do (
endlocal
endlocal
set "%#RTN%=%%~R"
exit /B %ERRL%
)
exit /B
:SPLIT rtn_left_string ref_split_char val_string
rem /* Routine to split a string at the first occurrence of a certain character and to
rem return the portion before it: */
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "#CHR=%~2"
set "#ARG=%~3"
rem // Initialise required variables:
setlocal EnableDelayedExpansion
set "CHAR= " & if defined %#CHR% set "CHAR=!%#CHR%:~,1!"
if "!CHAR!"=="<" (set "PREF=>") else (set "PREF=<")
rem // Check whether a split character is defined:
if defined %#CHR% (
rem /* Split character available, so split off first occurrence and everything after
rem from the original string: */
for /F eol^=^%CHAR%^ delims^=^%CHAR% %%C in ("%PREF%!%#ARG%!") do (
endlocal
set "%#RTN%=%%C"
setlocal EnableDelayedExpansion
set "%#RTN%=!%#RTN%:~1!"
)
) else (
rem // No split character defined, so do not split off anything:
set "%#RTN%=!%#ARG%!"
)
rem // Return the resulting string:
for /F delims^=^ eol^= %%R in (^""!%#RTN%!"^") do (
endlocal
endlocal
set "%#RTN%=%%~R"
)
exit /B
:LENGTH rtn_length ref_string
rem /* Routine to determine the length of a given string: */
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "#ARG=%~2"
setlocal EnableDelayedExpansion
rem // Check whether a string is provided:
if defined %#ARG% (
rem // String is available, so calculate its length:
set /A "%#RTN%=1"
for %%A in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if not "!%#ARG%:~%%A!"=="" (
set /A "%#RTN%+=%%A"
set "%#ARG%=!%#ARG%:~%%A!"
)
)
) else (
rem // String is empty, so length is zero:
set /A "%#RTN%=0"
)
rem // Return the computed length:
for /F %%R in ("!%#RTN%!") do (
endlocal
endlocal
set "%#RTN%=%%R"
)
exit /B
Provide (a) file pattern(s) as (a) command line argument(s), like this, for example (supposing the batch file is saved as sort-alpha-num.bat):
sort-alpha-num.bat "*.flv"
To store the resulting sorted list in a text file called file.txt, use this:
sort-alpha-num.bat "*.flv" > "file.txt"
In case an error message like ERROR: potential problem sorting "1000000000000.flv"! appears, increase the number of digits on top of the script (see line set /A "_DIGITS=12").
Since you didn't give us more information about how your output file should be ?
#echo off
set "folder=%userprofile%\Desktop\*.flv"
set "listfiles=listfiles.txt"
If Exist %listfiles% Del %listfiles%
setLocal EnableDelayedExpansion
Rem Populate the array with existent files in this folder
for /f "tokens=* delims= " %%a in ('Dir /s /b /a:-d "%folder%"') do (
set /a Count+=1
set "File[!Count!]=%%~na"
)
::******************************************************************
:Display_Files
cls & color 0B
echo(
For /L %%i in (1,1,%Count%) do (
echo %%i - !File[%%i]!
)
echo(
(
rem to save result into logfile
For /L %%i in (1,1,%Count%) do (
echo %%i - !File[%%i]!
)
)>> %listfiles%
echo Hit any key to open %listfiles% :
Pause>nul & Start "" %listfiles%
First of approaches, excuse me if I do not express myself well in English.
I'm debutante in batch and I need help to make a script
I articles.txt retrieves a document in which there are many lines.
some lines of my document
"T0047" ;"Tuyau 1km";"Marque2";"jardinage";"75 000";"promo"
"T00747";"Tuyau 1m";Marque2";"jardinage";"30 000";"promo"
First, I have to remove the quotation marks in the file.
It is done with:
#echo off
setlocal enabledelayedexpansion
for /F "delims=" %%a in (articles.txt) do (
set a=%%a
set a=!a:"=!
echo !a!
echo !a! >>resultat.txt
)
the result
T0047 ;Tuyau 1km;Marque2;jardinage;75 000;promo
T00747;Tuyau 1m;Marque2;jardinage;30 000;promo
Then I have to perform a multiplication on a column.
For this, I have the problem that if the space is not so mutiplication realize I made a script that removes spaces.
#echo off
setlocal enabledelayedexpansion
for /F "delims=; tokens=1-8" %%a in (resultat.txt) do (
set a=%%e
set a=!a: =!
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h >>resultat2.txt
)
the result
T0047 ;Tuyau 1km;Marque2;jardinage;75000;promo
T00747;Tuyau 1m;Marque2;jardinage;30000;promo
Then I made my multiplication.
#echo off
setlocal enabledelayedexpansion
for /F "delims=; tokens=1-8" %%a in (resultat2.txt) do (
set a=%%e
:: set /a a=!a!/0.6
set /a a=!a!*16666/10000
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h >>resultat3.txt
)
the result
T0047 ;Tuyau 1km;Marque2;jardinage;124995;promo
T00747;Tuyau 1m;Marque2;jardinage;49998;promo
Now, i add some text just after the first colomn
set champ2=MAGASIN_1;T
for /F "delims=; tokens=1,*" %%a in (resultat3.txt) do (
echo %%a;%champ2%;%%b
echo %%a;%champ2%;%%b >>resultat_final.txt
)
The actual result is:
T0047 ;MAGASIN_1;T;Tuyau 1km;Marque2;jardinage;124995;promo
T00747;MAGASIN_1;T;Tuyau 1m;Marque2;jardinage;49998;promo
Now I would add a space so that the figure is more readable.
T0047 ;MAGASIN_1;T;Tuyau 1km;Marque2;jardinage;124 995;promo
T00747;MAGASIN_1;T;Tuyau 1m;Marque2;jardinage;49 998;promo
This is the way I would do it:
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%A in (articles.txt) do (
set "a=%%A"
set a=!a:"=!
for /F "delims=; tokens=1-8" %%a in ("!a!") do (
set /A "g1=%%g*16666/10000"
set "g2="
for /L %%i in (1,1,3) do if defined g1 (
set "g2= !g1:~-3!!g2!"
set "g1=!g1:~0,-3!
)
echo %%a;%%b;%%c;%%d;%%e;%%f;!g2:~1!;%%h
echo %%a;%%b;%%c;%%d;%%e;%%f;!g2:~1!;%%h >> result.txt
)
)
articles.txt:
"T0047" ;"MAGASIN_1";"T";"Tuyau 1km";"Marque2";"jardinage";"75000";"promo"
"T00747";"MAGASIN_1";"T";"Tuyau 1m";Marque2";"jardinage";"30000";"promo"
result.txt:
T0047 ;MAGASIN_1;T;Tuyau 1km;Marque2;jardinage;124 995;promo
T00747;MAGASIN_1;T;Tuyau 1m;Marque2;jardinage;49 998;promo
Your program is good. Some tips:
Don't divide by a power of 10. Instead, remove the fractional part if you don't want it. Use *= . And to get the space in the number:
#echo off
set x=75000
set /a x *= 16666
set x=%x:~0,-4%
echo %x:~0,-3% %x:~-3%
I'll respond only to the multiplication section.
I can see nothing in your code that can possibly generte the two extra columns ;MAGASIN_1;Tand consequently, the target field 75000 and 30000 are in %%g, not %%e.
Comment : Do not use the "broken label" comment form ::comment within a block statement (a parenthesised series of statements) because it can terminate the block prematurely. Always use rem with a block.
So - modified code working on %%g
set a=%%g
rem set /a a=!a!/0.6
REM set /a a=!a!*16666/10000
set /a a=!a!*10/6
SET "a= !a:~-9,-6! !a:~-6,-3! !a:~-3!"
FOR /f "tokens=*" %%q IN ("!a!") DO SET "a=%%q"
echo %%a;%%b;%%c;%%d;%%e;%%f;!a!;%%h
Reason: Batch has a signed-32-bit limit, so if the source field is >~120000 then your calculation will generate a negative number (try 130000 for example) The revised calculation is more accurate and since intermediate results are less likely to exceed 2**31 can cope with larger values in the %%g field.
The set following the calculation changes the numeric value in a to
space(millions)space(thousands)space(units)
(The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned. set /a can safely be used "quoteless".)
The for /f "tokens=*"... statement simply removes leading spaces from the value of a.
With the explanatin of the two additional columns, This revision should solve the "add-spaces" problem:
set a=%%e
rem set /a a=!a!/0.6
REM set /a a=!a!*16666/10000
set /a a=!a!*10/6
SET "a= !a:~-9,-6! !a:~-6,-3! !a:~-3!"
FOR /f "tokens=*" %%q IN ("!a!") DO SET "a=%%q"
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h
however, if you want to skip the last step (insertion of 2 extra fields) then insert this line before the for line in the "multiplication" batch
set champ2=MAGASIN_1;T
and change the echo line in that batch to
echo %%a;%champ2%;%%b;%%c;%%d;!a!;%%f;%%g;%%h
Since you have a semicolon-delimited list of values where each item is enclosed within quotation marks, I would go for a standard for to get the items of each line and remove the enclosing quotation marks. The great advantage of this method is that it really cares about the quotation marks, so the list items may even contain semicolons on their own. The only disadvantage is that question marks and asterisks are not allowed in any of the list items:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Redirect all data to output file "resultat.txt" at once:
> "resultat.txt" (
rem Loop through all (non-empty) lines of input file "articles.txt":
for /F "usebackq delims=" %%L in ("articles.txt") do (
rem Reset list collector and loop index:
set "LIST="
set /A "INDEX=0"
rem Loop through the list items of the current line:
for %%I in (%%L) do (
rem Apply current list item with `""` removed, increment loop index:
set "ITEM=%%~I"
set /A "INDEX+=1"
rem Do numeric calculation for a certain list item:
setlocal EnableDelayedExpansion
if !INDEX! EQU 5 (
rem Convert item to a number, avoid error messages:
2> nul set /A "CALC=!ITEM!"
rem Do calculation with rounding (for negative and positive numbers):
if !CALC! LSS 0 (
set /A "CALC=(!CALC!*10-6/2)/6"
) else (
set /A "CALC=(!CALC!*10+6/2)/6"
)
rem Insert thousands separators (space) between every third digit:
set "CALC=!CALC:~-12,-9! !CALC:~-9,-6! !CALC:~-6,-3! !CALC:~-3!"
for /F "tokens=*" %%N in ("!CALC!") do (
set "ITEM=%%N"
)
)
rem Append separator (semicolon) and current item to list:
for /F delims^=^ eol^= %%S in ("!LIST!;!ITEM!") do (
endlocal
set "LIST=%%S"
)
)
rem Return built list, remove superfluous leading separator (`;`):
setlocal EnableDelayedExpansion
echo(!LIST:~1!
endlocal
)
)
endlocal
exit /B
The calculation herein incorporates rounding to the nearest integer, which works even for negative input numbers.
The newly generated list is stored into the new file resultat.txt.
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
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%]!