• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

Massenänderung einer CSV-Datei

Heart

Hacker
Hi,

ich habe eine csv-Datei, die so aussieht:

Code:
18.02.2005,05:05,1.3066,1.3068,1.3065,1.3066,1
18.02.2005,05:10,1.3068,1.3072,1.3065,1.3068,21
.....usw.

Der zweite Wert ist also die Zeit.

Wie kann ich mittels einem Skript bei allen Zeilen dieser Datei die Zeit um 1 Stunde zurücksetzen?

Die Datei sollte also im obigen Bsp dann so aussehen:

Code:
18.02.2005,04:05,1.3066,1.3068,1.3065,1.3066,1
18.02.2005,04:10,1.3068,1.3072,1.3065,1.3068,21
.....usw.

Vielen Dank
 

regexer

Advanced Hacker
Hallo!
Die Lösung könnte ziemlich kompliziert aussehen. Wenn man z.B. als Zeit "01.03.2005,00:45" hat, muss das Programm schlau genug sein und "28.02.2005,23:45" ausgeben. Wenn man es ganz sauber machen will, muss man sogar einen eventuellen Jahreswechsel bzw. Schaltjahrberechnung in betracht ziehen.

Das alles kann man aber umgehen, indem man auf das Computer-interne Datumsformat (Sekunden seit 1. Jan 1970) umwandelt, anschließend 3600 Sekunden abzieht und das Ergebnis wieder in das "normale" Format zurückwandelt. In der Scriptsprache Perl sieht dann die Lösung so aus:

Code:
#!/usr/bin/perl

use Time::Local;

while (<>) {
  # Aus der Eingabezeile das Datum holen
  ($day, $mon, $year, $hour, $min)=(split /[.,:]/)[0..4];

  # in Sekunden umwandeln
  $year-=1900;
  $time = timelocal(0,$min,$hour,$day,$mon,$year);

  # minus 3600 Sekunden
  $time-=3600;

  # zurueckwandeln
  ($n_min, $n_hour, $n_day, $n_mon, $n_year) =(localtime($time))[1..5];
  $n_year+=1900;

  # die ersten 16 Zeichen der Eingabezeile loeschen
  s/^.{16}//;

  # Datum formatiert ausgeben
  printf "%02d.%02d.%04d,%02d:%02d",
         $n_day, $n_mon, $n_year, $n_hour, $n_min;

  # Rest der Zeile ausgeben
  print;
}
Der Aufruf funktioniert wiefolgt:
Code:
script.perl <eingabe.csv >ausgabe.csv
 

regexer

Advanced Hacker
Vorsicht! Fehler!
Ich muss mein Script korrigieren, da die funktionen timelocal bzw. localtime die Monate bei 0 beginnend zählen. (Januar ist Monat Nr. 0 und nicht Monat 1). Bitte also folgende Version nehmen:
Code:
#!/usr/bin/perl

use Time::Local;

while (<>) {
  # Aus der Eingabezeile das Datum holen
  ($day, $mon, $year, $hour, $min)=(split /[.,:]/)[0..4];

  # in Sekunden umwandeln
  $mon--;
  $year-=1900;
  $time = timelocal(0,$min,$hour,$day,$mon,$year);

  # minus 3600 Sekunden
  $time-=3600;

  # zurueckwandeln
  ($n_min, $n_hour, $n_day, $n_mon, $n_year) =(localtime($time))[1..5];
  $n_mon++;
  $n_year+=1900;

  # die ersten 16 Zeichen der Eingabezeile loeschen
  s/^.{16}//;

  # Datum formatiert ausgeben
  printf "%02d.%02d.%04d,%02d:%02d",
         $n_day, $n_mon, $n_year, $n_hour, $n_min;

  # Rest der Zeile ausgeben
  print;
}
Sorry!
 
OP
H

Heart

Hacker
Danke an ZaB|SHC| (#linux-club.de Chat), hier die Lösung für die Nachwelt:

Code:
#!/usr/bin/perl

use Time::Local;

while (<>) {
  # Aus der Eingabezeile das Datum holen
  $line = $_;
  ($year_month_day, $hour_min_second)=(split /,/, $line)[2..3];
  $year_month_day =~ /(\d{4})(\d{2})(\d{2})/;
  $year = $1 - 1900;
  $mon = $2 -1;
  $day = $3;
  $hour_min_second =~ /(\d{2})(\d{2})/;
  $hour = $1;
  $min = $2;

  # in Sekunden umwandeln
  $time = timelocal(0,$min,$hour,$day,$mon,$year);

  # minus 3600 Sekunden
  $time-=300;

  # zurueckwandeln
  ($n_min, $n_hour, $n_day, $n_mon, $n_year) =(localtime($time))[1..5];
  $n_mon++;
  $n_year+=1900;

  # die ersten 16 Zeichen der Eingabezeile loeschen
  $line =~ s/$year_month_day/$n_year . sprintf('%02d', $n_mon) . sprintf('%02d',$n_day)/e;
  $line =~ s/$hour_min_second/sprintf('%02d', $n_hour) . sprintf('%02d', $n_min) . '00'/e;

  # Rest der Zeile ausgeben
  print $line;
}
 
Oben