AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

simplified csv operations.
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Tue Jun 17, 2008 11:35 pm    Post subject: simplified csv operations. Reply with quote

credits:
HugoV (for all bug reports and suggestions.)
Rhys (for his Format4CSV() beta 1 function.)


Format4CSV() is included with this code.
Code:
;+--------------+
;|function list.|
;+--------------+
/*
set_CSV(r_file, r_header=0, r_delimiter=",")
finalize_CSV(copy=0)
CSV_totalRows()
CSV_totalCols()
CSV_readCell(row = 1, column = 1)
XLS_readCell(range)
CSV_find(value, row=0, column=0, offset_x=0, offset_y=0)
CSV_exists(value)
CSV_sort(column=1, order="asc")
CSV_writeCell(row, column, value)
XLS_writeCell(range, value)
CSV_fileName()
CSV_filePath()
CSV_fullName()
*/
setBatchLines -1

set_CSV(r_file, r_header=0, r_delimiter=",") {
   global

   r_delimiter = %r_delimiter%
   r_header = %r_header%
   r_row = 0
   r_col = 0
   
   if r_delimiter = ,
      r2_delimiter = CSV
   else
      r2_delimiter = %r_delimiter%

   SplitPath r_file, r_name, r_path, r_ext, r_nameNoExt
   
   if(r_path = "")
      r_path = %A_WorkingDir%

   fileRead r_file, %r_file%
   
   if (errorLevel) {
      MsgBox unable to read the file
      return %errorLevel%
   }

   loop parse, r_file, `n
   {
      r_row++
      loop parse, A_LoopField, %r2_delimiter%
      {
         if(r_row = 1)
            r_col++

         __matrix%r_row%_%A_Index% := regExReplace(A_LoopField, "\R?$")

         if (A_Index = 1)
            __line%r_row% := Format4CSV(__matrix%r_row%_%A_Index%)
         else
            __line%r_row% := __line%r_row% . r_delimiter . Format4CSV(__matrix%r_row%_%A_Index%)
      }
   }
   r_fullName := CSV_fullName()
   CSV_totalRows()
   CSV_totalCols()
return
}


CSV_totalRows() {
   global
   
   r_lastRow := r_row - 1

return %r_lastRow%
}

CSV_totalCols() {
   global
   
   r_lastCol = %r_col%
   
return %r_lastCol%
}

CSV_readCell(row = 1, column = 1) {
   global

   if (r_header)
      row++

return % __matrix%row%_%column%           ;%
}

XLS_readCell(range) {
   global

   if (r_header)
      row++
      
   StringLeft column, range, 1
   StringReplace row, range, %column%
   StringUpper column, column
   column := ASC(column) - 64
   
return % __matrix%row%_%column%           ;%
}

CSV_writeCell(row, column, value) {
   global

   if (r_header)
      row++

   regExReplace(__line%row%, __matrix%row%_%column%, Format4CSV(value))
   __matrix%row%_%column% := Format4CSV(value)
   r_updated := false

return
}

XLS_writeCell(range, value) {
   global

   if r_headerExists = 1
      row++
      
   StringLeft column, range, 1
   StringReplace row, range, %column%
   StringUpper column, column
   column := ASC(column) - 64

   regExReplace(__line%row%, __matrix%row%_%column%, Format4CSV(value))
   __matrix%row%_%column% := Format4CSV(value)
   r_updated := false

return
}

CSV_fileName() {
   global
   return %r_name%
}
CSV_filePath() {
   global
   return % r_path . "\"                    ;%
}

CSV_fullName() {
   global
   return % r_path . "\" . r_name           ;%
}

CSV_find(value, row=0, column=0, offset_x=0, offset_y=0) {
   global

   i = 0
   ;full search
   if (row = 0 && column = 0) {
      loop %r_lastRow% {
         i++
         loop %r_lastCol% {
            if (__matrix%i%_%A_Index% = value)
               return % i + offset_x . "," . A_Index + offset_y                ;%
         }
      }
   }

   ;search specified row
   if (row != 0 && column = 0) {
      loop %r_lastCol% {
         if (value = __matrix%row%_%A_Index%)
            return % A_Index + offset_y                                         ;%
      }
   }

   ;search specified column
   if (row = 0 && column != 0) {
      loop %r_lastRow% {
         if (value = __matrix%A_Index%_%column%)
            return % A_Index + offset_x                                         ;%
      }
   }
return false
}

CSV_exists(value) {
   global

   if (InStr(r_file, value . "`,"))
      return true
   else
      return false
}

