manrique
Goto Top

Powershell - aus csv Spaltensumme einlesen und in Variable bzw. neue csv schreiben

Hallo an alle,

ich hab mit folgendem Code versucht anhand einer Beispieldatei die Spaltensumme zu ermitteln:

 $csv= import-csv C:\Temp\Data2018.csv
 $betrag1 = $csv.Betrag1 | measure-object -sum  
 $object = -Name NumCpu -Value $Betrag1.sum

 [System.Windows.Forms.MessageBox]::Show($betrag1,"Kontrollsummen",0)  

Was mache ich falsch? Ich möchte die Summe der Spalte1 und Spalte2 in eine Variable schreiben bzw. in das C:\Temp\Data2018_Summe.csv schreiben.

Kann mir bitte jemand helfen?

Vielen Dank
data
datasumme

Content-ID: 416467

Url: https://administrator.de/contentid/416467

Ausgedruckt am: 24.11.2024 um 01:11 Uhr

138810
138810 11.02.2019 aktualisiert um 09:06:20 Uhr
Goto Top
Was mache ich falsch?
"Strings" kannst du nicht summieren face-wink. Du musst sie erst zu echten Zahlen umwandeln!
[Int]
[Decimal]
[Double]
&Co. sind deine Freunde
$betrag1 = import-csv 'C:\Temp\Data2018.csv' -delim ";" | %{[decimal]::Parse($_.betrag1,[cultureinfo]::GetCultureInfo('de'))} | measure -Sum | select -ExpandProperty Sum  
erikro
erikro 11.02.2019 aktualisiert um 08:43:54 Uhr
Goto Top
Moin,

Zitat von @Manrique:
 $csv= import-csv C:\Temp\Data2018.csv

Hier fehlt der delimiter.

$betrag1 = $csv.Betrag1 | measure-object -sum  

Das ist soweit ok. Wenn das Trennzeichen bekannt ist, dann wird das das richtige Ergebnis auswerfen.

$object = -Name NumCpu -Value $Betrag1.sum

Was soll das werden?

 [System.Windows.Forms.MessageBox]::Show($betrag1,"Kontrollsummen",0)  

Hier fehlt das .sum hinter $betrag1. So steht das im Popup "Microsoft.Powershell..."

hth

Erik

<edit>P. S.: Warum schreibst Du nicht einfach =summe(b2:b4) in Deine Exceltabelle? face-wink </edit>
Manrique
Manrique 11.02.2019 um 09:23:12 Uhr
Goto Top
Hallo Erik,

vielen Dank. Das mit der "Excelsumme" ist von den Usern abhängig (ob die das machen oder nicht). Der Sinn ist, das die Werte in ein neues File geschrieben werden, damit das auch später dokumentierbar ist.

Ich bin kompletter PS Anfänger und hab jetzt das versucht: Ergebnis leider 0

import-csv 'C:\Temp\Data2018.csv' -delim ";" | %{[decimal]::Parse($_.BETRAG1,[cultureinfo]::GetCultureInfo('de'))} |measure -Sum | select -Expand Sum  
 
 $betrag1 = $csv.Betrag1 | measure-object -sum 

 [System.Windows.Forms.MessageBox]::Show($betrag1.Sum,"Kontrollsummen",0)  
datasummenull
138810
138810 11.02.2019 aktualisiert um 09:45:45 Uhr
Goto Top
Richtig von oben kopieren sollte man schon können.
Ich bin kompletter PS Anfänger
Dann mach halt erst mal einen Kurs und pfusch dich hier nicht so durch, das bringt dir nämlich gar nüscht.
erikro
erikro 11.02.2019 um 09:44:33 Uhr
Goto Top
Moin,

Zitat von @Manrique:
import-csv 'C:\Temp\Data2018.csv' -delim ";" | %{[decimal]::Parse($_.BETRAG1,[cultureinfo]::GetCultureInfo('de'))} |measure -Sum | select -Expand Sum  
>  
>  $betrag1 = $csv.Betrag1 | measure-object -sum 

Klar. $betrag1 ist ja auch leer. face-wink


so funktioniert es:

