In C Werte aus Protokoll einlesen und z.B. Mittelwert berechnen

R4TFIV3

Grünschnabel
Hallo zusammen,

ich bin neu hier und möchte gerne aus einem .txt Protokoll Rechnungen veranstalten. Von der Erfahrung her bin ich eher als Anfänger einzustufen und erwarte von euch natürlich keine vollständigen Lösungsvorschläge. Ich bräuchte lediglich Hilfe bei dem Ansatz wie ich den Druck aus der Datei einlese und z.B. einfach einen Mittelwert berechnen kann. Leider konnte ich bis jetzt nur Codes in C++ finden. Ich benötige diese Lösung leider in C.

So könnte das Protokoll in der .txt Datei aussehen:

Datum------Uhrzeit----------Nr.-Druck-Strom-Leistung
2016-05-17 17:42:05.707385 05 05.93 00.00 52.51
2016-05-17 17:42:05.707705 06 05.81 10.07 54.33
2016-05-17 17:42:05.908304 05 05.93 00.00 52.51
2016-05-17 17:42:05.908463 06 05.87 00.00 55.83

Mein Code vorerst der das Protokoll komplett auslesen kann:


C:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

int main()
{
   // Dateizeiger erstellen
FILE *datei;
char temp;
int i;

datei = fopen("Test_Daten.txt", "r");

if(datei == NULL) {
    printf("Datei konnte NICHT geoeffnet werden./n");

    } else {
    
            while((temp=fgetc(datei))!=EOF){
            printf("%c", temp);
          
            }

    }
// Datei schliessen
    fclose(datei);
return 0;
}

Genau wie das Protokoll vorliegt werden nun die Daten im Terminal ausgegeben. Aber wie mache ich nun weiter? Wie genau kann ich auf den Druck "zugreifen". Mein Ziel ist es am Ende von z.B. Nr. 05 den Mittelwert zu berechnen.

Eure Hilfe wäre sehr hilfreich. Vielen Dank schon mal vorab.

Viele Grüße
 

Jennesta

Erfahrenes Mitglied
Hallo,
schau dir mal die Funktion fscanf an. Dort ist ein (zugegeben sehr minimales) Beispiel, welches du aber auf dein Problem anwenden kannst.
Du kannst der Funktion ein einen Format-String geben, der die gesamte Zeile definiert und dann zeilenweise aus deiner Textdatei auslesen.

Du könntest im ersten Ansatz erstmal versuchen mit einem Funktinsaufruf alle Werte in einer Zeile in lokale Variablen einzulesen und diese dann nach jedem Schleifendurchlauf in der Konsole auszugeben, zur Kontrolle. Kleiner Hinweis, fscanf gibt die als Rückgabewert die Anzahl an erfolgreich eingelesenen Werte.

Wenn das funktioniert, gehst du das nächste Problem an, falls dabei weitere Probleme auftauchen, frag gerne nach.

Viele Grüße
 

Technipion

Erfahrenes Mitglied
fscanf bietet sich hier tatsächlich an. Ansonsten gibt es auch immer noch die Holzhammermethode...
Hier hat ein netter Mensch einen Blogpost dazu geschrieben:

https://codingboost.com/parsing-csv-files-in-c
Hast du denn selbst schon Google bemüht? Bei mir ist das ziemlich direkt aufgepoppt. Vielleicht fehlen dir ja auch nur die Schlagwörter, also in diesem Fall "C" und "csv" (weil dein Log quasi eine CSV-Datei ist, nur mit Leerzeichen als Trennzeichen).

Gruß Technipion
 

R4TFIV3

Grünschnabel
Vielen Dank für eure Vorschläge. Ich habe mich jetzt mit euren Vorschlägen befasst und mich auch weiterhin im Internet durchgestöbert. Durch einen Eintrag hier im Forum bin ich auch etwas schlauer geworden.

Ich habe nun versucht die Daten in eine Struktur zu bekommen um vielleicht so abhängig von der Anlageadresse 05 oder 06 an den Druck zu kommen.

Ich bin mir jetzt noch nicht ganz sicher, ob mein Vorgehen funktioniert hat.
Hier mal mein Code:

C:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>



typedef struct
{
    char Datum[10];
    char Uhrzeit[15];
    char Anlagenadresse[2];
    float Druck;
    float Volumenstrom;
    float Leistung;
}Daten;


