none
Datei lesen, selektieren und schreiben

    Frage

  • Hallo

    Ich habe eine grosse Wetterdaten-Datei (wettermon.txt) welche ich auf einen Datensatz pro Stunde zusammenfassen möchte.

    Die Datei sieht folgendermassen aus:

    1;13.03.2017 01:20:49;15;21.0;53;22.7;23;1002.4;944.6;3.6;6.1;SW;0.5;22.7;0.00;0.00;0.00;0.00;354.90
    2;13.03.2017 01:35:49;15;21.2;53;22.3;27;1002.3;944.5;6.1;8.6;SW;2.4;22.3;0.00;0.00;0.00;0.00;354.90
    3;13.03.2017 01:50:49;15;21.2;53;23.0;23;1002.3;944.5;3.6;5.0;S;0.8;23.0;0.00;0.00;0.00;0.00;354.90
    4;13.03.2017 02:05:49;15;21.2;53;22.4;25;1002.6;944.8;6.1;7.2;NW;1.4;22.4;0.00;0.00;0.00;0.00;354.90
    5;13.03.2017 02:20:49;15;21.3;53;22.3;28;1002.4;944.6;3.6;5.0;W;2.9;22.3;0.00;0.00;0.00;0.00;354.90
    6;13.03.2017 02:35:49;15;21.3;53;22.9;24;1002.4;944.6;2.5;3.6;SE;1.3;22.9;0.00;0.00;0.00;0.00;354.90
    7;13.03.2017 02:50:49;15;21.3;53;22.1;27;1002.2;944.4;2.5;5.0;N;2.3;22.1;0.00;0.00;0.00;0.00;354.90
    8;13.03.2017 03:05:49;15;21.3;53;22.8;23;1002.2;944.4;2.5;3.6;NE;0.6;22.8;0.00;0.00;0.00;0.00;354.90
    9;13.03.2017 03:20:49;15;21.3;53;22.8;22;1002.1;944.3;1.1;2.5;E;-0.0;22.8;0.00;0.00;0.00;0.00;354.90
    10;13.03.2017 03:35:49;15;21.3;53;20.8;24;1002.2;944.4;0.0;1.1;S;-0.5;20.8;0.00;0.00;0.00;0.00;354.90
    11;13.03.2017 03:50:49;15;21.5;53;19.3;30;1002.2;944.4;1.1;2.5;NE;1.3;19.3;0.00;0.00;0.00;0.00;354.90

    Dazu müsste ich vom zweiten Feld (Abtrennung ;) die 12 Position (erster Datensatz=01) zwischenspeichern und dann die Folgenden Zeilen (2x01, 3x02, 3x03) mit dem gleiche Inhalt überlesen und am Ende einen neue Datei (wetterstd.txt) erhalten.

    Kann ich das mit Powershell machen, wenn ja wie?

    Vielen Dank im Voraus und Grüsse aus dem Süden, Walter


    Sonntag, 15. April 2018 13:33

Antworten

  • Ich hab ja inzwischen auch gelernt, dass alte Leute mal ganz schön bockig sein können, wenn sie ihren Willen nicht bekommen.  ;-)  :-D  ... und ich habe dein Eindruck, dass ein etwas einfacherer Code besser geeignet ist, nebbiolo zum "Selbst-Aktiv-Werden" zu bewegen. Powershell ist ja nun wirklich deutlich leichter als Cobol.

    $HeaderListe =  'Index','DateTime','Wert01','Wert02','Wert03','Wert04','Wert05','Wert06','Wert07','Wert08','Wert09','Wert10','Wert11','Wert12','Wert13','Wert14','Wert15','Wert16','Wert17'
    Import-Csv -Path 'c:\temp\wetter\wettermon.txt' -Delimiter ';' -Header $HeaderListe |
        Select-Object -Property *,@{Name = 'Datum'; Expression = {Get-Date -Date $_.DateTime}} |
        Sort-Object -Property Datum | 
            ForEach-Object {
                If( -not ($_.Datum.Hour -eq $LoopVar)){
                    $_
                }
            $LoopVar = $_.Datum.Hour
            } |
                Format-Table -AutoSize

    Statt an Format-Table kann man die Ausgabe natürlich auch an Export-CSV weiter-pipen ... ganz nach Belieben.  ;-)

    Ab jetzt bekommst Du von mir aber nur noch Hilfe, wenn Du selbst ein bissl Mitarbeit zeigst.  ;-)


    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH_666 Montag, 16. April 2018 17:03
    • Als Antwort markiert nebbiolo Dienstag, 17. April 2018 10:03
    Montag, 16. April 2018 17:02