$csv= import-csv C:\Temp\Data2018.csv -delimiter ";"  
$betrag1 = $csv.Betrag1 | measure-object -sum  
[System.Windows.Forms.MessageBox]::Show($betrag1.sum,"Kontrollsummen",0)  

Liebe Grüße

Erik
Manrique
Manrique 12.02.2019 um 12:11:35 Uhr
Goto Top
Hallo Erik,

danke. Jetzt ist nur noch das "String-Thema", weil bei Summierung von Betrag 2 ist das Ergebnis 665 statt 86,3.

Kann mir bitte hier noch jemand helfen?

LG Josef
138810
138810 12.02.2019 aktualisiert um 12:21:56 Uhr
Goto Top
Zitat von @Manrique:
danke. Jetzt ist nur noch das "String-Thema", weil bei Summierung von Betrag 2 ist das Ergebnis 665 statt 86,3.

Kann mir bitte hier noch jemand helfen?
Sag mal bist du blind? Lese meinen ersten Beitrag nochmal da steht's schon in Codeform!

@erikro wandelt es nicht um das ist der Grund, in meinem ersten Beitrag steht es korrekt inkl. Umwandlung der Strings in Decimal.

Hier nochmal für Blinde:
$betrag1 = import-csv 'C:\Temp\Data2018.csv' -delim ";" | %{[decimal]::Parse($_.betrag1,[cultureinfo]::GetCultureInfo('de'))} | measure -Sum | select -ExpandProperty Sum  
$betrag1
Manrique
Manrique 12.02.2019 um 13:48:44 Uhr
Goto Top
Ergebnis korrekt, jedoch Fehlermeldung:
Ausnahme beim Aufrufen von "Parse" mit 2 Argument(en): "Die Eingabezeichenfolge hat das falsche Format."

LG
138810
138810 12.02.2019 aktualisiert um 13:58:19 Uhr
Goto Top
Dann hast du leere Felder in der CSV , die Felder musst du vorher ausfiltern.
$betrag1 = import-csv 'C:\Temp\Data2018.csv' -delim ";" | ?{$_.betrag1 -match "\d"} | %{[decimal]::Parse($_.betrag1,[cultureinfo]::GetCultureInfo('de'))} | measure -Sum | select -ExpandProperty Sum  
erikro
erikro 12.02.2019 um 14:39:31 Uhr
Goto Top
Moin,

Zitat von @138810:

Zitat von @Manrique:
danke. Jetzt ist nur noch das "String-Thema", weil bei Summierung von Betrag 2 ist das Ergebnis 665 statt 86,3.

Kann mir bitte hier noch jemand helfen?
Sag mal bist du blind? Lese meinen ersten Beitrag nochmal da steht's schon in Codeform!

@erikro wandelt es nicht um das ist der Grund, in meinem ersten Beitrag steht es korrekt inkl. Umwandlung der Strings in Decimal.

Hier nochmal für Blinde:
> $betrag1 = import-csv 'C:\Temp\Data2018.csv' -delim ";" | %{[decimal]::Parse($_.betrag1,[cultureinfo]::GetCultureInfo('de'))} | measure -Sum | select -ExpandProperty Sum  
> $betrag1
> 

Kannst Du Dich vielleicht mal im Ton mäsigen, zumal dann, wenn das, was Du erklärst nicht korrekt ist.

Das Problem ist nicht, dass die Zahlen nicht als Zahlen erkannt werden. Sonst würde die PS ja gar nicht rechnen und eine Fehlermeldung auswerfen. Das Problem ist, dass die Übergabe 12,23 nicht als zwölfkommazweidrei interpretiert wird, sondern als Array aus zwei Zahlen mit den Werten 12 und 23. Damit kommt bei der Summierung von scheinbar zwei Werten die Summe von vier Werten heraus. Also wenn ich die Zahlen 12,23 und 13,34 summiere, ist das Ergebnis nach dieser Logik korrekterweise 12 + 23 + 13 + 34 = 82. Die einfache Lösung des Problems ist, die Kommata vor der Berechnung durch Punkte zu ersetzen. Dann stimmt auch das Ergebnis. Das ist die wesentlich bessere Lösung, da ich dann nämlich nicht bei jedem Betrag das csv einzeln einlesen muss.

