Daten aus CSV Datei mit ANS C lesen

M0zart

Grünschnabel
Also sowohl die csv als auch der c Code wird in WinCC erstellt und verarbeitet. Das ist alles nur ein Umweg den ich gegen muss, weil Siemens das gerne kompliziert macht (langjährige Erfahrung).

Ich hab eigentlich im WinCC eine Tabelle in der Werte aus einer Datenbank stehen. Jetzt ist die Tabelle variabel d.h. es kann irgendeine Zeitspanne ausgewählt werden und ich muss dann von dieser Zeitspanne den ersten und den letzten Wert raus suchen, um damit weiter zu rechnen. Jetzt ist das aber so, dass ich nicht auf die Datenbanken zugreifen kann, weil diese verschlüsselt sind. Deswegen wurde mir von Siemens geraten, das über einen Export Import zu machen :D... also erst Tabelle in csv exportieren und direkt wieder zu importieren.

Export bekomm ich hin, das ist nur ein Klick, aber bei dem Import hapert es.

Die zwei Werte die ich praktisch raus schreiben müssen dringend float sein, weil ich sonst im weiteren Verarbeitungsschritt Probleme habe bzw. nichts damit anfangen kann.

Aber vielen vielen Dank, ihr habt mir sehr weitergeholfen. Ich Probier das ganze aus wenn ich wieder zu Hause bin (nächste Woche) und berichte dann.
 

M0zart

Grünschnabel
Hallo cwriter,
was meinst du damit genau? Ich bin immer noch unsicher was ich jetzt genau machen soll? Soll ich das fgets durch fscanf austauschen und bleibt der mittlere Teil erhalten:
C:
          strTag=buffer;
          pTmp = strchr(buffer,(int)';');
          *pTmp='\0';
          strValue=++pTmp;
          strValue[strlen(strValue)-1]='\0';

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

