Forum Moderators: coopster

Message Too Old, No Replies

A more efficient way to do this?

parsing comma delimited file

         

flapane

11:29 am on Feb 4, 2012 (gmt 0)

10+ Year Member



Hi,
I want to parse this csv file containing the latest temperature data (auto-updated every 10 minutes), and grab only the more recent temperature value (which is at the last line of the file).
INPUT: [wunderground.com...]
OUTPUT (code shown below): [flapane.com...]

I tried to use fgetcsv(), but I only managed to display the latest row and NOT latest line.
So I decided to handle it as a text file, and came up with:

<?
$expiretime=12; // cache expire time in minutes
$cachename="csv.txt"; //name of the cachefile

//create the cachefile if it doesn't exist
if (!file_exists($cachename)) {
$create = fopen($cachename, 'w');
chmod ("$cachename", 0777); //set chmod 777
fclose($create);
}


// Is the file older than $expiretime, or the file is new/empty?
$FileAge = time() - filectime($cachename);// Calculate file age in seconds
if ($FileAge > ($expiretime * 60) || 0 == filesize($cachename))
{
// Now refresh the cachefile with newer content
$handle = fopen("http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=INANAPOL3&format=1", "r+");
$content = stream_get_contents($handle);
fclose($handle);
$handle = fopen($cachename, 'wb');
fwrite($handle,$content);
}

// seconds to minutes - from http://forums.#*$!.com
$minutes = floor($FileAge/60);
$secondsleft = $FileAge%60;
if($minutes<10)
$minutes = "0" . $minutes;
if($secondsleft<10)
$secondsleft = "0" . $secondsleft;

// Disdplay most recent temperature
$display = file($cachename);
$count = count($display);
for($i=$count - 2; $i<$count-1; $i++){
//echo $display[$i]."<br />";
$temperature = explode(',', $display[$i], 14);
echo "<b>".$temperature[1]."</b>°C (Napoli Posillipo) | ".$minutes."m".$secondsleft."s ago<br />";
}

?>


I'm curious to know if there's a more efficient way to do this (ie. copy just the last line of the csv file in the cachefile).
According to microtime(), the execution time now is 0.00015secs.
Thanks

penders

12:51 pm on Feb 4, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



By the look of your source data, commas don't appear in the data itself, so you don't enclose field values in quotes. So there is probably no need to use fgetcsv() anyway.

Rather than manually opening and reading in your file you could use file() [uk3.php.net] which reads the entire file into an array (1 line per array element) in one go. Then it's a simple case of getting the last element of that array...

$data = file(/* filename */)
$lastRow = end($data)
...and then explode()

But you appear to be doing something like this anyway in the last part of your code? Why not apply the same principle in the first part? Your data file seems to be quite small. I would have said that perhaps the 'slow part' is probably the fact that the source data appears to be on another domain? But 0.00015secs is already pretty quick!

flapane

2:28 pm on Feb 4, 2012 (gmt 0)

10+ Year Member



Thanks, it worked!
Now my cachefile (yep, the main concern is that the data comes from an external domain) contains only the last line of the csv file, and the execution time dropped to 0.00011s and less (8^10-5s).
I couldn't use end() and then explode() because I noticed that the very last line of the file is an empty line (so we're going to consider the line BEFORE the last line).


$datawu = file('http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=INANAPOL3&format=1');
$count2 = count($datawu);
for($i=0; $i<$count2-1; $i++){
$lastRow = $datawu[$i];
}
$handle = fopen($cachename, 'wb');
fwrite($handle,$lastRow);



$display = file($cachename);
$count = count($display);
for($i=0; $i<$count; $i++){
$temperature = explode(',', $display[$i], 14);
echo "<b>".$temperature[1]."</b>°C (Napoli Posillipo) | ".$minutes."m".$secondsleft."s ago<br />";
}

penders

4:02 pm on Feb 4, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



... the very last line of the file is an empty line...


You can probably correct this in the call to file() - have a look at the 2nd argument and try passing the flag FILE_SKIP_EMPTY_LINES. If the data is reliable then this might be all that's required, or you could search backward for the last valid record?

If your cachefile now only contains 1 record (the last line), then you can potentially simplify your last part...

$display = file($cachename); 
$count = count($display);
for($i=0; $i<$count; $i++){
$temperature = explode(',', $display[$i], 14);
echo "<b>".$temperature[1]."</b>°C (Napoli Posillipo) | ".$minutes."m".$secondsleft."s ago<br />";


to something like....

$record = file_get_contents($cachename); 
$temperature = explode(',', $record, 14);
echo "<b>".$temperature[1]."</b>°C (Napoli Posillipo) | ".$minutes."m".$secondsleft."s ago<br />";


Also look at file_put_contents() [uk3.php.net] for saving your cache file.

Also, I would have thought you should be checking filemtime() [uk3.php.net] rather than filectime() in order to check the last modified date/time of your cache file. Maybe it doesn't matter?

flapane

4:55 pm on Feb 4, 2012 (gmt 0)

10+ Year Member



Thanks, the flag FILE_SKIP_EMPTY_LINES helped skipping the blank lines!

I added a second weather station, so the cachefile now contains two lines (and the script also calculates the averague value) (output: [flapane.com...] ).

I googled for file_put_contents() and I didn't understand if the wrapper actually helps saving time instead of doing fopen/fwrite/fclose.

You're right, filemtime() is technically more correct in this case.

g1smd

7:50 pm on Feb 4, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



When I needed to read a CSV file into a PHP array I used a pre-written module
parsecsv-for-php
to get up and running very quickly. That left me to just juggle the data around and select the particular wanted entries.

flapane

3:02 pm on Feb 7, 2012 (gmt 0)

10+ Year Member



Thanks, I'll try it with some csv file with more data this.

As a sidenote, I noticed that sometimes one of the stations is not reporting (ie. PC shut down, electricity gone and so on), so:

if(empty ($temperature1[1])){
$temperature1[1] = "N/A";
$temp_avg = "N/A";
}

if(empty ($temperature2[1])){
$temperature2[1] = "N/A";
$temp_avg = "N/A";
}