Search through CSV and calculate and rewrite file

100questions
100questions used Ask the Experts™
on
I would like to modify a CSV file found at ..

C:\users\user\Desktop\file.csv
A sample of the data is found below.
However there could be thousands of lines.


"Data1","Qty1","Qty2"
"ABC1234","0","0"
"ZDG123","525012","0"
"JKL432","0","0"
"TYI00121-1","0","0".
"No of Items","4",""


I want to modify it using a Script so that it searches through the file and when it sees ZDG123, or any other numbers that I will specify in position 1, that it will take the Quantity showing and divide it by 1000, and then re-write the file with the correct changed data.   For example for line shown above as:  "ZDG123","525012","0",  it will then become:   "ZDG123","525.01","0"

Would this be possible, using a scipt?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2018
Distinguished Expert 2018

Commented:
You have PowerShell in your topics, so, yes, of course it's possible ;)
This allows you to divide Qty1 by any number depending on Data1, as specified in the hash table $divide
$inFile = 'D:\Temp\file.csv'
$outFile = 'D:\Temp\file_out.csv'
$divide = @{
	'ZDG123' = 1000
}
Import-Csv -Path $inFile | ForEach-Object {
	If ($divide.Keys -contains $_.Data1) {
		$_.Qty1 = [math]::Round((([int]$_.Qty1) / $divide[$_.Data1]), 2).ToString('0.00')
	}
	$_
} | Export-Csv -NoTypeInformation -Path $outFile

Open in new window

Top Expert 2014

Commented:
You can also use ADODB to run an UPDATE query to change this CSV file directly.

Update file.csv
Set Qty1 = Qty1 / 1000
Where Data1 = "ZDG123"

Open in new window

The process can be accomplished with the following simple batch file

@echo off
setlocal enabledelayedexpansion

>file.tmp (
  for /f "tokens=1,2,3* delims=," %%a in (file.csv) do (
    set b=%%~b
    if /i "%%~a"=="%~1" if not "!b:~-3,1!"=="." set b=!b:~0,-2!.!b:~-2,2!
    echo %%a,"!b!",%%c
  )
)

move /y file.tmp file.csv >nul

echo Process complete.

Open in new window

Call the batch file in DOS passing it the Data1 value as a parameter on the command line, eg:

BATCH zdg123

Where BATCH is the name of your batch file program and zdg123 is the Data1 value passed as a parameter. As you can see, it is NOT case-sensitive.

A safeguard mechanism is included to protect against corruption should you process file.csv multiple times. This is handy where you intend to update file.csv by adding more lines and then processing it again - only the new lines will be affected.
Learn SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

Almost identical program as my previous one above. In this version, a count of values changed is displayed at the end of the program.


@echo off
setlocal enabledelayedexpansion
set count=0

>file.tmp (
  for /f "tokens=1,2,3* delims=," %%a in (file.csv) do (
    set b=%%~b
    if /i "%%~a"=="%~1" if not "!b:~-3,1!"=="." (
      set b=!b:~0,-2!.!b:~-2,2!
      set /a count=!count!+1
    )
    echo %%a,"!b!",%%c
  )
)

move /y file.tmp file.csv >nul

if %count%==1 (
  echo %count% occurance of %~1 was updated.
) else (
  echo %count% occurances of %~1 were updated.
)

Open in new window

Count displayed at end of program

Author

Commented:
@aikimark - thanks very much for this, however how do I run this, is it in a .bat file?

Author

Commented:
@oBdA - thanks very much.. can I also add more numbers just below the 'ZDG123' = 1000...

for example:
'ZDG123' = 1000
'ZDG124' = 1000
'123-1125' = 1000
'HIJ126' = 1000
'ABC127' = 1000

Thanks again.
Most Valuable Expert 2018
Distinguished Expert 2018

Commented:
Yes; if it's really always the same number (1000) for every selected value in Data1, this could be turned into a simple array instead of a hash table, but I thought you might need some flexibility there.

Author

