Perl :: Aufgabe #9
2 Lösungen

CSV-Datei auslesen und aus den Daten eine Tabelle und ein Diagramm erzeugen (CGI)
Fortgeschrittener - Perl
von Gustl
- 16.08.2012 um 10:36 Uhr
Erstellen Sie ein Perl- Skript, das die angehängte CSV- Datei (EUR/USD Dollarkurs von einem Jahr) in ein Array einliest. Diese eingelesenen Werte sollen in einer Tabelle dargestellt werden und sich in einem Linien-, Punkt- oder Balkendiagramm widerspiegeln.
In dem Diagramm soll nur jeder vierte Wert ausgegeben werden.
Das Datum soll in folgendes Format konvertiert werden: DD.MM.YY (16.08.12)
In dem Diagramm soll nur jeder vierte Wert ausgegeben werden.
Das Datum soll in folgendes Format konvertiert werden: DD.MM.YY (16.08.12)
Lösungen:

#!/usr/bin/perl use strict ; use warnings ; use GD::Graph::lines ; #I first downloaded the data file open ( FH , "<" , "dataset.txt" ) or die "Can't open the data file!\n" ; my @dataset ; while ( <FH> ) { chomp ; next unless $_ =~ /\d+/ ; #don't read in the column headers my @data = split ( ";" , $_ ) ; for my $i ( 0..$#data ) { push ( @{$dataset[ $i ]} , $data[ $i ] ) ; } } close( FH ) ; #we now want to reverse the data sets to reflect the chronological order for my $i ( 0..$#dataset ) { my @reversed = reverse @{$dataset[ $i ]} ; @{$dataset[ $i ]} = @reversed ; } #convert the data values into the desired format @{$dataset[ 0 ] } = map { my ( $year , $month , $day ) = split ( /-/ , $_ ) ; join ( "." , $day , $month , substr ( $year , -2 ) ) } @{$dataset[ 0 ] } ; #convert the data into numbers with a decimal point for my $i ( 1..4 ) { map { $_ =~ s/,/\./ } @{$dataset[ $i ] } ; } #for the y axis numbering, find the smallest and the greatest data value my $datapointnumber = @{$dataset[ 0 ] } ; my $graph = GD::Graph::lines->new( 1600 , 400 ) ; $graph->set( x_label => "Datum" , y_label => "Kurse( Start , höchster, tiefster, Schluss )" , title => "Kursentwicklung des Euro zum Dollar von 2011 bis 2012" , y_tick_number => 5 , y_number_format => \&y_format , x_label_skip => 4 , x_labels_vertical => 1 , transparent => 0 , ) ; $graph->set_legend( "Startkurs" , "Hoechstkurs" , "Tiefstkurs" , "Abschlusskurs" ) ; my $gd = $graph->plot( \@dataset ) or die $graph->error ; open ( IMG , '>exchange.png' ) or die $! ; binmode IMG ; print IMG $gd->png ; close IMG ; sub y_format { my $value = shift ; $value =~ s/\./,/ ; return $value . "0" . " \$" ; }
Meine Lösung ist vielleicht nicht ganz ausgereift, aber es kann zumindest ein dynamisches Diagramm erstellt werden, welches mit canvas/javascript dargestellt wird. Beim Datum habe ich das Jahr noch abgeschnitten.
Perl-Code
Hier ein Link zur Ausgabe (Diagramm + Tabelle)

