Einlesen von Fließkommazahlen mit sscanf

WingMan81

Grünschnabel

Hallo,

wie angedroht kommt auch schon mein nächstes Thema ;)

Dieses mal versuche ich aus einer Textdatei Werte einzulesen.
Das einlesen mache ich mittels:
C:
int a;
double b, c, d;

// puffer = Zeile aus Textdatei
sscanf(puffer,"%i,%lf,%lf,%lf\n",&a, &b, &c, &d);

puffer ist dabei ein char Array und beinhaltet jeweils eine Zeile aus einer Textdatei.
Eine Zeile der Textdatei hat das Format:

Code:
1,2.456,44.5558.16,55544

Das Problem daran ist nun, dass aus irgendeinem Grund bei mir das "," als Trennzeichen einer Fließkommazahl genommen wird und nicht wie üblich der ".".
Da die Textdateien aber vorgegeben sind muss ich den "." als Trennzeichen einer Fließkommazahlen behandeln. Ich verstehe auch überhaupt nicht wieso (wie ja doch normal) der Punkt nicht erkannt wird :confused:

Ändere ich die Textdatei in
Code:
1,2,456,44,5558,16,55544

dann funktioniert alles und ich bekomme a=1 b= 2.456 c=44.5558 und d=16.55544. Aber wie gesagt: Format der Textdateien ist gegeben und ich kenne es auch von anderen Sprachen (Java,...) nur so das "." als Trennziffer erkannt werden. Laut API soll das ja auch bei C so sein. Seltsam, nicht wahr

Hoffe ihr könnt mir wieder einmal helfen ;)

Viele Grüße und noch einen schönen Rest-Sonntag,
WingMan
 
Zuletzt bearbeitet:
Jaja, das liebe sscanf. Das hat mir auch schon den ein oder andern Arbeitstag versüßt ;-)

Nein im Ernst. Ich bin dazu übergegangen, diese Hilfsfunktionen nicht zu verwenden, stattdessen mittels Pointer über die Zeile zu sausen und bedingungsbehaftet dann die Werte in Strukturen oder simple Variablen zu parsen. Das ist zwar nicht so elegant wie sscanf aber zuverlässig:

C++:
/* Zeiger auf den Anfang der Zeile */
char *p = puffer;
char *part = puffer;
char value[4][20];
int i = 0;
do{
  if(*p == ',')
  {
     *p = 0;
     strcpy(value[i], part);
     i++;
     p++;
     part = p;
     continue;
  }
  p++;
  if(*p == 0) break;
} while(p != NULL);

Anschließend kannst du mit value[0] - [3] alles tun, was du willst. Es ist außerdem noch relativ schnell. Sicherlich kann man dies auch noch optimieren.
 
Hi,
vielen Dank für Deine Antwort! Ich werde das auf jeden Fall auch mal so austesten, aber verstehen möchte ich doch ganz gern warum meine Lösung nicht so funktioniert wie sie es doch eigentlich sollte.
Das ganze für mich ja mehr eine Übungsaufgabe und weniger für einen Produktiveinsatz gedacht, daher suche ich natürlich weiter nach der Auflösung ;)

Aber wie gesagt: Vielen Dank für Deine Antwort!

VG,
WingMan
 
Das Problem daran ist nun, dass aus irgendeinem Grund bei mir das "," als Trennzeichen einer Fließkommazahl genommen wird und nicht wie üblich der ".".
Da die Textdateien aber vorgegeben sind muss ich den "." als Trennzeichen einer Fließkommazahlen behandeln. Ich verstehe auch überhaupt nicht wieso (wie ja doch normal) der Punkt nicht erkannt wird :confused:
Welche Formate bei der Ein- und Ausgabe verwendet werden, hängt von der gesetzten „Locale“ ab. Diese kann mit setlocale geändert werden. Einen Punkt als Dezimaltrennzeichen erhält man beispielsweise mit der Locale "C".

Grüße, Matthias
 
Danke für den Hinweis mit der Locale. Daran habe ich gar nicht gedacht. Noch ein Grund mehr, auf glibc-Funktionen zu verzichten, wenn man multi-linguale Applikationen entwickelt und darin Zeilen parsen muss. Vielen Dank, nun weiß ich wenigstens den Grund für mein Gefühl, es zu Fuss implementieren zu müssen.
 