Mit fgets hol ich mir ja jede Zeile nach einander. Buffer ist die maximale Länge die ich mit übernehme (in dem Fall 80, hab ich ja bei MaxLineLength definiert). das übergebe ich praktisch strTag. Aber was ab
C:
pTmp=strchr(buffer,(int)';');
versteh ich nicht mehr wirklich. Ich denk da wird der ganze String (Ganze Zeile) aufgetrennt und zwar immer bei dem ';' Simikolon oder?
das *pTmp='\0'; damit setzte ich am ende noch die \0, damit der weiß das hier ende ist?
dann wird pTmp erst um eins erhöht und in strValue geschrieben (
C:
 strValue=++pTmp;
)?
C:
strValue[strlen(strValue)-1]='\0';
da bin ich wieder voll raus.
und hier:
C:
    pTmp=strchr(strValue,(int)',');
          if(pTmp !=NULL){
             *pTmp='.';
ersetzt er einfach das Komma durch einen Punkt.

in meinem strValue steht jetzt so gesehen jede Zelle von mir und wird ständig wieder überschrieben?

Und wo bzw. wann wird da strTag beschrieben?
 
Zuletzt bearbeitet:

cwriter

Erfahrenes Mitglied
was meinst du damit genau?
Source Lines Of Code.
(Der Code ist syntaktisch aufgeräumter, aber semantisch äquivalent).

Soll ich das fgets durch fscanf austauschen und bleibt der mittlere Teil erhalten
fscanf ersetzt fgets und sscanf (nur in Verbindung mit setlocale() ). Der Inhalt der Schleife bleibt damit nicht erhalten.

versteh ich nicht mehr wirklich. Ich denk da wird der ganze String (Ganze Zeile) aufgetrennt und zwar immer bei dem ';' Simikolon oder?
Nein, das wäre die Funktionsweise von strtok().
strchr könnte so implementiert sein:
C:
const char* strchr(const char* str, const char* delim)
{
for(const char* it = str; it < str + strlen(str); it++)
for(const char* it2 = delim; it2 < delim + strlen(delim); it2++)
if(*it == *it2) return it;
return (const char*)0;
}
Es gibt also die Position des ersten Zeichens in Delim innerhalb des Strings zurück. Der String wird aber NICHT verändert.
das *pTmp='\0'; damit setzte ich am ende noch die \0, damit der weiß das hier ende ist?
Nein, du ersetzt das Semikolon mit 0 (strtok()-Behavior).
dann wird pTmp erst um eins erhöht und in strValue geschrieben
Ja. pTmp zeigt zuerst auf den nächsten Teilstring, dann wird erhöht.

da bin ich wieder voll raus.
Die Länge des 2. Strings wird um 1 gekürzt.

in meinem strValue steht jetzt so gesehen jede Zelle von mir und wird ständig wieder überschrieben?
Überschrieben.

Gruss
cwriter
 

M0zart

Grünschnabel
Also ich hab mal jetzt jede Zeile von mir Ausgegeben und da hab ich folgendes raus:
C:
#include "apdefap.h"

int gscAction( void )
{
#define MaxLineLength 80

FILE *fpFile;
char *strTag, *strValue, *pTmp;
char buffer[MaxLineLength];
double dVal, dVal2, dVal3;
int i=0;

fpFile = fopen("c:\\Users\\Desktop\\Mappe1.csv","r");
printf("C-Skript: lese Datei: Mappe1.csv\n");
if(fpFile !=NULL)
{
     while(fgets(buffer,MaxLineLength, fpFile))
     {
          i++;
          strTag=buffer;
printf("strTag1: %s   Buffer: %s\n", strTag, buffer);
          pTmp = strchr(buffer,(int)';');
printf("pTmp1: %s\n", pTmp);
          *pTmp='\0';
printf("pTmp2: %s\n", pTmp);
          strValue=++pTmp;
printf("strValue1: %s\n", strValue);
          strValue[strlen(strValue)-1]='\0';
printf("strValue2: %s\n", strValue);
          pTmp=strchr(strValue,(int)',');
printf("pTmp3: %s\n", pTmp);
          if(pTmp !=NULL){
             *pTmp='.';
printf("pTmp4: %s\n", pTmp);
          }
          sscanf (strValue, "%lf", &dVal);
          //SetTagDouble(strTag,dVal);
          printf("%s Value: %lf pTmp: %s  strValue: %s \r\n", strTag, dVal, pTmp, strValue);
printf("------------------------------------------Zeile: %d--------------------------------------------\n", i);
     }
     fclose(fpFile);
} else{
printf("Error: File not found!\n");
}

return 0;
}
Im Anhang ist die Ausgabedatei. Wie man sieht, nimmer der Code die Nachkommastelle nicht mit. also er nimmt die Zahl vor dem Komma, ersetzt das Komma durch den Punkt, aber vernachlässigt die Nachkommastelle. Ich hab auch mal jetzt die csv Datei um eine Spalte ergänzt.

und cwriter: wenn ich jetzt dieses strtok() von dir verwende, wird die Zeile ja immer bei dem Simikolon getrennt oder? Das heißt ich kann die ganze Zeile direkt so nehmen, dieses strtok() anwenden und direkt in Variablen speichern, sprich erste ist %s und dan %lf und noch ein %lf. Ich kenn ja den aufbau meiner csv. Die Spalten bleiben immer gleich nur die Anzahl der Zeilen variiert, aber das ist ja bei der Schleife egal.
 

Anhänge

  • Ausgabe.PNG
    Ausgabe.PNG
    12,3 KB · Aufrufe: 3
Zuletzt bearbeitet:

M0zart

Grünschnabel
  • while(fscanf(fpFile, "%[^;]; %lf\n", strName, &strValue) == 2) // strName entsprechend ein Stringbuffer (kein "reiner" Pointer)
  • {
  • //Werte eingelesen
  • }
setlocale(LC_NUMERIC, ""); //Reset auf (System-)Standard

das wär praktisch der Code den ich verwenden müsste oder? Aber was meinst du mit //Werte eingelesen? Da muss ich doch nichts merh machen, weil ich oben doch schon in strName und strValue schreibe oder nicht?
 

M0zart

Grünschnabel
Also ich hab mal setlocale verwendet aber scheinbar nicht richtig. Sorry nochmal dafür das ich etwas länger brauche um das zu verstehen, aber wie gesagt, programmier eher selten mit C bzw. programmiere eigentlich garnicht.

C:
#include "apdefap.h"

int gscAction( void )
{

#define MaxLineLength 80

FILE *fpFile;
char *strTag, *strValue, *pTmp;
char buffer[MaxLineLength];
double dVal, dVal2, dVal3;
int i=0;

fpFile = fopen("c:\\Users\\Desktop\\Mappe1.csv","r");
printf("C-Skript: lese Datei: Mappe1.csv\n");
if(fpFile !=NULL)
{
setlocale(LC_NUMERIC, "de_DE");
while(fscanf(fpFile, "%[^;]; %lf; lf\n", strTag, &dVal, &dVal2)==2)
{
//Werte eingelesen?
}
setlocale(LC_NUMERIC, "");

          }

          sscanf (strValue, "%lf", &dVal);
          //SetTagDouble(strTag,dVal);
          printf("%s Value: %lf pTmp: %s  strValue: %s  Value2: %lf \r\n", strTag, dVal, pTmp, strValue, dVal2);

printf("------------------------------------------Zeile: %d--------------------------------------------\n", i);
     
     fclose(fpFile);

else{
printf("Error: File not found!\n");
}

return 0;
}

Folgende Fehlermeldungen beim Compiling:
line 18: error (003f) : undefined identifier 'setlocale'
line 18: error (003f) : undefined identifier 'LC_NUMERIC'
line 18: error (0066) : 'setlocale' is not a function
line 23: error (0066) : 'setlocale' is not a function
line 35: error (0040) : <ident> expected before 'else'
 

cwriter

Erfahrenes Mitglied
Im Anhang ist die Ausgabedatei. Wie man sieht, nimmer der Code die Nachkommastelle nicht mit.
Naja. Du hast ja auch einen Wert "zu viel": "1.2;1,1" ist ein unbequemes Datenformat.

Folgende Fehlermeldungen beim Compiling:
C:
#include <locale.h>

Da muss ich doch nichts merh machen, weil ich oben doch schon in strName und strValue schreibe oder nicht?
Partizip. Du musst die Werte nicht mehr einlesen, wohl doch aber verarbeiten, oder? Strvalue solltest du auch nicht verwenden... Wenn du den Code ein bisschen aufräumst, sollte es klarer werden.

und cwriter: wenn ich jetzt dieses strtok() von dir verwende, wird die Zeile ja immer bei dem Simikolon getrennt oder?
strtok() ist nicht threadsafe. Aber generell würde strtok einiges vereinfachen, ja. Du kannst es aber auch selbst threadsafe machen (siehe strtok_s() von Microsoft).
Ich habe aber ein Beispiel für strchr und nicht strtok gegeben.

Das heißt ich kann die ganze Zeile direkt so nehmen, dieses strtok() anwenden und direkt in Variablen speichern, sprich erste ist %s und dan %lf und noch ein %lf. Ich kenn ja den aufbau meiner csv. Die Spalten bleiben immer gleich nur die Anzahl der Zeilen variiert, aber das ist ja bei der Schleife egal.
Gewissermassen. Dann kannst du aber auch gleich *scanf() verwenden, was ein bisschen schöner (und mit Posix-Erweiterungen ("Halbstandard") sogar um einiges mächtiger) ist.

Gruss
cwriter
 

M0zart

Grünschnabel
Vielen Dank cwriter, bin schon sehr weit gekommen
Mein Code sieht wie folgt aus:
C:
#include "apdefap.h"

int gscAction( void )
{
#define MaxLineLength 80
FILE *fpFile;
char *strTag, *strValue, *pTmp;
char buffer[MaxLineLength];
char delimiter[]=";";
double dVal, dVal2, dVal3;
int i=0, j=0;
fpFile = fopen("C:\\Users\\18.01.2017 12_15.csv","r");
printf("C-Skript: lese Datei: Mappe1.csv\n");
if(fpFile !=NULL)
{
     while(fgets(buffer,MaxLineLength, fpFile))
     {
        
          strTag=buffer;
printf("strTag1: %s   Buffer: %s\n", strTag, buffer);
pTmp = strtok(strTag, delimiter);
printf("strTag2: %s    pTmp1: %s\n", strTag, pTmp);

//printf("dVal: %lf\n", dVal);
while(pTmp != NULL) {
i++;
printf("strTag3: %s      pTmp2: %s\n", strTag, pTmp);
dVal=atof(pTmp);
printf("dVal: %lf\n", dVal);
if(i==2)
{dVal2=dVal;}
if(i==3)
{dVal3=dVal;}

SetTagDouble("wert4", dVal2); //Wert rausschreiben
SetTagDouble("wert5", dVal3); //Wert rausschreiben

//nächsten Abschnitt erstellen
pTmp=strtok(NULL, delimiter);
printf("strTag4. %s      pTmp3: %s\n", strTag, pTmp);
//printf("dVal3: %lf\n", dVal3);
}
j++;

printf("------------------------------------------Zeile: %d     %d--------------------------------------------\n", i, j);
     }
     fclose(fpFile);
} else{
printf("Error: File not found!\n");
}
return 0;

}

Meine Ausgabe ist auch im Anhang zu sehen.
Also ich bekomm jetzt die einzelnen Werte raus gelesen, funktioniert alles wunderbar. Muss dann nur noch irgendwas finden wie ich die Werte rausschreibe die ich brauche (Werte aus zweiter Zeile und Wert aus letzter Zeile). Aber das bekomm ich schon hin, das wird nicht das Problem sein.

Jetzt hab ich nur noch ein Problem. Wenn ich die csv Datei in Excel öffne, hab ich zwei Varianten einmal:
Spalte1;Zeile1: wert1
Spalte2;Zeile1: wert2
Spalte3;Zeile1: wert3
Spalte1;Zeile2: wert4
Spalte2;Zeile2: wert5
Spalte3;Zeile2: wert6
Bei dieser Variante funktioniert das C-Skript.

Zweite Variante:
Spalte1;Zeile1: wert1;wert2;wert3
Spalte1;Zeile2: wert4;wert5;wert6
Spalte1;Zeile3: wert7;wert8;wert9

Bei dieser Variante funktioniert das Skript nicht. Ich muss erst die Datei in Excel öffnen und ';' als Trennzeichen eintragen, damit er die Felder trennt und neu speichern. Erst dann wird die Datei von dem Skript richtig verarbeitet. Da gibts doch sicher eine elegantere Lösung oder?
 

Anhänge

  • Ausgabe.PNG
    Ausgabe.PNG
    11,6 KB · Aufrufe: 1

M0zart

Grünschnabel
Das Problem hat sich auch erledigt :D haha. Ich muss einfach die csv Datei als UTF-8 Dateiformat speichern, dann klappt alles wunderbar.
vielen Dank nochmal für die hilfe