CSV_sort(column=1, order="") {
   global
   r_file =
   i = 0

   if (order = "desc")
      order = R

   loop %r_lastRow% {
      If __matrix%A_Index%_%column% is not number
      {
         StringUpper sortBy, __matrix%A_Index%_%column%
         sortBy := ASC(sortBy) + 1000000000000   
      }
      else
         sortBy := __matrix%A_Index%_%column%

      __line%A_Index% := sortBy . r_delimiter . __line%A_Index% . "`n"
      r_file := r_file . __line%A_Index%
   }
   Sort r_file, N %order%

   ;reload the array
   loop parse, r_file, `n
   {
      i++
      x = 0
      loop parse, A_LoopField, %r2_delimiter%
      {
         if (A_Index = 1)
         continue

         x++
         __matrix%i%_%x% := A_LoopField

         if (x = 1)
            __line%i% := Format4CSV(__matrix%i%_%x%)
         else
            __line%i% := __line%i% . r_delimiter . Format4CSV(__matrix%i%_%x%)
      }
   }
   r_file =
   loop %r_lastRow%
      r_file := r_file . __line%A_Index% . "`n"
      
   r_updated := true

return
}

finalize_CSV(copy=0) {
   global

   if (copy = 1) {
      r_fullName = %r_path%\%r_nameNoExt%_copy.%r_ext%
      ifExist %r_fullName%
         fileDelete %r_fullName%
   }
   else
      fileDelete %r_fullName%

   if(r_updated = "false") {
      loop %r_lastRow%
         r_file := r_file . __line%A_Index% . "`n"
   }
   
   fileAppend %r_file%, %r_fullName%
return
}

;author Rhys.
;[Function] Convert String for CSV - Format4CSV() beta 1
;can be found at http://www.autohotkey.com/forum/viewtopic.php?t=27233
Format4CSV(F4C_String) {
   Reformat:=False                   ;Assume String is OK
   
   IfInString F4C_String,`n          ;Check for linefeeds
      Reformat:=True                ;String must be bracketed by double quotes
   IfInString F4C_String,`r          ;Check for linefeeds
      Reformat:=True
   IfInString F4C_String,`,          ;Check for commas
      Reformat:=True
   
   IfInString F4C_String, `"         ;Check for double quotes
   {   
      Reformat:=True
      StringReplace, F4C_String, F4C_String, `",`"`", All           ;The original double quotes need to be double double quotes
   }
   
   If (Reformat)
      F4C_String=`"%F4C_String%`"               ;If needed, bracket the string in double quotes

return F4C_String
}


Note, reserved variables:
r_row
r_lastRow
r_col
r_lastCol
r_headerExists
r_file
r_name
r_path
r_ext
r_nameNoExt
r_delimiter

(2 underscores) __matrix%dim1%_%dim2% - is a reserved array


Functions:
set_CSV(r_file, r_header=0, r_delimiter=",")
whenever you use this function, you write CSV file into an array.
From this point on you are now working with array, not the file itself.


Parameters:
file: file to be read
r_header: make it 1(true) if you have headers (0 is default)
r_delimiter: A_Tab or A_Space (coma is default)
Code:
set_CSV("example.csv", 1)

the following will read "example.csv" into an array, first line is considered a header.

finalize_CSV(copy=0)
use if you want to apply written changes to file
Parameters: make it as 1 to apply changes to file copy which will be created in the same directory as the original

CSV_totalRows()
returns total amount of rows in file
Parameters: None


CSV_totalCols()
returns total amount of columns in file
Parameters: None


CSV_readCell(row = 1, column = 1)
reads specified cell
Parameters:
row: row to be read
column: column to be read


CSV_writeCell(row, column, value)
writes specified cell
Parameters:
row: row to be written to
column: column to be written to
value: value to be written

Note, you can only overwrite already existing cells.
Creation of new rows or new columns is not yet implemented


CSV_find(value, row=0, column=0, offset_x=0, offset_y=0)
search

Parameters:
value: what to search for
row: specify this if you know what row searched value is located in.
column: specify this if you know what column searched value is located in.
offset_x: specify this if you want to offset search result by a number of rows. Accepts positive and negative values.
offset_y: specify this if you want to offset search result by a number of columns. Accepts positive and negative values.

return values:
if row and column weren't specified in search, function returns row number followed by a column number separated with coma.

if row was specified, function returns column number
if column was specified, function returns row number


CSV_exists(value)
returns 1(true) if searched text if found, 0(false) otherwise
Parameters:
value: searched text.