Alle Antworten

  • Kann ich das mit Powershell machen, wenn ja wie?

    Definitiv kannst Du das mit Powershell machen.

    Anfangen solltest Du, indem Du Dir Grundlagen von Powershell aneignest. Hol Dir ein gutes Buch oder mach z.B. einen kostenlosen Video-Kurs bei der Microsoft Virtual Academy: Getting Started With Microsoft Powershell
     ...  oder Du bezahlst jemanden der es für Dich macht ... das ginge natürlich auch  ...  ;-)


    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Sonntag, 15. April 2018 13:39
  • Vielen Dank "Computacenter" .

    Vielleicht gibt es ja auch noch jemand der mir einige Zeilen ohne kommerziellen Hintergrund angibt, ich habe schon viel Aufwand mit googeln und austesten von Scripts für eine einmalige Angelegenheit aufgewendet ...

    Sonntag, 15. April 2018 14:10
  • Das ist eher (auch langfristig) eine Aufgabe für einen SQL-Server (die Expressversion kostet auch nichts).
    Damit lädst du dir die Daten in eine SQL-Datenbank als Tabelle einzelner Felder und kannst diese dann per SQL beliebig auswerten.

    Der Geschmack kommt bekanntlich beim Essen und die Kriterien können vielfältig sein.

    Oder du installierst dir das kostenlose Tool Power-BI!
    Auch mit diesem kannst du CSV-Daten importieren und beliebige Berichte stricken.
    Das ist allemal effektiver (und weitreichender) als Datenverarbeitung mit PowerShell.
    Das erinnert mich an meine EDV-Grundausbilding von 1975, als es SQL noch gar nicht gab.

    Sonntag, 15. April 2018 15:43
  • nebbiolo,

    wir machen das hier alle in unserer Freitzeit. Auch wenn die meisten von uns, die Woche über Jobs machen, wo wir dafür bezahlt werden. Das ist auch ein bissl der Grund, warum wir hier keinen gebrauchsfertigen Code auf Bestellung liefern. Deshalb auch die Empfehlung, erst mal die Grundlagen von Powershell zu lernen. Ohne diese, wird Dir vermutlich kein Tipp, den Dur hier bekommst, wirklich weiterhelfen. Du wirst also ein ganzes Stück selber mitarbeiten müssen.  ;-) ;-)

    Wenn Du es wirklich unbedingt mit Powershell machen willst - bfuerchau hat ja schon ein wenig anklingen lassen, warum das eventuell nicht die allerbeste Idee ist - kannst Du Dir schon mal die cmdlets Import-CSV, Where-Object und Export-CSV ansehen. Dazu kommen vielleicht noch die Comparison-Operators für den Anfang. 

    Wenn Du dann Code hast, den Du hier posten kannst und bei dem Du Hilfe brauchst, werden wir uns ein Bein ausreissen, beim Versuch Dir zu helfen.  ;-) :-D


    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Sonntag, 15. April 2018 16:03
  • Vielen Dank für Eure Antworten!

    Dann werde ich versuchen einen "alten" XP-PC mit Cobol (meine EDV-Ausbildung begann 1969- in der Zwischenzeit vergesse ich mehr als ich aufnehmen kann ;-) zum laufen zu bringen ... so würde dann die Lösung aussehen:

               OPEN INPUT  IDATEI.
               OPEN OUTPUT ODATEI.
           H00-A20.
               READ IDATEI NEXT INTO I-REC
                 AT END
                     GO TO H00-A90.
               IF  I-TAG = HF-TAG
                    GO TO H00-A20.
               MOVE I-REC TO O-REC.
               WRITE OUT-REC-FD FROM O-REC
                 INVALID KEY
                   DISPLAY "INV. WRITE EOJ"
                   STOP RUN.
               MOVE I-TAG TO HF-TAG.
               GO TO H00-A20.
           H00-A90.

    Nachdem ich mich ein bisschen mit PS beschäftigt habe, glaubte ich dass dies mit einem Script von 5-6 Zeilen möglich sei ... 

    Guten Start in die Woche und viele Grüsse aus Italien, nebbiolo


         

    Sonntag, 15. April 2018 17:50
  • Dann werde ich versuchen einen "alten" XP-PC mit Cobol zum laufen zu bringen ...

    Klingt nach einem Hobby-Projekt. Die meisten hier sind doch vorwiegend eher in einer professionellen Windows-Umgebung unterwegs.

    ... ist zwar eigentlich off topic, aber ich könnte mir vorstellen, dass ein RaspberryPi mit Python für Deine Zwecke besser geeignet wäre. So'n Dingens kostet gerade mal 40 €uronen und Du wärst, statt mit einem schon lange nicht mehr supporteten Windows, mit einem ziemlich aktuellen Linux unterwegs.

    (meine EDV-Ausbildung begann 1969- in der Zwischenzeit vergesse ich mehr als ich aufnehmen kann ;-)

    Die meisten von uns hier haben auch nicht gerade eben vor kurzem die Schule beendet! ;-)

    Nachdem ich mich ein bisschen mit PS beschäftigt habe, glaubte ich dass dies mit einem Script von 5-6 Zeilen möglich sei ... 

    Wenn Du es drauf anlegst, bekommst Du alles in eine Zeile. Aber die wäre dann sehr sehr lang und sehr schlecht zu lesen und zu debuggen.

    Da Du jetzt sowieso schon angefangen hast, mit Powershell "zu spielen", kannst Du es ja auch mal versuchen. Die nötigen cmdlets für den Anfang hast Du oben schon mal gelesen. Wenn Du Dir die Beispiele in den verlinkten Hilfe-Artikeln gut anschaust, bekommst Du den Anfang bestimmt relativ einfach hin. Und wenn Du mit Deinem Code nicht weiterkommst, weist Du ja jetzt, wen Du fragen kannst.


    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Sonntag, 15. April 2018 18:18
  • Also mit COBOL CSV-Daten zu verarbeiten ist ungleich schwieriger, als mit PS.
    Nun ja, mit UNSTRING kann man schon einiges reißen.

    Das eine nennt sich Spagetti-Programmierung, das andere arbeitet mit Objekt-Orientierung.
    Es gibt wirklich genug Onlineliteratur zur PS mit Beispielen, Tipps und Tricks und natürlich bei konkreten Fragen auch Hilfe von hier.

    Und, das hat die COBOL-Fraktion nicht unbedingt gelernt, gehört das qulifizierte Suchen in den Suchmaschinen mit zum Erfolg.
    Du glaubst gar nicht, was ich alles als alter COBOL- und RPG- sowie auch C++-Programmierer  schon alles im Netz gefunden habe um ebenso nun VB.NET, C# oder Java programmieren zu können.
    Aber: geschenkt wird einem nichts, man muss schon selber mitwirken.

    Achja, google doch mal nach "COBOL .NET", ist schon erstaunlich was es da alles gibt.


    • Bearbeitet bfuerchau Sonntag, 15. April 2018 19:23
    Sonntag, 15. April 2018 19:22
  • Ich habe eine grosse Wetterdaten-Datei (wettermon.txt) welche ich auf einen Datensatz pro Stunde zusammenfassen möchte.

    [...]

    Dazu müsste ich vom zweiten Feld (Abtrennung ;) die 12 Position (erster Datensatz=01) zwischenspeichern und dann die Folgenden Zeilen (2x01, 3x02, 3x03) mit dem gleiche Inhalt überlesen und am Ende einen neue Datei (wetterstd.txt) erhalten.

    So ganz verstehe ich nicht, was du machen möchtest. Eingangs schreibst du, dass du die Datensätze zusammenfassen möchtest. Das würde ja bedeuten, dass du die Werte mit einer Aggregatsfunktion (Durchschnitt, Summe, etc.) wie in einem GROUP BY in SQL konsolidieren würdest. Danach schreibst du jedoch, dass die "folgenden Zeilen mit dem gleichen Inhalt überlesen" möchtest. Abgesehen von der Datensatznummer und dem Zeitstempel ist doch kein Datensatz identisch und das Ergebnis wäre eine Datei nur mit Datensatznummern und der Stunde, wo ich mich frage zu was das gut sein soll. Insofern würde ich auf Ersteres tippen. 

    Hier wäre mal ein Lösungsansatz, bei dem die Werte gemittelt werden:

    # Import-WeatherData.ps1
    
    #Requires -Version 3
    
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false,Position=0)]
        [string]$Path = (Join-Path -Path ([System.Environment]::GetFolderPath('MyDocuments')) -ChildPath 'wettermon.txt') 
    )
    
    if (-not (Test-Path -Path $Path -PathType Leaf))
    {
        Write-Error -Message "Could not find path '$Path'."
        exit
    }
    
    $Pattern = '^(\d{2}\.\d{2}\.\d{4} \d{2})'
    $Header  = 'No', 'DateTime', 'Value1', 'Value2', 'Value3', 'Value4', 'Value5', 'Value6', 'Value7', 'Value8'
    $Header += 'Value9', 'Value10', 'Value11', 'Value12', 'Value13', 'Value14', 'Value15', 'Value16', 'Value17'
    Import-Csv -Path $Path -Header $Header -Delimiter ';' | 
        Select-Object -Property *,@{Name='Hour';Expression={if($_.DateTime -match $Pattern){$Matches[0]}}} |
        Group-Object -Property Hour |
        ForEach-Object -Process {
            [PSCustomObject][ordered]@{
                Hour = $_.Name
                'Value1(AVG)' = ($_.Group | Measure-Object -Property Value1 -Average).Average
                'Value2(AVG)' = "{0,11:n3}" -f ($_.Group | Measure-Object -Property Value2 -Average).Average
                # Value3
                # ...
            }
        }

    Montag, 16. April 2018 09:52
  • Eine Gruppierung über die Stunde ohne Berücksichtigung des Datums erscheint mir nicht so sinnvoll.
    Auch von der Aufgabe wird nicht der Durchschnitt je Stunde sondern nur der Zustand zum Anfang jeder Stunde benötigt. Wobei hier aber das Datum bis zur Stunde als Gruppenbegriff herhalten müsste und jeweils der 1. Satz einer Gruppe herausgefiltert werden soll.

    Montag, 16. April 2018 11:39
  • Vielen Dank Joachim Meyer - hatte die Hoffnung auf Hilfe schon aufgegeben.

    Die Wetterstation war lange so eingestellt, dass alle 5 Sekunden ein Datensatz erstellt wird. Für meine WEB-Statistik möchte ich aber nicht so viele Daten, einen Datensatz pro Stunde genügt mir - das gibt ja 24 Messungen pro Tag. Deshalb möchte ich nur den ersten Datensatz pro Stunde alle anderen benötige ich nicht - sie können also vergessen werden. Einen Durchschnitt ist auch nicht notwendig - die Werte (vor allem Temperatur) weichen ja wenig ab.

    Nun habe ich versucht das Script anzupassen, aber ich habe schon Mühe mit dem Pfad (es liegt im c:\temp\wetter) - ich es mal so angepasst (das batch und das wetter.ps1 liegt auch dort):

     

    Import-Csv "wettermon.txt" -Delimiter ';' |

    aber es funktioniert nicht ... die Ausgabe ist mir nicht klar.

    Hast Du mir vielleicht nochmals ein Vorschlag?

    Montag, 16. April 2018 13:57
  • nebbiolo,

    wenn die Datei in c:\temp\wetter liegt, gib doch den Pfad einfach mit an

    Import-Csv -Path 'c:\temp\wetter\wettermon.txt' -Delimiter ';'
    Edit: Solltest Du das dynamisch gestallten wollen und anstatt eines festen Pfades immer das Verzeichnis benutzen wollen, in dem auch das Script liegt, kannst Du die automatisch erzeugte Variable $PSScriptRoot benutzen ... also so ungefähr:
    Import-Csv -Path '$PSScriptRoot\wettermon.txt' -Delimiter ';'


    Best regards,

    (79,108,97,102|%{[char]$_})-join''




    • Bearbeitet BOfH_666 Montag, 16. April 2018 14:45
    Montag, 16. April 2018 14:34
  • Die Pfade passt du am besten in der Param-Sektion an:

    Param
    (
        [Parameter(Mandatory=$false,Position=0)]
        [string]$Path = (Join-Path -Path ([System.Environment]::GetFolderPath('MyDocuments')) -ChildPath 'wettermon.txt')
    )

    Der Wert hier gibt den Default-Pfad an, den du beim Aufruf des Scripts überschreiben kannst.

    Join-Path -Path ([System.Environment]::GetFolderPath('MyDocuments')) -ChildPath 'wettermon.txt'

    löst sich bei aktuellen Windows-Versionen nach C:\Users\%USERNAME%\Documents\wettermon.txt auf, kannst du jedoch beliebig anpassen. Zum Beispiel wie von Olaf vorgeschlagen:

    [string]$Path = "$PSScriptRoot\wettermon.txt"

    Wenn du nur den ersten Datensatz pro Stunde benötigst, könnte das in etwa so aussehen (Import und Exportdatetei jeweils im Dokumente-Ordner):

    # Merge-WeatherData.ps1
    
    #Requires -Version 3
    
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false,Position=0)]
        [string]$Path = (Join-Path -Path ([System.Environment]::GetFolderPath('MyDocuments')) -ChildPath 'wettermon.txt'),
    
        [Parameter(Mandatory=$false)]
        [string]$OutFile = (Join-Path -Path ([System.Environment]::GetFolderPath('MyDocuments')) -ChildPath 'WetterKonsolidiert.csv') 
    )
    
    if (-not (Test-Path -Path $Path -PathType Leaf))
    {
        Write-Error -Message "Could not find path '$Path'."
        exit
    }
    
    $Pattern = '^(\d{2}\.\d{2}\.\d{4} \d{2})'
    $Header  = 'No', 'DateTime', 'Value1', 'Value2', 'Value3', 'Value4', 'Value5', 'Value6', 'Value7', 'Value8'
    $Header += 'Value9', 'Value10', 'Value11', 'Value12', 'Value13', 'Value14', 'Value15', 'Value16', 'Value17'
    Import-Csv -Path $Path -Header $Header -Delimiter ';' | 
        Select-Object -Property *,@{Name='Hour';Expression={if($_.DateTime -match $Pattern){"$($Matches[0]):00:00"}}} |
        Group-Object -Property Hour |
        ForEach-Object -Process {
            $Hour = $_.Name
            $_.Group[0]
        } |
        Select-Object -Property @{Name='Hour';Expression={$Hour}},Value* |
        Export-Csv -Path $OutFile -Delimiter ';' -NoTypeInformation





    Montag, 16. April 2018 15:43
  • Ich hab ja inzwischen auch gelernt, dass alte Leute mal ganz schön bockig sein können, wenn sie ihren Willen nicht bekommen.  ;-)  :-D  ... und ich habe dein Eindruck, dass ein etwas einfacherer Code besser geeignet ist, nebbiolo zum "Selbst-Aktiv-Werden" zu bewegen. Powershell ist ja nun wirklich deutlich leichter als Cobol.

    $HeaderListe =  'Index','DateTime','Wert01','Wert02','Wert03','Wert04','Wert05','Wert06','Wert07','Wert08','Wert09','Wert10','Wert11','Wert12','Wert13','Wert14','Wert15','Wert16','Wert17'
    Import-Csv -Path 'c:\temp\wetter\wettermon.txt' -Delimiter ';' -Header $HeaderListe |
        Select-Object -Property *,@{Name = 'Datum'; Expression = {Get-Date -Date $_.DateTime}} |
        Sort-Object -Property Datum | 
            ForEach-Object {
                If( -not ($_.Datum.Hour -eq $LoopVar)){
                    $_
                }
            $LoopVar = $_.Datum.Hour
            } |
                Format-Table -AutoSize

    Statt an Format-Table kann man die Ausgabe natürlich auch an Export-CSV weiter-pipen ... ganz nach Belieben.  ;-)

    Ab jetzt bekommst Du von mir aber nur noch Hilfe, wenn Du selbst ein bissl Mitarbeit zeigst.  ;-)


    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH_666 Montag, 16. April 2018 17:03
    • Als Antwort markiert nebbiolo Dienstag, 17. April 2018 10:03
    Montag, 16. April 2018 17:02
  • So :-)

    Vielen Dank an alle für Ihre Geduld und Hilfe!

    Ich habe nun das "Muster" von BOfH_666 angepasst und das gewünschte Resultat fast bekommen. Einzig sind die Felder mit Hochkomma abgetrennt und die Reihenfolge ist nicht mehr identisch - aber damit kann ich gut leben.

    "Index";"Zeit";"Wert01";"Wert02";"Wert03";"Wert04";"Wert05";"Wert06";"Wert07";"Wert08";"Wert09";"Wert10";"Wert11";"Wert12";"Wert13";"Wert14";"Wert15";"Wert16";"Wert17";"Datum"
    "1";"13.03.2017 01:20:49";"15";"21.0";"53";"22.7";"23";"1002.4";"944.6";"3.6";"6.1";"SW";"0.5";"22.7";"0.00";"0.00";"0.00";"0.00";"354.90";"13.03.2017 01:20:49"


    $input = 'c:\Users\Walter\Documents\wetter\daten\easyweather\test.csv'
    $output = 'c:\Users\Walter\Documents\wetter\daten\easyweather\testo.csv'
    $HeaderListe =  'Index','Zeit','Wert01','Wert02','Wert03','Wert04','Wert05','Wert06','Wert07','Wert08','Wert09','Wert10','Wert11','Wert12','Wert13','Wert14','Wert15','Wert16','Wert17'
    Import-Csv -Path $input -Delimiter ';' -Header $HeaderListe |
        Select-Object -Property *,@{Name = 'Datum'; Expression = {Get-Date -Date $_.Zeit}} |
        Sort-Object -Property Datum |
            ForEach-Object {
                If( -not ($_.Datum.Hour -eq $LoopVar)){
                    $_
                }
            $LoopVar = $_.Datum.Hour
            } |
                Export-CSV -Path $output -Delimiter ';'




    • Bearbeitet nebbiolo Dienstag, 17. April 2018 10:07
    Dienstag, 17. April 2018 10:02
  • Die Anführungszeichen entsprechen dem CSV-Standard. Jedes System, welches CSV-Daten verarbeiten kann, sollte damit problemlos umgehen können.

    Das mit der Reihenfolge ist leider so bei der Powershell - die Daten kommen in beliebiger Reihenfolge. Wenn das zwingend eine bestimmte Reihenfolge sein müsste, müsste man da noch einen weiteren Schritt einbauen. Das wäre aber nur Kosmetik, die Daten bleiben ja dabei die gleichen.


    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Dienstag, 17. April 2018 11:19