WinCC WinCCv7 - csv-String splitten mit sscanf

RONIN

Level-3
Beiträge
2.518
Reaktionspunkte
765
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo an alle.

Ich scheitere in WinCC v7.4SP1Upd4 gerade daran einen csv-String zu verarbeiten. Bin zugegebenermaßen auch nicht geübt da darin.
Der Code sieht in etwa so aus:
Code:
char buffer[1024];
int result;

[COLOR=#008000]//Zeileninhalt[/COLOR]
char szInhaltZeile[1024];        //gesamte Zeile
char szInhaltZeilenNr[256];        //ZeilenNrstring
char szInhaltBezeichnung[256];    //Bezeichnung
char szInhaltBezeichnung2[256];    //Bezeichnung Sprache 2
char szInhaltIBS[256];            //IBS-Wert
char szInhaltAktuell[256];        //aktueller Wert

[COLOR=#008000]//... Hier zuerst einige Dateioperationen zum Einlesen der Zeilen

//Lesen der Zeile aus dem File[/COLOR]
fgets(buffer, 1024, pFile);
printf("Buffer %s\n", buffer);
[COLOR=#008000]
//In buffer steht dann eine Zeile. Beispiel:[/COLOR]
[COLOR=#008000]//P11;Maximaler Vorrückweg Zwischenspeicher Fremdaufgabe [1/10s];...;10;10[/COLOR]

[COLOR=#008000]//Versuch an die Komponenten zu kommen[/COLOR]
result = sscanf(buffer, "%[^;],%[^;],%[^;],%[^;],%[^\n]", szInhaltZeilenNr, szInhaltBezeichnung, szInhaltBezeichnung2, szInhaltIBS,  szInhaltAktuell);
[COLOR=#008000]
//Print Ergebnis[/COLOR]
printf("Result %d\n", result);        
printf("ZeileNr %s\n", szInhaltZeilenNr);
printf("Bez %s\n", szInhaltBezeichnung);
printf("Bez2 %s\n", szInhaltBezeichnung2);
printf("IBS %s\n", szInhaltIBS);

Als Ergebnis bekomme ich dann
Code:
[COLOR=#008000]//Print Ergebnis[/COLOR]
Result 1
ZeileNr P11
Bez
Bez2
IBS
Wie man sieht liefert hört sscanf nachdem es den ersten String gefunden hat einfach auf.
Ich denke es wird am Formatstring liegen, hab auch schon alles mögliche probiert. Komm aber nicht drauf.

Vielleicht weiß jemand was ich probieren müsste. Danke.
 
Ohne jetzt WinCC zur Verfügung zu haben, versuch mal folgendes:
Code:
CSV_Container = split(buffer,";")
i = 0
for each a in CSV_Container
    column[i] = a
    i = i + 1
next

Code:
char delimiter[] = @";";
char *ptr;
char ColumnContainer[10][255]
int i;

ptr = strtok(buffer, delimiter);
i = 0;

while(ptr != NULL) {
  ColumnContainer[i] = ptr;
  ptr = strtok(NULL, delimiter);
}
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
result = sscanf(buffer, "%[^;],%[^;],%[^;],%[^;],%[^\n]", szInhaltZeilenNr, szInhaltBezeichnung, szInhaltBezeichnung2, szInhaltIBS,  szInhaltAktuell);

Oder versuch mal:

Code:
result = sscanf(buffer, "%[^; ], %[^; ], %[^; ], %[^; ], %[^\n]", szInhaltZeilenNr, szInhaltBezeichnung, szInhaltBezeichnung2, szInhaltIBS,  szInhaltAktuell);
 
Hallo Krumnix

Danke schonmal für die ersten Ideen. Das Einfügen der Blanks beim sscanf bringt leider nichts. Ich bekomme weiterhin nur den ersten String mit "P11" beschrieben.
Das mit dem zerlegen über strtok hab ich im Internet auch schon gefunden gehabt und getestet, das funktioniert.

Ich möchte aber herausfinden warum die fscanf bzw. sscanf Varianten nicht gehen.
Hintergrund ist dass ich gerade ein altes WinCC v5 Fremd-Projekt auf V7 migrieren muss. Da wurde extensiv mit csv-Dateien gearbeitet.
Zum Lesen wird überall fscanf verwendet. Nach der Migration scheitern alle diese Skripte an den fscanf-Befehlen.

Testweise habe ich das das fscanf gegen eine fgets/sscanf-Kombination ersetzt. Das ist im Endeffekt der Code den ich oben gezeigt hatte.
Auf strtok umbauen heißt natürlich alles anfassen.

Kann es sein dass es Inkompatibilitäten zwischen WinCCv5 und v6/7 im Bezug auf fscanf und sscanf gibt?
 
Du musst zwischen den einzelnen Scansets auch das Trennzeichen deiner CSV-Datei schreiben. Wenn das ein Semikolon sein soll, dann
Code:
result = sscanf(buffer, "%[^;];%[^;];%[^;];%[^;];%[^\n]", szInhaltZeilenNr, szInhaltBezeichnung, szInhaltBezeichnung2, szInhaltIBS,  szInhaltAktuell);
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke Thomas damit geht es.
Ich hatte die Scanset-Angabe aus nem Beispiel heraus die welches ein Komma als Trennszeichen hatte und "%[^;],%[^;]" zeigte.
Ich hab dann nur die Komma in den eckigen Klammern ersetzt und dachte die Kommas danach sind andere Trennzeichen.
Verstanden habe ich die Scanset-Angabe aber auf keinen Fall.

Wenn ich [%^;] verwende dann liest die Funktion bis zum ersten Auftreten des Semikolon. Wofür dann die weitere Angabe mit ";".
Gibt man das damit quasi so an: "Zeichen bis zum ersten ;" - "Das ; selbst" - "Zeichen bis zum nächsten ;" - "Das ; selbst"... usw. ???

Jetzt habe ich nur noch die Frage warum die originalen Scripte mit fscanf nicht hinhaut. Das sehen in etwa so aus:
Code:
[COLOR=#008000]//Einlesen der Überschrift aus altem File und abspeichern in Zwischenfile[/COLOR]
    fscanf (pFile, "%349[^\n]s", szInhaltZeile); 
    fprintf (pFileZw, "%s\n", szInhaltZeile);


[COLOR=#008000]    //Durchlauf der Zeilen[/COLOR]
    for (nCnt=0 ; nCnt < nAnzahl ; nCnt++)
    {
        if ((nCnt % 20) == 0)
        SetTagWord ("ParameterProzent", (short)(nCnt*100/nAnzahl));


[COLOR=#008000]        //Einlesen der Zeile aus altem File[/COLOR]
        fscanf (pFile, "%199[^;]s", szInhaltZeilenNr);        //ZeilenNrstring
        fscanf (pFile, "%199[^;]s", szInhaltBezeichnung);    //Bezeichnung
        fscanf (pFile, "%199[^;]s", szInhaltBezeichnung2);    //Bezeichnung Sprache 2
        fscanf (pFile, "%199[^;]s", szInhaltIBS);            //IBS-Wert
        fscanf (pFile, "%199[^\n]s", szInhaltAktuell);        //aktueller Wert
Die ganzen Skripte hauen nicht mehr so recht hin. Funktioniert das fscanf überhaupt wenn man das so in einzelne Aufrufe teilt?#
Wahrscheinlich schon da ja man aus einem Filestream liest. Frage ist auch auf das "s" hinter dem "[^;]" richtig ist.
Die oben gezeigten 5 Aufrufe von fscanf sollten ja eigentlich auch die eine Zeile in Einzelstrings zerlegen.
 
Zuletzt bearbeitet:
Mit %[^;] liest sscanf den String bis zum Vorkommen des Semikolons.
Der interne Zeiger steht anschließend direkt auf dem Semikolon, sodass alle folgenden %[^;] direkt terminieren ohne ein Zeichen einzulesen.
Mit dem Semikolon dazwischen %[^;];%[^;] wird nach dem ersten Semikolon erwartet, dass im String auch ein Semikolon folgt, und wenn das der Fall ist dann wird der Zeiger entsprechend weitergesetzt und der nächste Scanset ausgewertet.

fscanf setzt den Dateizeiger nach dem Aufruf weiter auf das nächste Zeichen nach dem Ergebnis, funktioniert also etwas anders als sscanf.

Dass sscanf den Zeiger nicht anfasst, kann man auch an der Parameterdefinition mit "const char * s" erkennen.
 
Ja, Danke. Das klingt logisch.
Nur um mein Verständnis zu festigen, man kann fscanf ja dann eigentlich auch so aufrufen...
Code:
fscanf(pFile, "%[^;];%[^;];%[^;];%[^;];%[^\n]", szInhaltZeilenNr, ...

Auch den Grund warum im Originalcode das "s" am Ende des format-string "%199[^;]s" angegeben wird hab ich noch nicht raus.
Laut der Beschreibung von fscanf weist man mit dem "s" auf einen non-whitespace Zeichensequenz hin. Sehe ich in den meisten Beispielen direkt mit "%s" verwendet. Das [set] davor verweist ebenso auf eine Zeichensequenz. Der erste Teil mit "%199[^;]" sollte ja schon auf eine Zeichensequenz bis zum ersten ";" mit max. Länge von 199 hinweisen. Warum als nochmal ein "s" spezifizieren. Oder ist das redundant?

Ich werde morgen nochmal genau posten was die fscanf-Aufrufe des Originalprojekts nach der Migration genau für Ergebnisse liefern. Ich bin mir nicht mehr hundertprozentig sicher was da rauskam. Damit wird es sicher einfacher sein das Problem ausfindig zu machen.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Das mit fscanf sollte auch so mit einer Zeile funktionieren.

Die mehrfachen Aufrufe mit einzeln "%199[^;]s" funktionieren meiner Meinung nach nicht. Das einzelne s and der Stelle ist ohne % ein Literal und kein Format-String.
Wenn dort %199[^;]%s stünde, dann bräuchtest du aber einen zweiten Parameter für den Zielstring, oder mit einem Sternchen %199[^;]%*s würde das Ergebnis davon weggeworfen und es ginge auch ohne.

Meiner Meinung nach würde bei den von dir gezeigten nacheinanderfolgenden Aufrufen von fscanf immer der gleiche Wert gelesen.
Mit %199[^;]; oder %199[^;]%*c sollte es funktionieren, zumindest solange man sich nicht am Zeilenende befindet.
 
Ja, genau. So hatte ich mir das mit dem "s" hinten dran auch gedacht. Betreffend dem "s" bin ich im Internet aber auf verschiedene Quellen gestoßen. Bei machen Tutorials -LINK- ist das "s" in Verwendung. Die Idee scheint also nicht ganz aus der Welt gegriffen zu sein. Ist das eine Implementations-/Compiler-Sache?

Ich konnte die Skripte mit meinem angesammelten Verständnis der Scanset-Struktur wieder zum Laufen bringen.
Folgendes Original funktioniert nicht:
Code:
[COLOR=#008000]//Erste Zeile    [/COLOR]
fscanf (pFile, "%349[^\n][B][COLOR=#ff0000]s[/COLOR][/B]", szInhaltZeile); 


[COLOR=#008000]//Datenzeilen[/COLOR]
for (nCnt=0 ; nCnt < nAnzahl ; nCnt++)
{
    //Daten aus Zeile
    fscanf (pFile, "%199[^;][B][COLOR=#ff0000]s[/COLOR][/B]", szInhaltZeilenNr);        //ZeilenNrstring
    fscanf (pFile, "%199[^;][B][COLOR=#ff0000]s[/COLOR][/B]", szInhaltBezeichnung);    //Bezeichnung
    fscanf (pFile, "%199[^;][B][COLOR=#ff0000]s[/COLOR][/B]", szInhaltBezeichnung2);    //Bezeichnung Sprache 2
    fscanf (pFile, "%199[^;][COLOR=#ff0000][B]s[/B][/COLOR]", szInhaltIBS);        //IBS-Wert
    fscanf (pFile, "%199[^\n][B][COLOR=#ff0000]s[/COLOR][/B]", szInhaltAktuell);        //aktueller Wert
}
Folgendes funktioniert:
Code:
[COLOR=#008000]//Erste Zeile    [/COLOR]
fscanf (pFile, "%349[^\n][COLOR=#ff0000][B]%*c[/B][/COLOR]", szInhaltZeile); 


[COLOR=#008000]//Datenzeilen[/COLOR]
for (nCnt=0 ; nCnt < nAnzahl ; nCnt++)
{
    //Daten aus Zeile
    fscanf (pFile, "%199[^;][COLOR=#ff0000][B];[/B][/COLOR]", szInhaltZeilenNr);        //ZeilenNrstring
    fscanf (pFile, "%199[^;][COLOR=#ff0000][B];[/B][/COLOR]", szInhaltBezeichnung);    //Bezeichnung
    fscanf (pFile, "%199[^;][COLOR=#ff0000][B];[/B][/COLOR]", szInhaltBezeichnung2);    //Bezeichnung Sprache 2
    fscanf (pFile, "%199[^;][COLOR=#ff0000][B];[/B][/COLOR]", szInhaltIBS);        //IBS-Wert
    fscanf (pFile, "%199[^\n][COLOR=#ff0000][B]%*c[/B][/COLOR]", szInhaltAktuell);        //aktueller Wert
}
Anscheinend scheint sich da was zwischen WinCC v5 und v6.2 getan zu haben (Zum testen der Skripte oben hatte ich v6.2.3.14) im Einsatz.
Die Sache hatte unter v5 sicher funktioniert.

Danke nochmal für die Hilfe an alle.
 
Zuletzt bearbeitet:
Auf der verlinkten Seite steht aber auch unten im Kommentar, dass das "s" nicht zum Formatstring gehört. Ich meine scanf verhält sich hier auch etwas anders als z.B. sscanf.
Ich hatte am Montag noch zu Hause in mein K+R Büchlein geschaut, damals gab es jedoch noch keine Scansets. Ansonsten ist das immer eine gute Quelle, weil dort auch Beispielimplementierungen der Standardfunktionen gezeigt werden und man daran selber erkennen kann wie diese arbeiten sollten, wenn die Beschreibung nicht ganz so zuverlässig ist.

Ich konnte selber auch nicht aus einer Beschreibung herauslesen, dass es überhaupt zulässig ist mehrfach mit fscanf auf eine Datei zu lesen. Meine Vermutung war ja, dass der Dateizeiger weitergesetzt wird. Das hatte ich am Montag auch nochmal probiert und überprüft, aber der Zeiger wird nicht verändert. Ich weiß auch nicht wo diese Information gespeichert wird.

Es gibt ein paar Standardfunktionen wie z.B. strtok die nicht reentrant sind, weil sie Informationen über den aktuellen Zustand intern abspeichern. Unter diesen Funktionen ist fscanf aber nicht geführt.
Diese Scansets habe ich selber noch nie benutzt, würde mich aber schon interessieren ob das so zulässig ist, oder ob es nur zufällig funktioniert. Dass es früher mal funktionierte würde eher dafür sprechen, dass das nicht so verwendet wird wie angedacht.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe nochmal versucht das nachzuvollziehen, das Verhalten ist meiner Meinung nach nicht eindeutig definiert.
Zumindest wird die aktuelle Leseposition bei fscanf in der FILE Struktur der mit dem File-Pointer an fscanf übergeben wird gespeichert. Darin sind noch weitere Werte enthalten. Die aktuelle Leseposition lässt sich z.B. mit fgetpos auslesen oder mit fseek neu aufsetzen.

Dass es bei einer älteren WinCC Version des C-Compilers (bzw. Interpreters) mit dem gezeigten Code funktionierte, kann ich mir nur so erklären, dass dort wenn der Mustervergleich fehlschlägt der Lesezeiger schon auf das nächste Zeichen weitergesetzt wurde, somit dieses Zeichen dann mehr oder weniger verloren geht. Da das nur selten im Sinne des Anwenders ist, wurde das vermutlich korrigiert.

Ich habe mir mal die Umsetzung von fscanf im GCC angesehen, das ist ein echtes Monstrum diese Funktion. Interessant ist auch ein Kommentar wo u.A. die Scanlisten verarbeitet werden:
"This is not a constant lookup time but who uses this feature deserves to be punished."
Scheint nicht so gut angesehen sein diese Option...
 
Danke nochmal Thomas dass du dir das so genau angeschaut hast.
Kann mir gut vorstellen dass die Umsetzung dieser Scansets ein Monstrum ist. Der Kommentar im Code sagt auch einiges.
Für den Laien (mich) scheinen die Scansets ja prädestiniert für das auslesen von csv-Dateien zu sein. Ich hab bis jetzt immer mit fgets und strtok gearbeitet. Was ist denn deiner Meinung nach die Standard-Goto-Prozedur zum verarbeiten von csv-Dateien?
 

Similar threads

Zurück
Oben