$csv= import-csv betrag.csv -delimiter ";"   
$betrag1 = $csv.Betrag1.replace(",",".") | measure-object -sum    
$betrag2 = $csv.Betrag2.replace(",",".") | measure-object -sum  

Liebe Grüße

Erik
138810
138810 12.02.2019 aktualisiert um 14:53:54 Uhr
Goto Top
Das ist die wesentlich bessere Lösung, da ich dann nämlich nicht bei jedem Betrag das csv einzeln einlesen muss.
Nö, so werden dann nämlich auch falsch formatierte Zahlen falsch interpretiert und es kommt das falsche Ergebnis heraus, deswegen besser gleich einzeln mit der passenden Culture einlesen und validieren oder TryParse nutzen dann stimmt das Ergebnis auch.
Manrique
Manrique 12.02.2019 um 14:50:43 Uhr
Goto Top
Vielen Dank!!!

Kannst du mir vielleicht bei der Implementierung in den bestehenden Code ( Dateiname variabel bzw. -Filter *.csv) noch helfen?

Mit dem bestehenden Code wird/werden "Original" csv eingelesen. Aus diesen sollen mit deinem Code die Spaltensummen der Spalten Soll und Haben gebildet werden.
Die Spaltensummen aus der "Original" csv sowie die Summe der im bestehende Code berechneten Werte der Spalten betrag und steuer (werden in Zeile 39 und 40 negativ - muss aber so sein) sollen in ein neues file C:\Temp\"Original"_Summe.csv geschrieben werden

Der Sinn ist:
1. Spätere Nachvollziehbarkeit
2. Falls beim schreiben des export-csv etwas passiert (Ziel nicht erreichbar, etc.), stimmen die Summen nicht überein.

Daher ist es wahrscheinlich notwendig nach Zeile 67 das neu erstellte export csv einzulesen, die Werte falls negativ in positiv umwandeln und dann ebenfalls wieder die Spaltensummen bilden. Die Summe beider Spalten (Original bzw export file) müssen ident sein.

LG
$quellordner = 'C:\Temp\'  
$zielordner = 'C:\Temp\Import'  

if(!(Test-Path $zielordner)){md $zielordner -Force | out-null}

$konto_replacements = @{
        "2700" = "9500"  
		"2820" = "2800"  
		"2830" = "2801"  
		"2810" = "2802"  
		"-" = "9999"  
    }