CSV_sort(column=1, order="asc")
sorting
Parameters:
Column: specify what column you want to sort by.
order: leave blank for ascending sort or specify "desc" for descending sort.

CSV_fileName()
returns file name
Parameters: None


CSV_filePath()
returns file path
Parameters: None


CSV_fullName()
returns full file name(includes path)
Parameters: None

Example:
meet abc.csv it's going to be our lab rat.
Quote:

header 1,header 2,header 3
2,b,b2
3,c,c3
4,d,d4
5,e,e5

let's squeeze some info out of it:
Code:

set_CSV("abc.csv")
MsgBox % "total rows: " . CSV_totalRows() . "`ntotal columns: " . CSV_totalCols()                               
MsgBox % "B2 content: " . CSV_readCell(2, 2) . "`nC1 content: " . CSV_readCell(1, 3)                             
MsgBox % "file name: " . CSV_fileName() . "`nfile path: " . CSV_filePath() . "`nfull name: " . CSV_FullName()   

output:
Quote:

total rows: 5
total columns: 3
B2 content: b
C1 content: header 3
file name: abc.csv
file path: C:\
full name: C:\abc.csv


Here's an example with headers set to 1(true):
Code:

set_CSV("abc.csv", 1)
MsgBox % CSV_readCell(1, 3)

output:
Quote:
b2


Let's do a search:

Code:

set_CSV("abc.csv")

MsgBox % "d4 x,y: " . CSV_find("d4")
MsgBox % "d4 x: " . CSV_find("d4", 4)
MsgBox % "c3 y: " . CSV_find("c3", 0, 3)
MsgBox % "d offset by 1 row(back), offset 1 column: " . CSV_find("d", 0, 0, -1, 1)


output:
Quote:

d4 x,y: 4,4
d4 x: 4
c3 y: 3
d offset by 1 row(back), offset 1 column: 1,3


let's add some values to it:
Code:

set_CSV("abc.csv")
CSV_writeCell(1, 1, "Updated header")
MsgBox % CSV_readCell()

this will write come new values to abc.csv, but it wont be saved just yet, so if you open the file you wont see any changes.
Let's save our changes:
Code:

set_CSV("abc.csv")
CSV_writeCell(1, 1, "Updated header")
finalize_CSV()

Now the file is updated.

Have fun Very Happy


Last edited by neXt on Mon Aug 04, 2008 5:18 pm; edited 21 times in total
Back to top
View user's profile Send private message
shajul



Joined: 15 Sep 2006
Posts: 26
Location: India

PostPosted: Wed Jun 18, 2008 6:28 pm    Post subject: very nice Reply with quote

very nice!!

but may involve lot of read-writes to disk, if multiple ops are to be carried out! also code for creating backups may be prudent before filedelete.
_________________

Shajul.
Back to top
View user's profile Send private message Yahoo Messenger
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Thu Jun 19, 2008 3:52 am    Post subject: Reply with quote

Good idea about back up, i'll work on that.
Yeah, you are right, it is a lot of requests to disk, i though of using some "start" trigger, do all operations in memory and then write to disk, but haven't worked on it yet.
Back to top
View user's profile Send private message
Wouther



Joined: 01 May 2007
Posts: 78
Location: The Netherlands

PostPosted: Thu Jun 19, 2008 10:46 am    Post subject: Reply with quote

Thanks for posting, it can be useful. Smile
However, I remember someone coded something to count the lines of a file without a loop. Something with StringSplit, then counting the number of items in the array (useing array0) and then perhaps add 1 to it? I'll look it up.
Edit: I found it: http://www.autohotkey.com/forum/viewtopic.php?t=26146#162899 Wink
_________________
Printing css/html-formatted text
Back to top
View user's profile Send private message
n-l-i-d
Guest





PostPosted: Thu Jun 19, 2008 10:59 am    Post subject: Reply with quote

Nice. It might be good to add a "custom delimiter" option, since CSV files are not automatically always really comma-seperated files (could be semi-colon or tab)
Back to top
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Thu Jun 19, 2008 3:14 pm    Post subject: Reply with quote

n-l-i-d, thanks!
I'll abandon this btw Rolling Eyes . I noticed that heresy is working on a text file manipulation library, i think i'll try to help him out instead.
Back to top
View user's profile Send private message
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Fri Jun 20, 2008 11:01 pm    Post subject: Reply with quote

Ok, i guess it might take them a while to develop their library, so here's my update(see first post).
Now it reads file in 2 dimensional array and works with it until changes are finalized, that's when changes are applied.
Have fun.
Back to top
View user's profile Send private message
HugoV