Hi.
Danke für den Hinweis mit der Locale. Daran habe ich gar nicht gedacht. Noch ein Grund mehr, auf glibc-Funktionen zu verzichten, wenn man multi-linguale Applikationen entwickelt und darin Zeilen parsen muss.
Ich finde zu ziehst die falschen Schlüsse. Das heißt nur, das man in Konfigurationsdateien bzw. Datendateien die Standardlocale verwenden sollte und nicht lokalisierte Daten abspeichert.

Abgesehen davon ist deine Funktion fehlerhaft. Wie sollte denn p plötzlich NULL sein können (vor allem nachdem du es schon x-mal verwendet hast)? Außerdem kann es passieren, dass du über das Ende des Strings hinaus liest. Ein schönes Beispiel dafür, das man doch lieber die Funktionen der C Bibliothek (richtig) anwenden sollte bevor man sich was selber bastelt.

@WingMan81: Um herauszufinden welche Locale gerade aktiv ist, kannst du setlocale aufrufen:
C:
puts(setlocale(LC_NUMERIC, NULL));
Gruß
 
Hallo,

vielen Dank für die Antworten! Das setlocal() wirkt wahre Wunder ;) Es funktioniert nun alles so wie es soll!

Viele Grüße
WingMan
 
Hi.
Ich finde zu ziehst die falschen Schlüsse. Das heißt nur, das man in Konfigurationsdateien bzw. Datendateien die Standardlocale verwenden sollte und nicht lokalisierte Daten abspeichert.

Geschenkt. Brauch man nicht drüber zu diskutieren. Was mach ich aber mit Daten-Dateien, die bereits lokalisiert gespeichert wurden?

Abgesehen davon ist deine Funktion fehlerhaft. Wie sollte denn p plötzlich NULL sein können (vor allem nachdem du es schon x-mal verwendet hast)?

Ebenfalls geschenkt. Als Schleifen-Abbruch-Bedingung hätte ich ebenso 1 wählen können.
Außerdem kann es passieren, dass du über das Ende des Strings hinaus liest.

Mag sein, das ich was übersehe. Aber ich prüfe doch

C++:
if(*p == 0) break;

Sollte das nicht genügen, wenn ich davon ausgehen darf, das der puffer Null-terminiert ist? Was übersehe ich?
 
Geschenkt. Brauch man nicht drüber zu diskutieren. Was mach ich aber mit Daten-Dateien, die bereits lokalisiert gespeichert wurden?
Du könntest die Daten entsprechend konvertieren.
Ebenfalls geschenkt. Als Schleifen-Abbruch-Bedingung hätte ich ebenso 1 wählen können.
Oder du hättest es auch einfach vernünftig programmieren können... ;-]
Mag sein, das ich was übersehe. Aber ich prüfe doch

C++:
if(*p == 0) break;

Sollte das nicht genügen, wenn ich davon ausgehen darf, das der puffer Null-terminiert ist? Was übersehe ich?
Du springst über die Abfrage mit continue drüber. Schlechter Programmierstil break und continue zu benutzen obwohl absolut nicht notwendig.

Gruß
 
So besser?

C++:
/* Zeiger auf Puffer-Anfang setzen */
char *p = puffer;
char *part = puffer;

/* Result-Puffer */
char value[4][20];

/* Count-Variable */
int i = 0;

/* Solange Ende des Null-terminierten Puffers nicht erreicht */
while(*p != 0)
{
  /* Wenn Komma gefunden */
  if(*p == ',')
  {
    /* Terminiere Teil von part bis Komma */
    *p = 0;
    
    /* Kopiere Teil in Result-Variable */
    strcpy(value[i++], part);
    
    /* Setze neuen Part-Anfang aktuelle Position + 1 (nach 0-Terminator) */
    part = p+1;
  }
  
  /* Inkrementiere Zeiger */
  p++;
}

Das war einfach so schnell hingeschludert als Möglichkeit das Problem zu lösen. Zugegeben, es ist nicht sehr schön. Aber es hat mein Problem damals gelöst.
 
Zurück