1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

Einlesen von Fließkommazahlen mit sscanf

Dieses Thema im Forum "C/C++" wurde erstellt von WingMan81, 27. September 2009.

  1. WingMan81

    WingMan81 Grünschnabel

    [C] Einlesen von Fließkommazahlen mit sscanf

    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);

    [/C]

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

    Code (Text):
    1. 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 (Text):
    1. 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: 27. September 2009
  2. saftmeister

    saftmeister Nutze den Saft! Premium-User

    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:

    Code (C++):
    1.  
    2. /* Zeiger auf den Anfang der Zeile */
    3. char *p = puffer;
    4. char *part = puffer;
    5. char value[4][20];
    6. int i = 0;
    7. do{
    8.   if(*p == ',')
    9.   {
    10.      *p = 0;
    11.      strcpy(value[i], part);
    12.      i++;
    13.      p++;
    14.      part = p;
    15.      continue;
    16.   }
    17.   p++;
    18.   if(*p == 0) break;
    19. } while(p != NULL);
    20.  
    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.
  3. WingMan81

    WingMan81 Grünschnabel

    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
  4. Matthias Reitinger

    Matthias Reitinger ɐɯıǝɹ Premium-User

    Welche Formate bei der Ein- und Ausgabe verwendet werden, hängt von der gesetzten „Locale“ ab. Diese kann mit [url=http://www.cplusplus.com/reference/clibrary/clocale/setlocale/]setlocale[/url] geändert werden. Einen Punkt als Dezimaltrennzeichen erhält man beispielsweise mit der Locale "C".

    Grüße, Matthias
    saftmeister sagt Danke.
  5. saftmeister

    saftmeister Nutze den Saft! Premium-User

    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.
  6. deepthroat

    deepthroat Premium-User

    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.

    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));[/c]
    Gruß
  7. WingMan81

    WingMan81 Grünschnabel

    Hallo,

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

    Viele Grüße
    WingMan
  8. saftmeister

    saftmeister Nutze den Saft! Premium-User

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

    Ebenfalls geschenkt. Als Schleifen-Abbruch-Bedingung hätte ich ebenso 1 wählen können.
    Mag sein, das ich was übersehe. Aber ich prüfe doch

    Code (C++):
    1.  
    2. if(*p == 0) break;
    3.  
    Sollte das nicht genügen, wenn ich davon ausgehen darf, das der puffer Null-terminiert ist? Was übersehe ich?
  9. deepthroat

    deepthroat Premium-User

    Du könntest die Daten entsprechend konvertieren.
    Oder du hättest es auch einfach vernünftig programmieren können... ;-]
    Du springst über die Abfrage mit continue drüber. Schlechter Programmierstil break und continue zu benutzen obwohl absolut nicht notwendig.

    Gruß
  10. saftmeister

    saftmeister Nutze den Saft! Premium-User

    So besser?

    Code (C++):
    1.  
    2. /* Zeiger auf Puffer-Anfang setzen */
    3. char *p = puffer;
    4. char *part = puffer;
    5.  
    6. /* Result-Puffer */
    7. char value[4][20];
    8.  
    9. /* Count-Variable */
    10. int i = 0;
    11.  
    12. /* Solange Ende des Null-terminierten Puffers nicht erreicht */
    13. while(*p != 0)
    14. {
    15.   /* Wenn Komma gefunden */
    16.   if(*p == ',')
    17.   {
    18.     /* Terminiere Teil von part bis Komma */
    19.     *p = 0;
    20.    
    21.     /* Kopiere Teil in Result-Variable */
    22.     strcpy(value[i++], part);
    23.    
    24.     /* Setze neuen Part-Anfang aktuelle Position + 1 (nach 0-Terminator) */
    25.     part = p+1;
    26.   }
    27.  
    28.   /* Inkrementiere Zeiger */
    29.   p++;
    30. }
    31.  
    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.
  11. deepthroat

    deepthroat Premium-User

    Ja.

    Außer das man solche Kommentare wie
    besser weglassen sollte. Das dort der Zeiger inkrementiert wird, sieht man schließlich auch so.

    Gruß
    Zuletzt bearbeitet: 28. September 2009
  12. Matthias Reitinger

    Matthias Reitinger ɐɯıǝɹ Premium-User

    Alternativ-Vorschlag:
    Code (C++):
    1. char value[4][20];
    2. int i = 0;
    3. char *part = strtok(puffer, ",");
    4. while (part != NULL)
    5. {
    6.   strcpy(value[i++], part);
    7.   part = strtok(NULL, ",");
    8. }
    Grüße, Matthias

Diese Seite empfehlen