Joined: 27 May 2007
Posts: 499

PostPosted: Fri Jun 20, 2008 11:20 pm    Post subject: Reply with quote

Hi there,

this is a correct CSV file"
Quote:
"1,1",2,3
4,5,6
7,8,9
Now try
Code:
set_CSV("simplecsv.csv")
MsgBox % CSV_totalCols()
and it says 4.

It's easily fixed if you use loop, parse, string, CSV as documented
in the helpfile. Now if you want to use another delimiter like
A_Tab for example it isn't going to work because the CSV parameter
expects a , not a TAB.

[/code]
Back to top
View user's profile Send private message
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Fri Jun 20, 2008 11:29 pm    Post subject: Reply with quote

Hmmm, nice one Very Happy . I'll fix that.
Back to top
View user's profile Send private message
HugoV



Joined: 27 May 2007
Posts: 499

PostPosted: Fri Jun 20, 2008 11:45 pm    Post subject: Reply with quote

CSV files may contain a header line. This header will contain
names corresponding to the fields. But of course it shouldn't
count when you count nor have an effect on any edit actions.
E.g. editing cell 1,1 would edit the fieldname and not the actual
cell which would be 2,1 in your case.

I pass on a extra parameter in one of my functions I'm working on:
TXT_TransposeCSV(TextFile,HeaderLine = 0)
where I can specify whether or not to account for the header line.
While parsing the file:

Code:
Loop, Parse, Str, `n, `r
{
If (A_Index = 1) AND (HeaderLine=1)
   Continue
Row:=A_Index - HeaderLine
...
Back to top
View user's profile Send private message
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Sat Jun 21, 2008 12:19 am    Post subject: Reply with quote

This is a nice suggestion, i should implement that too, thanks for your comments on this Very Happy .
I also thought of adding an additional way of reading and writing using "excel" coordinates(A1, B4 etc.) Rolling Eyes
Back to top
View user's profile Send private message
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Sat Jun 21, 2008 2:06 am    Post subject: Reply with quote

Ok, updated.
Back to top
View user's profile Send private message
HugoV



Joined: 27 May 2007
Posts: 499

PostPosted: Sat Jun 21, 2008 8:55 am    Post subject: Reply with quote

It still gives the incorrect number of columns if you try the simply
test CSV I gave above. Using CSV will give the correct answer
but in that case you can't use any other characters as delimiter
such as TAB for example.
Code:
set_CSV(r_file, r_header=0, r_delimiter=",") {
   global

   if r_header = 1
      r_headerExists = 1
   
   setBatchLines -1
   SplitPath r_file, r_name, r_path
   if(r_path = "")
     r_path = %A_WorkingDir%

   r_row = 0
   r_col = 0
   fileRead r_file, %r_file%

   loop parse, r_file, `n
   {
      r_row++
      loop parse, A_LoopField, CSV
      {
         if(r_row = 1)
         r_col++

         __matrix%r_row%_%A_Index% = %A_LoopField%
      }
   }
return
}

Also consider this write action:
Code:
CSV_writeCell(1, 2, "1,234,1234.65")
if you add that to your test CSV you will have an incorrect CSV
file because of the , in the value parameter. I suggest you have a look at
[Function]Convert String for CSV - Format4CSV() beta 1
http://www.autohotkey.com/forum/viewtopic.php?t=27233
by Rhys

Try to work with this test data:
http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm#ExampleData
I've been using Format4CSV() with the above data and it works
really well as far as I can tell.

Edit: see also http://www.autohotkey.com/forum/viewtopic.php?t=32938
Back to top
View user's profile Send private message
neXt



Joined: 19 Mar 2007
Posts: 463

PostPosted: Sat Jun 21, 2008 4:27 pm    Post subject: Reply with quote

I fixed it, thanks Very Happy !
Simple If statement did the trick.

I also added a feature, now you can apply changes to a file copy, not the original.
Back to top
View user's profile Send private message
HugoV



Joined: 27 May 2007
Posts: 499

PostPosted: Sat Jun 21, 2008 7:30 pm    Post subject: Reply with quote

neXt wrote:
Simple If statement did the trick.

Code:
if r_delimiter = ,
      delimiter = CSV
....
      loop parse, A_LoopField, %delimiter%

I thought of something similar last night, but this is a more elegant
solution Wink

Instead of
Code:
 value := Format4CSV(value)
   __matrix%row%_%column% := value
doesn't this work as well
Code:
__matrix%row%_%column% := Format4CSV(value)
Saves you a line of code ...
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group