int main (void)
{
    int c;
  FILE *datei;
   
Daten dt;
  datei = fopen ("Test_Daten.txt", "r");
  if (datei != NULL)
  {
       while( (c=fgetc(datei)) != EOF)
         putchar(c);
   }
   else {
      printf("Konnte Datei nicht finden bzw. öffnen!\n");
   }
 
  while(fscanf(datei,"%s;%s;%s;%f,%f,%f,",&dt.Datum,&dt.Uhrzeit,&dt.Anlagenadresse,&dt.Druck,&dt.Volumenstrom,&dt.Leistung) != EOF)
{
    printf("%s %s %s %f %f%f", dt.Datum,dt.Uhrzeit,dt.Anlagenadresse,dt.Druck,dt.Volumenstrom,dt.Leistung);
}

   fclose (datei);

  return 0;
}


Die Ausgabe sieht wie folgt aus:

2018-05-17 17:42:05.707385 05 05.93 00.00 52.51
2018-05-17 17:42:05.707705 06 05.81 10.07 54.33
2018-05-17 17:42:05.908304 05 05.93 00.00 52.51
2018-05-17 17:42:05.908463 06 05.87 00.00 55.83

Ist mein Vorgehen richtig gewesen? Ich weis leider nicht wie ich nun weiter vorgehen kann.
Ziel ist es am Ende von z.B. Anlageadresse 05 den Mittelwert des Drucks zu berechnen.

@Technipion Danke für den Hinweis ich habe wohl wirklich die falschen Schlagwörter benutzt. Ich habe die Methode zwar probiert, scheitere aber trotzdem noch. Falls mein jetziges Vorgehen nicht so gut ist, lass es mich wissen.
 
Zuletzt bearbeitet:

ComFreek

Mod | @comfreek
Moderator
Du hast klassische Bufferoverflow Sicherheitslückem im Code: du leist via fscanf einen beliebig langen String in einen begrenzten Buffer ein. Das ist nicht nur eine Sicherheitslücke, sondern eine tickende Bombe, die bei kleinsten Änderungen in den Dateien, die du parst, hochgehen kann. Dann macht dein Programm unvorhersehbare Dinge :) ("undefined behavior")

Warum benötigst du die Lösung insgesamt in C? Python wäre zum Beispiel viel einfacher.
 

R4TFIV3

Grünschnabel
Ok danke für den Hinweis. Also alles nochmal von vorne :cry:
Leider brauche ich den Weg über C da das ganze im Rahmen eines Semester Projekts läuft. Ist das über C wirklich so schwer? Ich dachte jetzt lerne ich eine Programmiersprache mit der ich praktische Dinge anstellen kann. ;)
 

Jennesta

Erfahrenes Mitglied
Grundsätzlich spricht nichts gegen C. Auch mit der Sprache kann man durchaus viele praktische und sinnvolle Dinge anstellen. Ich persönlich programmiere auch lieber in C/C++, aber es kommt auch stark darauf an wofür du das Programm brauchst. Typische Anwednungen sind immer noch hardwarenahe Lösungen oder wenn es wirklich schnell gehen muss.

Wenn du das Programm z.B. nutzt um Daten (wie in deinem Beispiel) einzulesen, zu bearbeiten und evtl. auch zu plotten, dann kannst du das gleiche schneller und leichter mit Python erledigen. Theoretisch könntest du dann aber auch Excel nehmen :)

Lass dich also nicht entmutigen C zu lernen. Die Frage ist am Ende immer wo du es einsetzen möchtest. Und im Rahmen des Semester Projektes geht es ja ohnehin vorrangig darum etwas zu lernen, ggf. mit Weitblick je nachdem was du studierst.
 

ComFreek

Mod | @comfreek
Moderator
Ich dachte jetzt lerne ich eine Programmiersprache mit der ich praktische Dinge anstellen kann. ;)
Wenn du mit "praktisch" etwas Grafisches meinst mit Rapid-Prototyping-Charakter, dann ist da C eher die falsche Sprache, würde ich sagen :)
Für sowas nimmt man meist eine viel höhere Sprache wie z. B. HTML/JavaScript, Mathematica, Python.

Du musst nicht alles neu machen. Google mal nach "fscanf string length", etwa Checking size of string before operation with fscanf?.
 

R4TFIV3

Grünschnabel
Ich gebe immer noch nicht auf ;)

Ich bin schon wieder etwas weiter gekommen.

Hier mein Code:

C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

    char puffer[48];
    char *ptr1, *ptr2, *ptr3, *ptr4;
    char Datum[11];
    char Uhrzeit[16];
    char Adresse[3];
    float Druck;
    float sumDruck05=0;
    float sumDruck06=0;
    
    char Leerzeichen[] = " ";
    char Adresse05[] = "05";
    char Adresse06[] = "06";
    
int vergleichAdresse(){

            if(strcmp(ptr3,Adresse05)==0){
                Druck = atof(ptr4);
                strcpy(Datum,ptr1);
                strcpy(Uhrzeit,ptr2);
                strcpy(Adresse,ptr3);
                sumDruck05+=Druck;
                printf("ersterDruck: %.2f\n",sumDruck05);
                return sumDruck05;
            }
            if(strcmp(ptr3,Adresse06)==0){
                Druck = atof(ptr4);
                sumDruck06+=Druck;
                strcpy(Datum,ptr1);
                strcpy(Uhrzeit,ptr2);
                strcpy(Adresse,ptr3);
                printf("zweiterDruck: %.2f\n",sumDruck06);
                return sumDruck06;
                
            }
}


int main() {
    FILE *fp = fopen("Test_Daten.txt", "r");

    int i=0;

    if (fp == NULL) {
    printf("Fehler beim oefffnen");   
        
    }else{
        while(fgets(puffer,48,fp)){
        
        ptr1=strtok(puffer,Leerzeichen);
        ptr2=strtok(NULL,Leerzeichen);
        ptr3=strtok(NULL,Leerzeichen);
        ptr4=strtok(NULL,Leerzeichen);

        if(i==0){

            vergleichAdresse();

            strcpy(Datum,ptr1);
            strcpy(Uhrzeit,ptr2);
            strcpy(Adresse,ptr3);
            
            i++;
        }else{
            
            //MaxDruck
            if(atof(ptr4)>Druck){
                Druck = atof(ptr4);
                strcpy(Datum,ptr1);
                strcpy(Uhrzeit,ptr2);
                strcpy(Adresse,ptr3);
                
                }
            }
        } 

    }
            fclose(fp);
            printf("\nGroesster Druck:\n");
            printf("Datum: %s\n",Datum);
            printf("Uhrzeit: %s\n",Uhrzeit);
            printf("Adresse: %s\n",Adresse);
            printf("max. Druck: %.2f\n",Druck);

            printf("\nSumme Druck:\n");
            printf("sumDruck05: %.2f\n",sumDruck05);
            printf("sumDruck06: %.2f\n",sumDruck06);

    return 0;

}
//Programm Ende

Die Ausgabe sieht wie folgt aus:
ersterDruck: 5.93

Groesster Druck:
Datum: 2018-05-17
Uhrzeit: 17:42:05.908304
Adresse: 05
max. Druck: 5.95

Summe Druck:
sumDruck05: 5.93
sumDruck06: 0.00

Das mit der Summe bilden von dem Druck bei Adresse 05 bzw Adresse 06 hat immer noch nicht ganz geklappt. Vielleicht weis jemand von euch weiter. Ich schätze ich muss irgendwas an der Schleife ändern aber ich komme nicht drauf.
Mit "fscanf" komme ich leider nicht weiter aber dafür mit "fgets".


So sieht (gekürzt) die Datei aus: (Datum Uhrzeit Adresse Druck Strom Leistung)
2018-05-17 17:42:05.707385 05 05.93 00.00 52.51
2018-05-17 17:42:05.707705 06 05.81 10.07 54.33
2018-05-17 17:42:05.908304 05 05.95 00.00 52.51
2018-05-17 17:42:05.908463 06 05.87 00.00 55.83

Ziel:
Mein Ziel ist es am Ende von z.B. Adresse 05 den Mittelwert zu berechnen.
 

ComFreek

Mod | @comfreek
Moderator
strcpy(Datum,ptr1);
ptr1 ist vorher noch gar nicht einen Wert zugewiesen worden. Damit kannst du der ganzen Ausführung deines Programms gar nicht mehr trauen. Klassisches "Undefined Behavior".

Dass du auf uninitialisierten Speicher zugreifst, hätte dir aber auch valgrind gesagt. Du kannst ja mal valgrind ./dein-programm probieren.

An deiner Stelle würde ich also zuerst mal Valgrind ausprobieren und diese Fehler beheben.
 

Neue Beiträge