gci $quellordner -Filter *.csv -File | %{
     $csv = gc $_.Fullname -Encoding String | ?{$_ -notmatch '^\s*$'} | ConvertFrom-CSV -Delimiter ";"  

    $csv | ?{$_.Beleg -ne ''} | %{  
        $konto = $_.Konto
        $gkonto = $_.Gegenkonto
		$konto = $konto_replacements.GetEnumerator() | ?{$_.key -eq $konto} | %{$_.Value}
		$gkonto = $konto_replacements.GetEnumerator() | ?{$_.key -eq $gkonto} | %{$_.Value}
		$konto = @{$true = $konto_replacements.'-';$false = $konto}[$konto -eq $null]  
		$gkonto = @{$true = $konto_replacements.'-';$false = $gkonto}[$gkonto -eq $null]  
		
        $beleg = [regex]::match($_.Beleg,'(?i)^([\w])(\d+)')  
        if ($_.Soll.trim() -ne ''){  
            $betrag = [decimal]::Parse([string]$_.Soll,[cultureinfo]::GetCultureInfo('de'))  
        }elseif($_.Haben.trim() -ne ''){  
            $betrag = [decimal]::Parse([string]$_.Haben,[cultureinfo]::GetCultureInfo('de'))  
        }else{
            $betrag = 0
        }
        $prozent = [int]$_.UStSatz
		$s = 0
		[decimal]::TryParse([string]$_.Soll,[System.Globalization.NumberStyles]::Any,[cultureinfo]::GetCultureInfo('de'),[ref]$s) | out-null  
		
		$buchcode =  @{$true = '2'; $false = '1'}[$s -gt 0]  
		if ($buchcode -eq 2){
			$betrag = -$betrag
			$steuer = -$steuer
		}	
		$stc = @{$true = '1'; $false = '2'}[$buchcode -eq 2]  
		
		if($_.UStSatz -ne ''){  
			$steuer = @{$true=([math]::round(($betrag / (100+$prozent))*$prozent,2));$false=""}[$prozent -gt 0]  
			$steuercode = $stc
		}else{
			$steuer = ""  
			$steuercode = ""  
		}
			
        [pscustomObject]@{
            satzart = 0
            gkonto = $gkonto
            konto = $konto
            belegnr = $beleg.Groups[2].Value
            belegdatum = $_.Datum
            buchsymbol = @{'R' = 'RB';'S' = 'SP';'V' = 'VB';'K' = 'HK'}[$beleg.Groups[1].Value]  
            buchcode = $buchcode
            prozent = $_.UStSatz
			steuercode = $steuercode
            betrag = @{$true=[math]::Round(($betrag - $steuer),2);$false=[math]::round($betrag,2)}[$prozent -gt 0]
            steuer = $steuer
            text = $_.Buchungstext
			verbuchstatus = 0
        }
    } | export-csv "$zielordner\$($_.Basename)_Import$($_.Extension)" -Delimiter ";" -NoType -Encoding UTF8 -Verbose  
}
138810
138810 12.02.2019 aktualisiert um 14:53:25 Uhr
Goto Top
Man fragt sich wie der TO so ein Script hinbekommt wo es doch an den Grundlagen schon scheitert, wahrscheinlich wieder irgendwo her kopiert und ohne Quellenangabe hier reingepastet ... face-sad
Manrique
Manrique 12.02.2019 um 14:57:09 Uhr
Goto Top
Das ist aus diesem Forum von colinardo

Kleinigkeiten von mir angepasst.
77559
77559 12.02.2019 um 15:12:45 Uhr
Goto Top
Mit der Installation des ImportExcel Moduls von Doug Finke
https://github.com/dfinke/ImportExcel
https://www.powershellgallery.com/packages/ImportExcel/5.4.4
Kannst du mit diesem Skript (auch ohne installiertes Excel) direkt Excel Dateien lesen/schreiben ohne Umweg über .csv Datein.
$Data2018 = Import-Excel .\Data2108.xlsx
$KontrollSummen = [PSCustomObject]@{
    Name    = 'Summe Data2018'  
    Betrag1 = ($Data2018|Measure-Object Betrag1 -Sum).Sum
    Betrag2 = ($Data2018|Measure-Object Betrag2 -Sum).Sum
}
$KontrollSummen
$KontrollSummen | Export-Excel .\Data2018_Summe.xlsx -AutoSize
[System.Windows.Forms.MessageBox]::Show(($KontrollSummen|Out-String),,"Kontrollsummen",0)  

Bekommst du diese Ausgabe:
> $KontrollSummen

Name           Betrag1 Betrag2
----           ------- -------
Summe Data2018      36    86,3

Und direkt auch als .xlsx Datei

Die MessageBox geht zwar, sieht aber durch den Standard Font (variable width) blöd aus.
Manrique
Manrique 12.02.2019 um 15:26:22 Uhr
Goto Top
ok danke, muss aber für Weiterverarbeitung csv bleiben.

LG
erikro
erikro 12.02.2019 um 16:31:35 Uhr
Goto Top
Moin,

Zitat von @138810:
Das ist die wesentlich bessere Lösung, da ich dann nämlich nicht bei jedem Betrag das csv einzeln einlesen muss.
Nö, so werden dann nämlich auch falsch formatierte Zahlen falsch interpretiert und es kommt das falsche Ergebnis heraus,

Wieder falsch. Stehen da keine Zahlen drin, dann gibt es wie bei Deiner Lösung eine Fehlermeldung um die Ohren.

deswegen besser gleich einzeln mit der passenden Culture einlesen

Janee, is klar. Macht ja auch kaum ein Unterschied, wenn ich aus 2.000.000 Datensätzen je zehn Werte auslese, ob ich dann das csv einmal oder zehnmal komplett auslesen muss. Wir haben ja heute schnelle Prozessoren und einen Haufen Speicher. Und die PS ist ja bekannt dafür, dass sie rasend schnell arbeitet.