Commented:
@oBdA - perfect, let me add more number in and test it.. and I'll get right back to you.. many thanks.
Most Valuable Expert 2018
Distinguished Expert 2018

Commented:
Can't reproduce. You saw that it's writing the output to a new file?
$inFile = 'D:\Temp\file.csv'
$outFile = 'D:\Temp\file_out.csv'
$divide = @{
	'ZDG123' = 1000
	'ZDG124' = 1000
	'123-1125' = 1000
	'HIJ126' = 1000
	'ABC127' = 1000
}
Import-Csv -Path $inFile | ForEach-Object {
	If ($divide.Keys -contains $_.Data1) {
		$_.Qty1 = [math]::Round((([int]$_.Qty1) / $divide[$_.Data1]), 2).ToString('0.00')
	}
	$_
} | Export-Csv -NoTypeInformation -Path $outFile

Open in new window

Author

Commented:
@Paul Tomasi - thanks very much Mr. Tomasi, I tried your script and it work, however it does not divide by 1000
For example 467499 should be 467.50 after it's divided by 1000
But your script returns the number 4674.99
Can this be modified?
Otherwise it works great.

Author

Commented:
@oBDA - I think I know why it's now working.
The headings are different in the actual file..
Instead of:

"Data1","Qty1","Qty2"

how would you modify the script to look for..

"Data1 Info","Qty1 Heading","Qty2 Heading"

Thanks again and sorry for any confusion.
Most Valuable Expert 2018
Distinguished Expert 2018
Commented:
Well, wherever you find "Data1" and "Qty1" ... ("Qty2" isn't used); just needs to be enclosed in quotes, since the new ones contain spaces.
$inFile = 'D:\Temp\file.csv'
$outFile = 'D:\Temp\file_out.csv'
$divide = @{
	'ZDG123' = 1000
	'ZDG124' = 1000
	'123-1125' = 1000
	'HIJ126' = 1000
	'ABC127' = 1000
}
Import-Csv -Path $inFile | ForEach-Object {
	If ($divide.Keys -contains $_."Data1 Info") {
		$_."Qty1 Heading" = [math]::Round((([int]$_."Qty1 Heading") / $divide[$_."Data1 Info"]), 2).ToString('0.00')
	}
	$_
} | Export-Csv -NoTypeInformation -Path $outFile

Open in new window

Author

Commented:
@oBdA - works great thanks so much.
Top Expert 2014

Commented:
I'll have to get an example from my other laptop when I return home.  It will look something like this:

Sub Q_29126981()
    Dim cn As Object
    Set cn = CreateObject("adodb.connection")
    cn.Open "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=C:\users\user\Desktop\;" & _
            "Extensions=asc,csv,tab,txt;"
    cn.Execute "Update file.csv " & _
                "Set Qty1 = Qty1 / 1000 " & _
                "Where Data1 In ('ZDG123','ZDG124','123-1125','HIJ126','ABC127')"
    cn.Close
End Sub

Open in new window

Top Expert 2014

Commented:
in a .cmd file run
cscript aikimark.vbs

assuming that's what you have named your VBS file.
100questions

Batch file is modified as requested.    

@echo off
setlocal enabledelayedexpansion

set count=0
del file.tmp 2>nul

for /f "tokens=1,2,3* delims=," %%a in (file.csv) do (
  set b=%%~b

  if /i "%%~a"=="%~1" if not "!b:~-3,1!"=="." (
    if !b:~-1! geq 5 set /a b+=10
    set b=!b:~0,-3!.!b:~-3,2!
    set /a count+=1
  )

  echo %%a,"!b!",%%c>>file.tmp
)

move /y file.tmp file.csv >nul

if %count%==1 (
  echo %count% occurance of %~1 was updated.
) else (
  echo %count% occurances of %~1 were updated.
)

Open in new window

Original CSV file with added 5th line.
DOS showing two updates occurred.
Updated CSV file.
DOS no additional lines added so no further updates performed.
CSV file. No additional lines added so no further updates.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial