Daten aus CSV Datei mit ANS C lesen

M0zart

Grünschnabel
Hallo, ich bin relativ neu hier und noch nicht so fit in C. Ich bin auf ein Problem gestoßen, wobei mir bisher keiner weiterhelfen konnte. Ich hab auch gesehen, das ziemlich viel zu finden ist über Datei auslesen usw., aber wirklich weitergeholfen hat es mir nicht.

Zu meinem Problem: Ich muss in ANS C aus einer CSV Datei Werte lesen. Ich kenn jetzt den unterschied zwischen ANS C und "normal" C nicht wirklich. Ich hab auch schon ein Beispielprogramm bekommen und auch ausprobiert aber irgendwie funktioniert das ganze nicht richtig.

Dazu sei noch gesagt, das ganze wird in einem Programm von Siemens (WinCC V7.3) verarbeitet, deswegen auch ANS C. Und das Programmbeispiel ist auch von Siemens.

Ich bekomm meine gewünschte Datei geöffnet, nur wenn ich versuche irgendwas raus zu lesen, bekomm ich folgenden Fehler: general protection fault (Fehlermeldung vom WinCC). Der Compiler übersetzt alles wunderbar nur mit 3 Warnungen (Anhang).

Ich hoffe ihr könnt mir helfen... Vielen Dank schon einmal im vorraus
 

Anhänge

  • aus CSV nach WinCC schreiben.jpg
    aus CSV nach WinCC schreiben.jpg
    75,4 KB · Aufrufe: 9
  • Code.PNG
    Code.PNG
    21,9 KB · Aufrufe: 11

sheel

I love Asm
Hi

bitte Code als Text reinkopieren, in [code=c]hier der code[/code]

Die Sprache C ist seit Erfindung nicht gleich geblieben, sondern wurde mehrmals erweitert/geändert (also nicht nur Compiler usw., sondern die Sprache selber, welche Symbole und Funktionen usw. es gibt).
Es gibt
  • das "Ur-C", wie es von Kernighan und Ritchie irgendwann vor 1980 erfunden wurde
  • C89 (1989, auch ANSI-C genannt)
  • C99 (1999)
  • und C11 (2011)
Die neueren Varianten sind für den Programmierer im Wesentlichen besser (weniger umständlich usw.), wenn es um neue Programme geht. Die älteren Varianten sind trotzdem noch wichtig,
  • wenn es um alte bestehende Programme geht (die sehr oft in den neueren Varianten auch richtig sind, aber nicht immer)
  • wenn man aus irgendeinem Grund keine neue Variante verwenden darf (Chef der die Neuerungen nicht lernen will und deshalb der ganzen Firma die Verwendung verbietet, oder Ähnliches...)
  • oder wenn man, wie du anscheinend, keinen passenden Compiler hat.
In C89 programmieren ist jedenfalls nicht lustig, wenn man C11 auch haben könnte. Aber wenns sein muss...

So, zum Fehler:
Bisher aufgefallen ist mir, dass du "\0" verwendest, was in C ein ganzer String bzw. der Pointer darauf ist. '\0' wie im Beispiel ist ein einzelner Buchstabe (bzw. Byte).

Und "Warnungen" von C-Compilern sind oft schlimmer als die ganzen Fehler. Immer versuchen, alle Warnungen loszuwerden, bzw. zu verstehen warum sie da ist.
 

M0zart

Grünschnabel
Vielen dank erst einmal für die schnelle Hilfe Sheel. Hier wie gewünscht der Code
C:
#include "apdefap.h"

int gscAction( void )
{
#define MaxLineLength 80

FILE *fpFile;
char *strTag, *strValue, *pTmp;
char buffer[MaxLineLength];
double dVal;
fpFile = fopen("c:\\Users\\Desktop\\Mappe1.csv", "r");
printf("C-Skript: lese Datei: Mappe1.csv\n");
if(fpFile !=NULL)
{
     while(fgets(buffer,MaxLineLength, fpFile))
     {
          strTag=buffer;
          pTmp = strchr(buffer,(int)';');
          *pTmp='\0';
          strValue=++pTmp;
          strValue[strlen(strValue)-1]='\0';

          pTmp=strchr(strValue,(int)',');
          if(pTmp !=NULL){
             *pTmp='.';
          }

          sscanf (strValue, "%lf", &dVal);
          //SetTagDouble(strTag,dVal);
          printf("%s Value: %lf\r\n", strTag, dVal);
     }
     fclose(fpFile);
} else{
printf("Error: File not found!\n");
}

return 0;
}

Ich hab das geändert mit den " und siehe da es funktioniert. Jetzt hab ich das Problem, dass meine Zahl praktisch gerundet oder abgeschnitten wird... Ich weiß nicht genau was da passiert. Und zwar macht das Programm aus der Zahl 1,1 eine 1.0000000 und das gleiche für 2,2 -> 2.00000.
 

Anhänge

  • C.PNG
    C.PNG
    2,1 KB · Aufrufe: 4

sheel

I love Asm
Hm ... wie schaut denn eine CSV-Zeile bei dir genau aus?

Zum fgets: Ein Einzelner aufruf von fgets liest eben eine Zeile aus der Datei fpFile, in den String buffer, aber maximal MaxLineLength Byte (weil buffer in dem Fall nicht mehr Platz hat; fgets weiß das aber nicht selber, deshalb auch übergeben).
fgets hat aber auch noch einen Returnwert (den man theoretisch mit "varname = fgets(...)" einer Variable zuweisen könnte), und zwar den ersten Parameter (buffer) wenn alles funktioniert hat (als Pointer-/Speicher-Adresse, wie Strings in C eben gehandhabt werden), oder 0 (das nie als Adresse verwendet wird) wenn die Datei schon zu Ende ist oder irgendein anderer Fehler passiert ist.
In dem Fall wird der Wert zwar keiner Variable zugewiesen, sondern als Schleifenbedingung im While verwendet - etwas seltsam (normal wären ja Vergleiche wie "a == b" oder "a < b", und keine einzelnen Werte), aber das kann man in C machen: 0 wird in dem Fall als falsch bzw. Schleife beenden verstanden, und alles andere als wahr bzw. weitermachen. Kann man zum Testen auch einfach so hinschreiben: while(1) wird ewig ausgeführt, und while(0) nie.
Also kurz, solange fgets nicht 0 ergibt, also solange die Datei nicht zu Ende ist...
 
Zuletzt bearbeitet:

cwriter

Erfahrenes Mitglied
Und zwar macht das Programm aus der Zahl 1,1 eine 1.0000000 und das gleiche für 2,2 -> 2.00000.
1,1 ist auch keine Zahl - jedenfalls nicht in wissenschaftlicher Notation. Das wäre dann "1.1". Daher wird bei
C:
strchr(strValue, (int)',');
if(pTmp !=NULL){
             *pTmp='.';
          }
auch das Komma zu einem Punkt gewechselt. Aber nur einmal. Daher:
Hm ... wie schaut denn eine CSV-Zeile bei dir genau aus?

Die Casts von Zeichenliteralen auf int machen auch nicht wirklich viel Sinn - das ist nämlich Standard-Verhalten.

Und:
In C89 programmieren ist jedenfalls nicht lustig, wenn man C11 auch haben könnte. Aber wenns sein muss...
Hehe. Seuche...

Gruss
cwriter
 

M0zart

Grünschnabel
Also das mit dem Komma durch Punkt hab ich verstanden, aber das macht er scheinbar nicht richtig. Für den Beispielcode ist in der CSV Datei folgender Inhalt:
TagA; 1,1
TagB; 2,2
TagC; 3,3
usw.

Nochmals danke für die Erklärung, die hilft mir sehr weiter :D
 

cwriter

Erfahrenes Mitglied
Für den Beispielcode ist in der CSV Datei folgender Inhalt:
Und den Code hast du so bekommen?
Seufz. Manche Leute müssen das Rad aber auch immer neu erfinden...
Der gesamte Code liesse sich sehr viel leichter schreiben (mit ein paar Konzessionen, versteht sich):
C:
setlocale(LC_NUMERIC, "de_DE");
while(fscanf(fpFile, "%[^;]; %lf\n", strName, &strValue) == 2) // strName entsprechend ein Stringbuffer (kein "reiner" Pointer)
{
    //Werte eingelesen
}
setlocale(LC_NUMERIC, ""); //Reset auf (System-)Standard
4 SLOC statt 8.
Einziger "Nachteil": Die Zahlen müssen jetzt mit Kommata statt Dezimalpunkten eingegeben werden. Allerdings wäre das Vermischen von Zahlenformaten ohnehin Blödsinn. Falls du dennoch erlauben wolltest, dass die Zahlenformate gemischt sein dürfen, kannst du statt %lf einfach %s einlesen und dort die Kommata durch Punkte ersetzen.
Übrigens ist das \r\n beim einen printf überflüssig - \n ist der Standard für Zeilenumbrüche (übrigens muss man nur dann auf \r\n achtgeben, wenn man die Datei binär öffnet - aber das ist eine andere Geschichte).

Also das mit dem Komma durch Punkt hab ich verstanden, aber das macht er scheinbar nicht richtig.
Was sagt der Debugger bzw. das printf-Debugging?

Gruss
cwriter

/EDIT: Die locales brachten mich auf die Idee, dass dein System schon Kommata erwartet... Was passiert, wenn du die Kommata nicht durch Punkte ersetzt?
 
Zuletzt bearbeitet:

M0zart

Grünschnabel
Vielen Dann cwriter. Ja das ganze ist in der Siemens Sofware WinCC (Visualisierungsprogramm für die Industrie) erstellt und somit ist der Code auch von Siemens.
Deswegen kann ich den Code nur ganz normal Debuggin (keine Ahnung was der printf Debugger ist) und da gibt er 0 Fehler 0 Warnungen aus.

Also in meinem bestimmten Anwendungsfall muss ich aus einer csv Datei 2 Werte zurück schreiben. Deswegen brauch ich die Werte als float. Die csv von mir sieht wie folgt aus:
Text;Text;Text;Text
Zeitpunkt; Zahlenwert; Zahlenwert; Zahlenwert
Zeitpunkt; Zahlenwert; Zahlenwert; zahlenwert
Zeitpunkt; Zahlenwert; Zahlenwert; zahlenwert
usw...

Ich muss jetzt noch wissen wie ich die erste Zeile überspringe... weil wenn ich versuche einen Text als Zahl zu verwenden, wird der sehr wahrscheinlich meckern .... Und dann bräuchte ich letztendlich aus der zweiten Zeile einen Wert und aus der letzten. Das würde ich dann über einen Zähler oder so machen, sprich gucken wann der in der 2 Zeile ist und den Wert wegschreiben und wenn er am Schluss angekommen ist, hat er ja die letzte Zeile....
 

cwriter

Erfahrenes Mitglied
Deswegen kann ich den Code nur ganz normal Debuggin (keine Ahnung was der printf Debugger ist) und da gibt er 0 Fehler 0 Warnungen aus.
Das ist kein Debuggen, sondern ein Kompilieren. Vereinfacht gesagt sagt dir der Compiler, was er nicht versteht (weil Syntaxfehler bestehen). Debuggen beginnt dann, wenn man syntaktisch korrekten Code hat (der Compiler versteht, was du willst), aber die Semantik nicht stimmt (du falsch ausgedrückt hast, was du wolltest).
printf-Debugging heisst nur, dass du dir die Variablenwerte periodisch ausgeben lässt, um die Werte zu überprüfen (statt wie bei einem echten Debugger das Programm anzuhalten).

Also in meinem bestimmten Anwendungsfall muss ich aus einer csv Datei 2 Werte zurück schreiben. Deswegen brauch ich die Werte als float.
Nur zum Kopieren musst du Strings nicht in (double) floats verwandeln.

Die csv von mir sieht wie folgt aus:
Dann kann der Matchstring auf "%[^;]; %lf; %lf; %lf\n" geändert werden. Das ist ja gerade das Tolle an scanf: Es ist sehr flexibel (was auch seine Nachteile hat, siehe vararg-Diskussion).

Ja das ganze ist in der Siemens Sofware WinCC (Visualisierungsprogramm für die Industrie) erstellt und somit ist der Code auch von Siemens.
Verstehe ich nicht. WinCC ist ja kein Compiler? Wurde die CSV-Datei in WinCC erstellt? Das wäre unerheblich - CSV ist CSV.

Und wenn der Code von Siemens ist, dann würde mein Weltbild recht erschüttert...

Ich muss jetzt noch wissen wie ich die erste Zeile überspringe...
Wenn du die Spaltennamen nicht kennen willst, ja.
Möglichkeiten sind fgets() und scanf("%*[^\n]"), wobei das Asterisk bedeutet, dass man das Gelesene nicht speichern will.

weil wenn ich versuche einen Text als Zahl zu verwenden, wird der sehr wahrscheinlich meckern ....
Nö. Wahrscheinlich wird es eher Undefined Behavior sein (bzw. der Wert wird nicht in deine Variable eingetragen, und da du diese nicht definierst, kann irgendwas in der Variable stehen).

Und dann bräuchte ich letztendlich aus der zweiten Zeile einen Wert und aus der letzten.
Und aus dem Rest nix? Dann gäbe es wesentlich schnellere Möglichkeiten als mit einem Zähler (fseek() und Konsorten).

Das würde ich dann über einen Zähler oder so machen, sprich gucken wann der in der 2 Zeile ist und den Wert wegschreiben und wenn er am Schluss angekommen ist, hat er ja die letzte Zeile....
Ja, das würde gehen.

Gruss
cwriter