und validieren oder TryParse nutzen dann stimmt das Ergebnis auch.

Wenn ich schon validiere, dann will ich aber auch wissen, welcher DS meiner 2 Mio. denn das Berechnen verhindert. Dann vielleicht so:

# Referenzvariable für TryParse
[decimal]$test = $null

# Variablen leeren, damit keine falschen Ergebnisse angezeigt werden

$betrag1 = ""  
$betrag2 = ""  

# Zählvariable für den aktuellen Datensatz
$i = 1

# Semaphor für die Berechnung
$sem = $true

# Daten importieren
$csv= import-csv betrag.csv -delimiter ";"   

# Prüfung, ob Zahlen korrekt

foreach($dataset in $csv) {

    if(!([decimal]::TryParse($dataset.betrag1 , [ref]$test) -and [decimal]::TryParse($dataset.Betrag2 , [ref]$test))) {

        $sem = $false
        write-output "Datensatz $i enthält falsche Zahlen!"  

    }
    $i++
}

if($sem) {
    
    # Umwandlung des Kommas in Punkt und Summenbildung
    $betrag1 = $csv.Betrag1.replace(",",".") | Measure-Object -sum  
    $betrag2 = $csv.Betrag2.replace(",",".") | Measure-Object -Sum  
}

# Ausgabe auf dem Bildschirm
write-output "Betrag 1:" $betrag1.sum  
write-output "Betrag 2:" $betrag2.sum  
138810
138810 12.02.2019 aktualisiert um 16:37:33 Uhr
Goto Top
Wieder falsch. Stehen da keine Zahlen drin, dann gibt es wie bei Deiner Lösung eine Fehlermeldung um die Ohren.
Du willst nicht verstehen und verdrehst einem das Wort ... schon klar.
[decimal]::TryParse(
Und auf einmal nutzt er es auch wie vorgeschlagen face-big-smile. Ja nee is klar.

Glaub der TO kommt da eh nicht mit, wenn er es noch nicht mal selbst einbauen kann. Vermutlich also Zwecklos.
Manrique
Manrique 12.02.2019 um 16:47:38 Uhr
Goto Top
Ich würde mich trotzdem über eine Lösung mit dem bestehenden Code sehr freuen.

Vielen Dank
erikro
erikro 12.02.2019 um 16:55:52 Uhr
Goto Top
Zitat von @138810:

Wieder falsch. Stehen da keine Zahlen drin, dann gibt es wie bei Deiner Lösung eine Fehlermeldung um die Ohren.
Du willst nicht verstehen und verdrehst einem das Wort ... schon klar.

Nein, Du verstehst nicht, dass das Einlesen der csv jeweils für einen Wert nicht performant ist. Ein csv lese ich genau einmal in einem Skript ein und nicht für jede Spalte neu. Und wo verdrehe ich Deine Worte? Wie anders soll ich das

Nö, so werden dann nämlich auch falsch formatierte Zahlen falsch interpretiert und es kommt das falsche Ergebnis heraus,

verstehen als so, dass bei z. B. 1,,2 oder bei 1text2 ein Ergebnis heraus kommen soll? Das ist schlicht und ergreifend falsch. Wenn ich bei meiner Lösung irgend etwas anderes als eine Zahl im deutschen oder englischen Format reinschreibe, dann gibt es eine Fehlermeldung, die besagt, dass da keine Zahl steht.

[decimal]::TryParse(
Und auf einmal nutzt er es auch wie vorgeschlagen face-big-smile. Ja nee is klar.

Und wieder falsch. Du schlägst vor, die Werte spaltenweise einzeln einzulesen, die Spalte zu validieren und dann zu rechnen oder eben auch nicht. Ich schlage vor, dass, wenn denn nicht schon vorher in Excel oder wo auch immer validiert wurde, die Tabelle am Stück einzulesen, die einzelnen Werte jedes Datensatzes zu validieren, dann entweder zu rechnen oder auszugeben welche Werte genau denn nicht validiert werden konnten. Ich mache also etwas vollkommen anderes als Du.