#!/usr/bin/perl use strict; use POSIX; my ($one_ref_array, $two_ref_array) = read_csv_file( "eur_usd1.csv"); my @array_table = @$one_ref_array; my @array_diagramm = @$two_ref_array; make_html_table( \@array_diagramm, "tabelle.html"); make_html_diagramm( \@array_diagramm, "diagramm.html", 400, 800); # Liest die csv-datei ein # 1. Paramter: Pfad von csv-Datei # 2. Paramter: jeder x Eintrag wird dem Array für das Diagramm hinzugefügt sub read_csv_file{ my $csv_file = $_[0]; my (@arr_table, @arr_diagramm); my @head_table; my $i = 0; # csv-Datei öffnen open(CSV,$csv_file) or die "Die Datei $csv_file konnte nicht geoeffnet werden."; while(<CSV>){ # Spaltennamen auslesen if($i == 0){ chomp($_); @head_table = split(/;/,$_); } # Inhalt auslesen und in table-array und diagramm-array hinzufügen else{ chomp($_); my @line_array = split(/;/,$_); my $hash_ref; for(my $x = 0; $x < scalar(@line_array); $x++){ if($head_table[$x] eq "Datum"){ $hash_ref->{$head_table[$x]} = convert_datum($line_array[$x]); } else{ $hash_ref->{$head_table[$x]} = $line_array[$x]; $hash_ref->{$head_table[$x]} =~ tr/,/\./; } } push( @arr_table, $hash_ref ); push( @arr_diagramm, $hash_ref ); } $i++; } close CSV; return (\@arr_table, \@arr_diagramm); } sub make_html_table{ my @arr_diagramm = @{$_[0]}; my $file = $_[1]; open(TABLE_HTML,">".$file); print TABLE_HTML " <!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'> <html xmlns='http://www.w3.org/1999/xhtml'> <head> <meta http-equiv='Content-Type' content='text/html; charset=utf-8' /> <link rel='stylesheet' href='style.css' type='text/css' /> </head> <body>"; print TABLE_HTML "<table cellspacing='1' cellpadding='0'><tr>"; my $hashref = $arr_diagramm[0]; for my $key_th (keys %$hashref) { print TABLE_HTML "<th>".$key_th."</th>"; } print TABLE_HTML "</tr>"; foreach (@arr_diagramm){ print TABLE_HTML "<tr>"; for my $key (keys %$_) { print TABLE_HTML "<td>".$_->{$key}."</td>"; } print TABLE_HTML "</tr>"; } print TABLE_HTML "</table></body></html>"; close TABLE_HTML; } sub convert_datum{ my $datum = shift; my @date_array = split(/-/,$datum); my $year = substr($date_array[0],2,2); return $date_array[2].".".$date_array[1].".".$year; } sub make_html_diagramm{ my @arr_diagramm = @{$_[0]}; my $file = $_[1]; my $hoehe = $_[2]; my $width = $_[3]; my $x_hoehe_bedarf_top = 50; my $y_breite_bedarf_left = 20; my $x_hoehe_bedarf = 40; my $y_breite_bedarf = 70; my $hoehe_diagramm = $hoehe - $x_hoehe_bedarf - $x_hoehe_bedarf_top; my $width_diagramm = $width - $y_breite_bedarf - $y_breite_bedarf_left; # max und min werte ermitteln my $max_wert = get_max(\@arr_diagramm); my $min_wert = get_min(\@arr_diagramm); # differnez von max und min werte my $diff = floor(($max_wert - $min_wert) * 1000); # y schritt (zb. schritt = 6, pro 6px +1 wert) my $schritt = $hoehe_diagramm / $diff; # x schritt my $schritt_left = $width_diagramm / scalar(@arr_diagramm); my $left = 0 - $y_breite_bedarf_left; foreach(@arr_diagramm){ $left += $schritt_left; $_->{x} = $left; print "\nleft: ".$left; $_->{y} = ($_->{Schlusskurs} - $min_wert) * 1000 * $schritt; if($_->{Datum} =~ /(.{6})/){ $_->{Datum} = $1; } } # Beschriftungen und Hintergrundlinien # y-achse my $y_desc_abstand = $hoehe_diagramm / 25; for(my $i = $hoehe_diagramm; $i >= 1; $i-- ){ if( ($hoehe_diagramm / $i) >= $y_desc_abstand){ $y_desc_abstand = $i; last; } } my $y_desc_anzahl = $hoehe_diagramm/$y_desc_abstand; my $step_y_desc = $diff/$y_desc_anzahl; $y_desc_anzahl = floor($y_desc_anzahl); my @y_desc; foreach(0..($y_desc_anzahl)){ my $hashref; $hashref->{desc} = int( (($min_wert*1000) + ($step_y_desc * $_)))/1000; $hashref->{y} = $hoehe_diagramm + $x_hoehe_bedarf_top - ($y_desc_abstand * $_); print $hashref->{y}." ".$hashref->{desc}."\n"; push(@y_desc, $hashref); } # x-achse my $x_teiler = ($width - $y_breite_bedarf_left) / scalar(@arr_diagramm); print "\nteiler vor: ".$x_teiler; for(my $i = 1; $i >= 1; $i *= 2){ if($x_teiler >= (50/$i) ){ $x_teiler = $i; last; } } print "\nteiler nach: ".$x_teiler; print "\nMin: ".$min_wert; print "\nMax: ".$max_wert; print "\nSchritt: ".$schritt; print "\nDiff: ".$diff; print "\ndesc_anzahl: ".$y_desc_anzahl; print "\ndesc_abstand: ".$y_desc_abstand; print "\nstep_y_desc: ".$step_y_desc; print "\nscalar: ".scalar(@arr_diagramm); print "\nhoehe_diagramm: ".$hoehe_diagramm; open(DIAGRAMM_HTML,">".$file); print DIAGRAMM_HTML "<!doctype html> <html lang='de' xml:lang='de'> <head> <meta charset='utf-8'> <link rel='stylesheet' href='style.css' type='text/css' /> </head> <body> <canvas id='canvasLinie' width='".($width)."' height='".($hoehe)."' style='border: 1px solid silver;'> ein Browser unterstützt das Canvas-Element nicht. </canvas> <script type='text/javascript'> var canvas = document.getElementById('canvasLinie'); var context = canvas.getContext('2d'); context.lineWidth = 0.4;"; for(my $i = 0; $i < scalar(@arr_diagramm); $i++ ){ if($i == 0){ print DIAGRAMM_HTML "context.moveTo(".(($width-$y_breite_bedarf)-$arr_diagramm[$i]->{x}- $y_breite_bedarf_left).", ".(($hoehe-$x_hoehe_bedarf)-$arr_diagramm[$i]->{y}).");"; } else{ print DIAGRAMM_HTML "context.lineTo(".(($width-$y_breite_bedarf)-$arr_diagramm[$i]->{x}- $y_breite_bedarf_left).", ".(($hoehe-$x_hoehe_bedarf)-$arr_diagramm[$i]->{y}).");"; } } print DIAGRAMM_HTML "context.stroke();"; print DIAGRAMM_HTML "var y_lines = canvas.getContext('2d');"; print DIAGRAMM_HTML "y_lines.lineWidth = 0.2;"; foreach(@y_desc){ print DIAGRAMM_HTML "y_lines.fillText('".$_->{desc}."',".($width-60+5).",".($_->{y}+2).");\n"; print DIAGRAMM_HTML "y_lines.moveTo(".$y_breite_bedarf_left.",".($_->{y})."); context.lineTo(".($width-60).",".($_->{y}).");\n"; } print DIAGRAMM_HTML "y_lines.stroke();"; print DIAGRAMM_HTML "var x_lines = canvas.getContext('2d');"; print DIAGRAMM_HTML "x_lines.lineWidth = 0.2;"; for(my $i = 0; $i < scalar(@arr_diagramm); $i += $x_teiler ){ print DIAGRAMM_HTML "x_lines.fillText('".$arr_diagramm[$i]->{Datum}."',".(($width-$y_breite_bedarf)- $y_breite_bedarf_left-$arr_diagramm[$i]->{x}-13).",".($hoehe-$x_hoehe_bedarf+24).");\n"; print DIAGRAMM_HTML "x_lines.moveTo(".(($width-$y_breite_bedarf)- $y_breite_bedarf_left-$arr_diagramm[$i]->{x}).",".$x_hoehe_bedarf_top.");context.lineTo(".(($width-$y_breite_bedarf)- $y_breite_bedarf_left-$arr_diagramm[$i]->{x}).",".($hoehe-$x_hoehe_bedarf+10).");\n"; } print DIAGRAMM_HTML "x_lines.stroke();"; print DIAGRAMM_HTML "var head = canvas.getContext('2d'); head.font=\"18px Verdana\"; head.fillText(\"Euro / US Dollar - Kurs (EUR/USD)\", 30, 33);</script> </body> </html>"; close DIAGRAMM_HTML; } sub get_max{ my @arr_diagramm = @{$_[0]}; my $max = 0; foreach(@arr_diagramm) { $max = $_->{Schlusskurs} if ($_->{Schlusskurs} > $max && $_->{Schlusskurs} > 0 ); } return $max; } sub get_min{ my @arr_diagramm = @{$_[0]}; my $min = 10000; foreach(@arr_diagramm) { $min = $_->{Schlusskurs} if ($_->{Schlusskurs} < $min && $_->{Schlusskurs} > 0 ); } return $min; }
Hier ein Link zur Ausgabe (Diagramm + Tabelle)