Find and replace several lines of text with an Batch File? - windows

I have parameter files (text files) which are always filled with different values. It's approx. 1500 Lines. Between this lines is a set of 30 lines which is always the same and this I would like to manipulate with batch file.
So for example.
This is the current file.
1 different value
2 different value
3 different value
4 different value
5 fixed value
6 fixed value
7 fixed value
8 different value
9 different value
But I would like to exchange it like.
1 different value
2 different value
3 different value
4 different value
5 EXCHANGED value
6 EXCHANGED value
7 EXCHANGED value
8 different value
9 different value
Perhaps also to add more text because its not always on the same position. The Text which get exchanged is always the same, but the position in the rows can move a little.
--
#echo off set "replace=something" set "replaced=different"
set "source=Source.txt" set "target=Target.txt"
setlocal enableDelayedExpansion ( for /F "tokens=1* delims=:" %%a in
('findstr /N "^" %source%') do ( set "line=%%b" if defined line set
"line=!line:%replace%=%replaced%!" echo(!line!
I found also already some code which i modifyed, but this works only for single words. If i try to do more he simply ignores the ones before.
I worked again a bit on it..
But its not really doing what i want.
Source_File ; the file where i the text inside what the batch is should looking for (exact)
Traget_File ; the file where what i want in the end with the exchanged lines. (final)
Search_For ; This is a line inside where the starts to exchange.
Exchange_With ; the file with the new Lines.
Dont be made about the not proper working progress bar, i simply like it :P
SET "Source_File=D:\SEARCH.para"
SET "Target_File=D:\A.para"
SET "Search_For=//______________DATA_______________"
SET "Exchange_With=D:\TARGET.para"
set i=10
(Set /P j=...Please wait) < NUL
ECHO.
(Set /P j=..............................) < NUL
:Start
call :DisplayProgressBar %i%
set /a i = i + 1
if /i %i% leq 100 goto start
ECHO.
ECHO.
IF NOT DEFINED Search_For (ECHO Failure: Variable Search_For not set!&GOTO :eof)
IF EXIST %Target_File% (DEL /f %Target_File% 1>NUL 2>NUL)
FOR /f "delims=" %%i IN ('FINDSTR . "%Source_File%"') DO (
SET Line=%%i& CALL :Exchange !Line!
)
GOTO :NEXT
:Exchange
SET Line=!Line:%Search_For%=%Exchange_With%!
IF [!Line!] EQU [] (ECHO.>>%Target_File%) ELSE (ECHO !Line!>>%Target_File%)
GOTO :eof
:NEXT
del "%Source_File%"
move /y "%Target_File%" "%Source_File%"
:DisplayProgressBar
(Set /P j=.) < NUL
title %1%% Completed
exit /b

Related

Batch: Iterate .csv file columns

I have this .csv file:
col0,col1,col2,col3,col4
a,1,10,100,1000
b,2,11,101,1001
c,3,12,102,1002
d,4,13,103,1003
e,5,14,105,1004
I need to iterate each column in the .csv without knowing the number of columns.
First column is skipped because is not needed.
I have this code so far, but I need a solution for the case where I don't know the number of columns.
I need the value of each column in a later step where I calculate something.
#echo off
setlocal enableDelayedExpansion
:: set workspace data
set INPUT_FILE_LOCATION=D:\Scripts\
set CSV_FILE_NAME=test.csv
pushd %INPUT_FILE_LOCATION%
::loop through the csv file
for /F "tokens=2,3,4,5 delims=," %%i in (%CSV_FILE_NAME%) do (
echo %%i,%%j,%%k,%%l
rem echo.%%~i^|END
)
endlocal
To be more specific, I have a .csv file, with some columns and many many rows. Starting with the second column, I will need to make the difference of every two elements of every column to verify if there is at least one difference greater than 1.(The values on columns are going to be in ascending order, so as an example using the csv above, the code should do the following: starting on col1, verify if 2-1 > 1, then if 3-2 > 1, then if 4-3 > 1 then 5-4 > 1, then it should verify the same thing for the next column(col2) and so on, until we reach the last column. If I will find one difference greater than 1, I want to print a message that "a bigger difference was found on" the header of that column where the bigger difference was found; Somehow I want to localize in which column was found the unexpected difference by using the title of the column from the header; for example, in col3, we have a difference greater than 1 and I want to print "there's a difference greater than 1 in col3", where col3 is in the header). In time, I will need to add some more columns, so the file could have 30 or 40 columns with the same structure like the previous ones.
#ECHO OFF
SETLOCAL
rem The following settings for the source directory, filenames are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q71308045.txt"
:: comma-separated list of columns to ignore
SET "ignorecolumns=1"
:: remove all 'cell' variables from environment
For %%b IN (cell) DO FOR /F "delims==" %%c In ('set %%b 2^>Nul') DO SET "%%c="
SET /a rowcount=0
SET /a maxcolumns=0
rem usebackq should be omitted if the source filename is not quoted
rem skip=1 skips the first (header) line. Omit to skip no lines
FOR /f "usebackq skip=1 delims=" %%b IN ("%filename1%") DO (
CALL :process %%b
CALL :linebyline
)
ECHO %rowcount% rows, maximum %maxcolumns% columns
SET cell
GOTO :EOF
:process
SET /a rowcount+=1
SET /a columns=0
:procloop
IF "%~1"=="" GOTO :eof
SET /a columns+=1
IF DEFINED ignorecolumns FOR %%c IN (%ignorecolumns%) DO IF %columns%==%%c GOTO donecolumn
SET "cell[%rowcount%,%columns%]=%~1"
:donecolumn
IF %columns% gtr %maxcolumns% SET /a maxcolumns=columns
SET /a cellsinrow[%rowcount%]=%columns%
SHIFT
GOTO procloop
GOTO :eof
:: processing line-by-line if required
:linebyline
ECHO row %rowcount% has %columns% columns
GOTO :eof
Here's a generalised solution, in the absence of specifics.
Note that it does not cater for empty columns.
Each line is presented to :process as a parameter. :process counts each column and inserts it into the cells wired-array omitting any columns not required and tracks the maximum column-number found and the number of cells in each row.
Environment space is limited AFAIAA, so compensatory measures are needed if huge amounts of data are processed.
The :linebyline routine is executed for each row, so if the required processing does not need cells then rowcount could be set back to 0 within this routine, having the effect of reporting cells[1,*] for each line
--- revision following clarification
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the source directory, filenames are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q71308045.txt"
:: comma-separated list of columns to ignore
SET "ignorecolumns=1"
:: remove all 'cell' variables from environment
For %%b IN (cell) DO FOR /F "delims==" %%c In ('set %%b 2^>Nul') DO SET "%%c="
SET /a rowcount=0
SET /a maxcolumns=0
rem usebackq should be omitted if the source filename is not quoted
rem skip=1 skips the first (header) line. Omit to skip no lines
FOR /f "usebackq delims=" %%b IN ("%filename1%") DO (
CALL :process %%b
CALL :linebyline
)
rem ECHO %rowcount% rows, maximum %maxcolumns% columns
rem SET cell
GOTO :EOF
:process
SET /a rowcount+=1
SET /a columns=0
:procloop
IF "%~1"=="" GOTO :eof
SET /a columns+=1
IF DEFINED ignorecolumns FOR %%c IN (%ignorecolumns%) DO IF %columns%==%%c GOTO donecolumn
SET "cell[%rowcount%,%columns%]=%~1"
:donecolumn
IF %columns% gtr %maxcolumns% SET /a maxcolumns=columns
SET /a cellsinrow[%rowcount%]=%columns%
SHIFT
GOTO procloop
GOTO :eof
:: processing line-by-line if required
:linebyline
:: if rowcount=1 then column names are in cell[1,*] and nothing to do
:: if rowcount=2 then we have the starting data row and nothing to do
IF %rowcount% lss 3 GOTO :eof
:: Now we can compare row 2 to row %rowcount%
FOR /L %%c IN (1,1,%maxcolumns%) DO IF "!cell[2,%%c]!" neq "" CALL :matchcells %%c
:: And move row %rowcount% to row 2; removing row %rowcount% from environment
FOR /L %%c IN (1,1,%maxcolumns%) DO IF "!cell[2,%%c]!" neq "" SET cell[2,%%c]=!cell[%rowcount%,%%c]!&SET "cell[%rowcount%,%%c]="
GOTO :eof
:: Match cell[2,%1] to cell[%rowcount%,%1]
:matchcells
SET /a celldiff = !cell[%rowcount%,%1]! - !cell[2,%1]!
IF %celldiff% == 1 GOTO :eof
ECHO row %rowcount% column %1 [!cell[1,%1]!] value difference = %celldiff%
GOTO :eof
Well, very little difference here. I still believe that the specification is faulty, since if you know the first data row, then you know what every following data row should be as each column in each succeeding row should be one more than the value in the row prior. Therefore you only need one row of data as you can generate the remaining rows and don't need to go through a generate/verify cycle.

Batch code to turn every line of text to a variable

So I need a code that will take a text file (we'll cal it list.txt) and turn every line into a variable.
To give some context, I have some file names listed in list.txt, which adds and deletes file names occasionally by user request. I want the user of this code to be able to select which document they'd like to open using variables.
For example, if list.txt looks like this
list.txt
loading.txt
test1.txt
test2.txt
test3.txt
test4.txt
Then I'd like an automatic variable for every .txt listed. I then would add a simple if statement to open the file matched with the variable.
Am I making this too complicated for myself or is this the only way to do this?
EDIT:
I am not attempting something like this:
type list.txt
echo.
echo.
set /p GROUPSELECT= Please type out the FULL name of the group listed:
CD %grouplist%
type %GROUPSELECT%
It will display the file contents, and then display the specific file chosen by the input. I'd think that the variable might be easier to do more with later though, just a thought.
Edit2
I tried this:
#Echo OFF
FOR /F "Usebackq Delims=" %%a IN (
"list.txt"
) DO (
set jim=%%a
)
echo %jim%
PAUSE
%jim% will only be the last line in the text file, so how do I make the next step into making them all different?
Give this a try. I have commented each line of code that should explain what it is doing. Let me know if you need any further explanation.
#echo off
REM FOR commnd is parsing the output of the FINDSTR commands.
REM First FINDSTR command is finding non empty lines in the file
REM Second Findstr command is assigning a number to each line of the file
REM The output is split into two tokens and the number is assigned to %%G and the line of the file to %%H
for /F "tokens=1* delims=:" %%G IN ('findstr /V "^$" list.txt ^|findstr /N ".*"') DO (
REM create a variable of the lines in the file
set "file%%G=%%H"
REM Create a menu of the lines in the file
echo %%G %%H
REM Get the number of lines in the output
set "num=%%G"
)
REM Ask user to chose a number
set /P "filenum=Chose a file number 1 to %num%:"
REM Need to enable delayed expansion to use array variables
setlocal enabledelayedexpansion
REM Check if they type in a correct number and display the file
IF DEFINED file%filenum% type !file%filenum%!
endlocal
pause
Is this the sort of thing you're looking for?
#For /F "Delims==" %%# In ('Set # 2^>Nul')Do #Set "%%#="
#For /F Tokens^=1*Delims^=[]^ EOL^= %%# In ('Type "file.txt"^|Find /V /N ""'
)Do #Set "#%%#=%%$"&Echo( %%#. %%$
:Opt
#Echo(
#Set /P "Opt=Choose a document to open>"
#Set #|Findstr /B "#%Opt%=">Nul||GoTo :Opt
#Call Start "" "%%#%Opt%%%"
Just change the name of the text file containing your list on line 2 as needed.
#TheBoy your question is confusingly framed. If you DO NOT want the example you put in the edit, then can you better explain what you DO want??
Do you want to say iterate over the list file, and create a choice screen?
#(SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ChoiceList_File=C:\Admin\ChoiceFile.txt"
REM Full Character List to populate Choices
SET "_CharList=0 1 2 3 4 5 6 7 8 9 A B C D E F N X"
)
CALL :Main
( ENDLOCAL
EXIT /B 0
)
:Main
REM Now we can Do Our Choices btu lets do it in a Sub Function.
CALL :MakeChoice _ChoiceResult
ECHO.
ECHO The Index Chosen Was: %_Chosen%
ECHO The Result Matched is: "%_ChoiceResult%"
REM ECHO Here you output "%_ChoiceResult%"
TYPE "%_ChoiceResult%"
GOTO :EOF
:MakeChoice
cls
color 1A
SET %~1="
SET "_Choices="
SET "_Chosen="
SET "_Amount="
SET "_Choice.17.Value=Next Set!"
SET "_Choice.18.Value=EXIT!"
SET "_Choice_Show_Next="
echo. Pick a File:
echo.========================
REM Create Numbered Array of Choices and output Choices to the Screen
FOR /F "tokens=* usebackq" %%A IN ("%_ChoiceList_File%") DO (
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=%%A"
IF !_Amount! EQU 16 (
SET /A "_Amount+=2"
CALL :MakeChoice "%~1"
IF DEFINED _Chosen (
IF !_Chosen! NEQ 17 (
REM IF !_Chosen! NEQ 18 (
GOTO :EOF
REM )
)
SET "_Amount="
SET "_Chosen="
)
)
)
IF NOT DEFINED _Chosen (
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=!"
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=EXIT!"
CALL :MakeChoice "%~1"
)
GOTO :EOF
:MakeChoice
CLS
ECHO.
SET "_Temp="
SET "_Choices="
SET /A "_Skipline= !_Amount! - 1"
REM Create Choice List to Display only the choices needed.
FOR %%A IN (%_CharList%) DO (
SET /A "_Temp+=1"
IF !_Temp! LEQ !_Amount! (
IF !_Temp! EQU !_Skipline! (
ECHO.
ECHO.=============================
)
IF DEFINED _Choice.!_Temp!.Value (
SET "_Choices=!_Choices!%%A"
CALL ECHO. %%A : %%_Choice.!_Temp!.Value%%
)
)
)
ECHO.
CHOICE /C !_Choices! /N /M "What File do you want to choose? "
SET "_Chosen=%ERRORLEVEL%"
SET "%~1=!_Choice.%_Chosen%.Value!"
GOTO :EOF
)
You can try this:
#echo off
setlocal enabledelayedexpansion
set jim=
for /f "delims=] tokens=1*" %%a in ('find /v /n "" ^<list.txt') do (
set jim=!jim! %%b
)
echo Select a file from !jim!
set /p file=
type !file!
pause
This will read all lines from the list.txt and return them within a variable !jim!.

How to find total commas in the first line of a file?

I want create a batch file to find the total number of commas in the first line of text file.
Sample Text File
input.txt
12345,Bhavik
12323,Bhavik,Sanghvi
Output
1
I tried to surf net for this but couldnt find a solution, please help
Here's another simple solution to this question.
#echo off
setlocal enabledelayedexpansion
set LF=^
::Above 2 blank lines are critical - do not remove
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
set /p var=<input.txt
echo "%var:,="!cr!!lf!"..***..%">temp.file
find /c "..***.." <temp.file
del temp.file
#echo off
setlocal EnableDelayedExpansion
rem Read the first line
set /P "line=" < input.txt
rem Store it in a text file
> before.txt echo !line!
rem Store the line without commas in a second file
> after.txt echo !line:,=!
rem Get the difference in sizes between both files
set "diff="
for %%a in (before.txt after.txt) do (
if not defined diff (
set "diff=%%~Za"
) else (
set /A "diff-=%%~Za"
)
)
del before.txt after.txt
echo %diff%
If, rather than being hampered by the awful Windows BATCH tools, you install awk from the Unix tools for Windows here, you can do this:
awk -F, 'NR==1{print NF-1;exit}' input.txt
That says... "Run awk and use commas as the separator to divide fields. On line 1, print the number of fields on this line minus 1, then exit. Do that for file input.txt."
gawk is just a slightly different version of awk if you get that one in the Unix Utils package. You may need to replace the single quotes with double ones to accommodate Windows' lack of abilities.
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q35826440.txt"
:: first method
SET /a count=0
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO SET "line=%%a"&GOTO got1
:got1
SET "line=%line:"=%"
IF NOT DEFINED line ECHO method 1: %count% found&GOTO method2
IF "%line:~-1%"=="," SET /a count+=1
SET "line=%line:~0,-1%"
GOTO got1
:: second method
:method2
SET /a count=-1
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO SET "line=%%a"&GOTO got2
:got2
SET "line=%line:"=%"
SET "line=%line:;=%"
SET "line=%line: =%"
SET "line=%line:,=x,x%"
FOR %%a IN (%line%) DO SET /a count+=1
ECHO method 2: %count% found
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
I used a file named q35826440.txt containing your data for my testing.
Two methods - both read the first line to line, then removes any " characters.
The first then mechanically loops, checking whether the last character is a comma, counting if it is and removing the last character until the string found is empty.
The second replaces all ; and Space characters (for good measure, Tab could be removed too) and then replacing commas with x,x.
The result is that the only separators left are commas, and there will be 1 more item in the list so formed than there are commas.
Hence, start the counter at -1 and increment for each element found in the list.
Next solution (similar to Magoo's second method) seems to treat even ˙cmd˙ and .bat poisonous characters supposed in input file:
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "infile=D:\bat\SO\files\35826440input.txt" change to suit your circumstances
set /A "commacount=-1"
for /F "usebackq delims=" %%G in ("%infile%") do (
set "line=%%G"
call :parseline
if /I not "%~1"=="/all" goto :continue
)
:continue
echo script continues here
ENDLOCAL
exit /B
:parseline
rem treat unbalanced doublequote in next line
set "lineToParse=%line:"=§%"
set "lineToParse=%lineToParse:,=","%"
set /A "commacount=-1"
for %%g in ("%lineToParse%") do (
set /A "commacount+=1"
rem echo %line%, !commacount!, %%g
)
echo %commacount% "%line%"
goto :eof
Output (with input file listing):
==> D:\bat\SO\35826440.bat
1 "12345,Bhavik"
script continues here
==> D:\bat\SO\35826440.bat /all
1 "12345,Bhavik"
2 "12323,Bhavik,Sanghvi"
3 "12323,Bhavik,Sanghvi,three"
0 "zero"
1 ",1 leading"
2 ",,2 leading"
1 "trailing,"
2 "2 trailing,,"
2 "2 middle,,mid"
4 "!OS!,!,!!,!!!,exclamations"
4 "%OS%,%,%%%,%%,percents"
8 "&,|,>,<,",",;,=,miscelaneous"
0 "unbalanced"doublequote"
script continues here
==> type D:\bat\SO\files\35826440input.txt
12345,Bhavik
12323,Bhavik,Sanghvi
12323,Bhavik,Sanghvi,three
zero
,1 leading
,,2 leading
trailing,
2 trailing,,
2 middle,,mid
!OS!,!,!!,!!!,exclamations
%OS%,%,%%%,%%,percents
&,|,>,<,",",;,=,miscelaneous
unbalanced"doublequote
==>

LastIndexOf in Windows batch

I need to implement a function in a Windows batch script to get the LastIndexOf a character into a given string.
For example: Given the following string, I need to get the last index of character '/':
/name1/name2/name3
^
So I need to get the value:
12
Joey's solution works, but the character to find is hard coded, and it is relatively slow.
Here is a parametized function that is fast and can find any character (except nul) within the string. I pass the name of variables containing the string and the character instead of string literals so that the function easily supports all characters.
#echo off
setlocal
set "test=/name1/name2/name3"
set "char=/"
::1st test simply prints the result
call :lastIndexOf test char
::2nd test stores the result in a variable
call :lastIndexOf test char rtn
echo rtn=%rtn%
exit /b
:lastIndexOf strVar charVar [rtnVar]
setlocal enableDelayedExpansion
:: Get the string values
set "lastIndexOf.char=!%~2!"
set "str=!%~1!"
set "chr=!lastIndexOf.char:~0,1!"
:: Determine the length of str - adapted from function found at:
:: http://www.dostips.com/DtCodeCmdLib.php#Function.strLen
set "str2=.!str!"
set "len=0"
for /L %%A in (12,-1,0) do (
set /a "len|=1<<%%A"
for %%B in (!len!) do if "!str2:~%%B,1!"=="" set /a "len&=~1<<%%A"
)
:: Find the last occurrance of chr in str
for /l %%N in (%len% -1 0) do if "!str:~%%N,1!" equ "!chr!" (
set rtn=%%N
goto :break
)
set rtn=-1
:break - Return the result if 3rd arg specified, else print the result
( endlocal
if "%~3" neq "" (set %~3=%rtn%) else echo %rtn%
)
exit /b
It wouldn't take much modification to create a more generic :indexOf function that takes an additional argument specifying which occurance to find. A negative number could specify to search in reverse. So 1 could be the 1st, 2 the 2nd, -1 the last, -2 penultimate, etc.
(Note: I'm assuming Windows batch files because, frankly, I have only seen a single question asking for an actual DOS batch file here so far. Most people simply misattribute “DOS” to anything that has a window of gray-on-black monospaced text without knowing what they're actually talking of.)
Just loop through it, updating the index as you go:
#echo off
setlocal enabledelayedexpansion
set S=/name1/name2/name3
set I=0
set L=-1
:l
if "!S:~%I%,1!"=="" goto ld
if "!S:~%I%,1!"=="/" set L=%I%
set /a I+=1
goto l
:ld
echo %L%
I know this question is a bit old now, but I needed a function that could find the location of a substring (of any length) within a string, and adapted dbenham's solution for my purposes. This function also works with individual characters within a string, as asked for in the original question, and can search for specific instances (as suggested by dbenham).
To use this function, the actual strings must be passed. Dbenham does note that this supports fewer characters than passing the actual variables, but I find that this variant is more reuseable (especially with pipes).
The third argument takes the instance that should be found, with negative numbers specifying to search from the end. The index returned is the offset from the start of the string to the first character in the substring.
#ECHO off
SET search_string=sub
CALL :strIndex "The testing subjects subjects to testing." "%search_string%" -2
ECHO %ERRORLEVEL%
PAUSE
EXIT
:strIndex string substring [instance]
REM Using adaptation of strLen function found at http://www.dostips.com/DtCodeCmdLib.php#Function.strLen
SETLOCAL ENABLEDELAYEDEXPANSION
SETLOCAL ENABLEEXTENSIONS
IF "%~2" EQU "" SET Index=-1 & GOTO strIndex_end
IF "%~3" EQU "" (SET Instance=1) ELSE (SET Instance=%~3)
SET Index=-1
SET String=%~1
SET "str=A%~1"
SET "String_Length=0"
FOR /L %%A IN (12,-1,0) DO (
SET /a "String_Length|=1<<%%A"
FOR %%B IN (!String_Length!) DO IF "!str:~%%B,1!"=="" SET /a "String_Length&=~1<<%%A"
)
SET "sub=A%~2"
SET "Substring_Length=0"
FOR /L %%A IN (12,-1,0) DO (
SET /a "Substring_Length|=1<<%%A"
FOR %%B IN (!Substring_Length!) DO IF "!sub:~%%B,1!"=="" SET /a "Substring_Length&=~1<<%%A"
)
IF %Substring_Length% GTR %String_Length% GOTO strIndex_end
SET /A Searches=%String_Length%-%Substring_Length%
IF %Instance% GTR 0 (
FOR /L %%n IN (0,1,%Searches%) DO (
CALL SET StringSegment=%%String:~%%n,!Substring_Length!%%
IF "%~2" EQU "!StringSegment!" SET /A Instance-=1
IF !Instance! EQU 0 SET Index=%%n & GOTO strIndex_end
)) ELSE (
FOR /L %%n IN (%Searches%,-1,0) DO (
CALL SET StringSegment=%%String:~%%n,!Substring_Length!%%
IF "%~2" EQU "!StringSegment!" SET /A Instance+=1
IF !Instance! EQU 0 SET Index=%%n & GOTO strIndex_end
))
:strIndex_end
EXIT /B %Index%

Help in writing a batch script to parse CSV file and output a text file

I am struggling to write a batch script which can read a CSV file such as below
Name:, City:, Country:
Mark, London, UK
Ben, Paris, France
Tom, Athens, Greece
There will be a heading row in the CSV file. It should output to a text file as below:
Name:Mark
City:London
Country:UK
Name:Ben
City:Paris
Country:France
Name:Tom
City:Athens
Country:Greece
The field separator (:) in the above output is expected to be provided in the header row itself. So all that I need to do is concatenate the field heading and its value.
The number of columns in this CSV file is not fixed, so the script should not limit to 3 tokens. Kindly help!
#ECHO OFF
IF "%~1"=="" GOTO :EOF
SET "filename=%~1"
SET fcount=0
SET linenum=0
FOR /F "usebackq tokens=1-10 delims=," %%a IN ("%filename%") DO ^
CALL :process "%%a" "%%b" "%%c" "%%d" "%%e" "%%f" "%%g" "%%h" "%%i" "%%j"
GOTO :EOF
:trim
SET "tmp=%~1"
:trimlead
IF NOT "%tmp:~0,1%"==" " GOTO :EOF
SET "tmp=%tmp:~1%"
GOTO trimlead
:process
SET /A linenum+=1
IF "%linenum%"=="1" GOTO picknames
SET ind=0
:display
IF "%fcount%"=="%ind%" (ECHO.&GOTO :EOF)
SET /A ind+=1
CALL :trim %1
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO !f%ind%!!tmp!
ENDLOCAL
SHIFT
GOTO display
:picknames
IF %1=="" GOTO :EOF
CALL :trim %1
SET /a fcount+=1
SET "f%fcount%=%tmp%"
SHIFT
GOTO picknames
This batch scipt:
accepts one parameter, the name of the file to process;
does not verify the presence of : at the end of a header token, and when the values are displayed they are placed immediately after the corresponding header tokens;
trims all the leading spaces (but not the trailing ones);
considers the first row to be the header row, which also defines the number of tokens to process in subsequent rows;
supports up to 10 tokens, and the two areas highlighted in bold italics are responsible for that (so when you need to change the maximum number, modify both areas: if you increase the number, you must expand the "%%a" "%%b" "%%c" … list, and, likewise, if you decrease the number, then shrink the list).
I know this is an old question, but this type of question is my favorite one so here it is my answer:
#echo off
setlocal EnableDelayedExpansion
rem Create heading array:
set /P headingRow=< %1
set i=0
for %%h in (%headingRow%) do (
set /A i+=1
set heading[!i!]=%%~h
)
rem Process the file:
call :ProcessFile < %1
exit /B
:ProcessFile
set /P line=
:nextLine
set line=:EOF
set /P line=
if "!line!" == ":EOF" goto :EOF
set i=0
for %%e in (%line%) do (
set /A i+=1
for %%i in (!i!) do echo !heading[%%i]!%%~e
)
goto nextLine
exit /B
This program have not any limit in the number of fields. This version requires to enclose in quotes the elements that may have spaces or other Batch delimiters, but this restriction may be easily fixed.
Python makes this so easy it should be regulated by the government.
from csv import DictReader
with open('file', 'rb') as file:
reader = DictReader(file)
for line in reader:
for field in reader.fieldnames:
print '{0}{1}'.format(field.strip(), line[field].strip())
print '\n'
Edit: I guess you need something native to the Windows command shell. Oh well.

Resources