Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei



  • Hi(gh)!

    Zum besseren Verständnis erst einmal eine Beschreibung meines aktuellen Programmierprojektes:

    "klima" fragt Temperatur- und Niederschlagsdaten (langjährige Monatsmittelwerte) von der Standardeingabe ab (in Zukunft auch aus Dateien) und berechnet daraus Klimaformeln nach Köppen und Geiger (siehe auch https://en.wikipedia.org/wiki/Köppen_climate_classification). Es soll diese Daten als Binärdatei auf die Festplatte schreiben, und zwar der Reihe nach erst den Namen der Wetterstation, der gleichzeitig leicht modifziert auch als Dateiname dient (30 Zeichen langer String), dann die Klimaepoche (das ist der Zeitraum von normalerweise 30 Jahren, auf den sich die Monatsmittelwerte beziehen; 10 Zeichen langer String), die geographische Breite (vorerst nur ein unsigned char für ganze Breitengrade, in Zukunft drei unsigned char-Werte für Breitengrade, Minuten und Sekunden), die Temperaturwerte (Array aus 12 floats), die Niederschlagswerte (Array aus 12 floats), schließlich die Klimaformel nach Köppen/Geiger (4 Zeichen langer String); bei allen Strings ist die Stringende-Null mitgezählt.

    Die dabei entstehenden Dateien sehen z. B. so aus:

    Kabul
    34
    1956-1983
    -2.300000
    -0.800000
    6.300000
    12.800000
    17.299999
    22.799999
    25.000000
    24.100000
    19.700001
    13.100000
    5.900000
    0.600000
    34.299999
    60.099998
    67.900002
    71.900002
    23.400000
    1.000000
    6.200000
    1.600000
    1.700000
    3.700000
    18.600000
    21.600000
    BSk

    Abgesehen davon, dass die Datei trotz "wb" in fopen() als Text- und nicht als Binärdatei geöffnet wird, wundere ich mich, dass die float-Werte mit sechs Nachkommastellen eingetragen werden, obwohl ich als Formatstring jeweils "%2.1f" angegeben habe - wieso?

    Hier der Code für das gesamte Programm:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define DIM 12
    #define bool char
    #define true 1
    #define false 0
    
    void input(float*, const char*);
    void display(float*, const char*);
    char* koeppen_geiger(const float*, const float*, const char*, const char, char*);
    int write(float*, float*, char*, char*, char, char*);
    char monate[DIM][10]={ "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" };  
    
    
    int main(void)
    {
      char f, r;
      char* station;
      size_t statsize = 30;
      char cperiod[10];
      char lat_degrees, lat_minutes, lat_seconds, long_degrees, long_minutes, long_seconds;
      float temperatur[DIM];
      float niederschlag[DIM];
      char formula[4];
      
      do
      {
        system("clear");
        printf("              -----------------------------------------\n");
        printf("              | 1 - Klimadaten laden und anzeigen     |\n");
        printf("              | 2 - Klimadaten eingeben und speichern |\n");
        printf("              | x - Programm beenden                  |\n");
        printf("              -----------------------------------------\n\n");
        printf("Funktion: ");
        f=getchar();
        getchar();
        switch(f)
        {
          case '1':
            printf("\nName der Wetterstation: ");
            scanf("%s", station);
            printf("Ladefunktion noch nicht implementiert!\n");
          break;
          case '2':
            printf("\nName der Wetterstation: ");
            station = (char*)malloc(statsize * sizeof(char));
            if (station == NULL)
            {
              perror("Speicher kann nicht zugewiesen werden");
              exit(1);
            }
            f = getline(&station,&statsize,stdin);
            station[f-1]='\0';
    	
            printf("\nGeographische Breite (Süd = negativ): ");
            scanf("%d", &lat_degrees);
            printf("\nKlimaperiode: ");
            scanf("%s",cperiod);
            input(temperatur, "Temperatur");
            input(niederschlag, "Niederschlag");
            printf("\n\nKlimaformel nach Köppen und Geiger: %s\n", koeppen_geiger(temperatur, niederschlag, station, lat_degrees, formula));
    	if (write(temperatur, niederschlag, station, cperiod, lat_degrees, formula) == 1)
    	  return 0;
    	printf("\nDaten wurden gespeichert - bitte Return drücken, um zum Menü zuruckzukehren");
    	getchar();
          break;
          case 'x':
            return 0;
          break;
        }
      }   
      while (f !='1' && f != '2' && f !='x');
      
      
      /* display(temperatur, "Temperatur");
      display(niederschlag, "Niederschlag"); */
    
      return 0;
    }
    
    void input(float* daten, const char* typ)
    {
      unsigned short i;
      for (i=0; i<DIM; i++)
      {  
        printf("\n%s für %s: ", typ, monate[i]);
        scanf("%f", daten++);
        getchar();
      }
    }
    
    void display(float* daten, const char* typ)
    {
      unsigned short i;
      for (i=0; i<DIM; i++)
        printf("\n%s für %s: %2.1f", typ, monate[i], daten[i]);
    }
    
    char* koeppen_geiger(const float* temp, const float* prec, const char* stat, const char lat_deg, char* formula)
    {
      char winter[3];
      char summer[3];
      char warmhalf[6];
      float highest_temp=temp[0];
      float lowest_temp=temp[0];
      float avg_temp;
      float temp_sum=0;
      float avg_prec;
      float prec_sum=0;
      float warmhalf_prec_sum=0;
      float highest_prec_summer;
      float highest_prec_winter;
      float lowest_prec_winter;
      float lowest_prec_summer;
      float lowest_prec=prec[0];
      float prec_ratio, prec_ratio2;
      float arid_threshold;
      unsigned short arid_add;
      char above10=0;
      unsigned char i;
      
      if (lat_deg < 0)
      {
        winter[0]=5;
        winter[1]=6;
        winter[2]=7;
        summer[0]=11;
        summer[1]=0;
        summer[2]=1;
        warmhalf[0]=9;
        warmhalf[1]=10;
        warmhalf[2]=11;
        warmhalf[3]=0;
        warmhalf[4]=1;
        warmhalf[5]=2;
      }
      else
      {
        winter[0]=11;
        winter[1]=0;
        winter[2]=1;
        summer[0]=5;
        summer[1]=6;
        summer[2]=7;
        warmhalf[0]=3;
        warmhalf[1]=4;
        warmhalf[2]=5;
        warmhalf[3]=6;
        warmhalf[4]=7;
        warmhalf[5]=8;
      }
      
      
      for (i=0; i<DIM; i++)
      {
        temp_sum += temp[i];  
        prec_sum += prec[i];
        if (temp[i] > highest_temp)
          highest_temp = temp[i];
        if (temp[i] < lowest_temp)
          lowest_temp = temp[i];
        if (prec[i] < lowest_prec)
          lowest_prec = prec[i];
        if (temp[i] > 10)
          above10++;  
      }
      
      avg_temp = temp_sum/DIM;
      avg_prec = prec_sum/DIM;
      
      if (highest_temp < 10)  // Tundren- und Eisklimate (E)
      {
         formula[0]='E';
         if (highest_temp > 0)
           formula[1]='T';
         else
           formula[1]='F';
         formula[2]='\0'; // Stringende-NULL
      }
      else // A-, B-, C- und D-Klimate
      {
        for (i=0; i<6; i++) // Berechnung der Ariditätsschwelle
          warmhalf_prec_sum += prec[warmhalf[i]];
        prec_ratio = warmhalf_prec_sum/prec_sum;
        if (prec_ratio >= 0.7)
          arid_add = 280;
        else if (prec_ratio >= 0.3)
          arid_add = 140;
        else
          arid_add = 0;
        arid_threshold = avg_temp * 20 + arid_add;
        if (arid_threshold <= 0)
          prec_ratio = 1; // trockene subarktische und polare Klimate gelten nicht als B-Klimate!
        else  
          prec_ratio = prec_sum/arid_threshold;
        if (prec_ratio < 1) // Trockenklimate (B)
        {
          formula[0]='B';
          if (avg_temp >= 18)
            formula[2]='h';
          else
            formula[2]='k';
          if (prec_ratio < 0.5)
            formula[1]='W';
          else
            formula[1]='S';
          formula[3]='\0'; // Stringende-NULL 
        }
        else // A-, C- und D-Klimate
        {
          if (lowest_temp >= 18) // Tropische Klimate (A)
          {
    	formula[0]='A';
    	if (lowest_prec >= 60) 
    	  formula[1]='f'; // Tropisches Regenwaldklima (Af)
    	else
    	{
    	  prec_ratio = lowest_prec / prec_sum;
    	  if (prec_ratio >= 0.04)
    	    formula[1]='m'; // Monsunklima (Am) 
    	  else
    	    formula[1]='w'; // Savannenklima (Aw)
    	}
    	formula[2]='\0'; // Stringende-NULL
          }
          else // C- und D-Klimate
          {
    	lowest_prec_winter = prec[winter[0]];
    	highest_prec_winter = prec[winter[0]];
    	lowest_prec_summer = prec[summer[0]];
    	highest_prec_summer = prec[summer[0]];
    	
    	for (i=1; i<3; i++)
    	{
    	  if (prec[winter[i]] < lowest_prec_winter)
    	    lowest_prec_winter = prec[winter[i]];
    	  if (prec[winter[i]] > highest_prec_winter)
    	    highest_prec_winter = prec[winter[i]];
    	  if (prec[summer[i]] < lowest_prec_summer)
    	    lowest_prec_summer = prec[summer[i]];
    	  if (prec[summer[i]] > highest_prec_summer)
    	    highest_prec_summer = prec[summer[i]];
    	}  
    	
    	prec_ratio = highest_prec_summer / lowest_prec_winter;
    	prec_ratio2 = highest_prec_winter / lowest_prec_summer;
    	if (prec_ratio >= 10)
    	  formula[1]='w'; // wintertrockene gemäßigte oder kontinentale Klimate
    	else if (prec_ratio2 >= 3 && lowest_prec_summer < 30)    
    	  formula[1]='s'; // sommertrockene gemäßigte oder kontinentale Klimate
    	else
    	  formula[1]='f'; // feuchte gemäßigte oder kontinentale Klimate
    	  
    	if (highest_temp >= 22)
    	  formula[2]='a'; // heiße Sommer
    	else if (above10 >= 4)
    	{  
    	  formula[2]='b'; // warme Sommer
    	  if (lowest_temp >= 10) // hochozeanisch, sehr milde Winter (z. B. Tristan da Cunha, Neu-Amsterdam)
    	  {
    	    formula[2]='l';
    	  }
    	}
    	else
    	  formula[2]='c'; // kühle Sommer
    	
    	if (lowest_temp <= -3) // kontinentale Klimate (D)
    	{
    	  formula[0]='D';
    	  if (lowest_temp <= -38)
    	    formula[2]='d';
    	}
    	else
    	  formula[0]='C'; // gemäßigte Klimate (C)
           
           	formula[3]='\0'; // Stringende-NULL
          } 	
        }
      }  
      printf("\nTemperatur:\n");
      printf("\nNiedrigstes Monatsmittel: %2.1f°C", lowest_temp);
      printf("\nHöchstes Monatsmittel:    %2.1f°C", highest_temp);
      printf("\nJahresmittel:             %2.1f°C", avg_temp);
      printf("\n\n");
      printf("\nNiederschlag:\n");
      printf("\nNiedrigstes Monatsmittel: %2.1f mm", lowest_prec);
      printf("\nJahressumme:              %2.1f mm", prec_sum);
      printf("\nAriditätsschwelle:        %2.1f mm", arid_threshold);
      if (formula[0]=='A')
        printf("\nVerhältnis Niederschlag\nSommerhalbjahr/Gesamt:    %2.1f", prec_ratio);
      else if (formula[0]=='B')
        printf("\nVerhältnis Niederschlag/Ariditätsschwelle:    %2.1f", prec_ratio);
      else if (formula[0]=='C' || formula[0]=='D')
      {
        printf("\nVerhältnis feuchtester Sommermonat/trockenster Wintermonat:    %2.1f", prec_ratio);
        printf("\nVerhältnis feuchtester Wintermonat/trockenster Sommermonat:    %2.1f", prec_ratio2);
      }
      
      return formula;
    }
    
    int write(float* temp, float* prec, char* stat, char* period, char lat_deg, char* formula)
    {
      bool flag=false; // zeigt an, ob von Groß- nach Kleinbuchstaben gewandelt wurde
      char i; // Zählvariable
      
      if (stat[0]>=65 && stat[0]<=90)
      {  
        stat[0]+=32; // erster Buchstabe des Stationsnamens wird in Kleinbuchstaben gewandelt
        flag=true;
      }
      FILE* fp;
      fp = fopen(strcat(stat,".cdat"), "wb"); // Datei wird als Binärdatei geöffnet!
    
      if (!fp)
      {  
        printf("\nDatei kann nicht geschrieben werden!");
        return 1;
      }
      if (flag == true)
        stat[0]-=32; // erster Buchstabe des Stationsnamens wird wieder in Großbuchstaben gewandelt
        
      stat[strlen(stat)-5]='\0'; // Stationsname wird auf ursprüngliche Länge gekürzt
      
      fprintf(fp, "%s", stat); // Stationsname wird geschrieben
      fprintf(fp, "%c", '\n');
      fprintf(fp, "%d", lat_deg);
      fprintf(fp, "%c", '\n');
      fprintf(fp, "%s", period);
      fprintf(fp, "%c", '\n');
      for (i=0; i<DIM; i++)
      {  
        fprintf(fp, "%2.1f", temp[i]); // Temperaturdaten werden geschrieben
        fprintf(fp, "%c", '\n');
      }
      for (i=0; i<DIM; i++)
      {  
        fprintf(fp, "%2.1f", prec[i]); // Niederschlagsdaten werden geschrieben
        fprintf(fp, "%c", '\n');
      }
      fprintf(fp, "%s", formula);
      
      if (fclose(fp) == EOF)
      {  
        printf("\nDatei kann nicht geschlossen werden!");
        return 1;
      }
    }    
      
    

    Dann frage ich mich außerdem, wie ich erreichen kann, dass deutsche Sonderzeichen als normales char und nicht als 2-Byte-Wert interpretiert werden...

    Bis bald im Khyberspace!

    Yadgar



  • scanf("%s", station); du schreibst ins Nirvana.

    Was verstehst du unter einer Binärdatei? Die wird jedenfalls mit read/write verarbeitet und nicht mit fscanf/fprintf. Sie ist dann auch nicht „lesbar“.

    Umlaute bestehen aus 2 Byte, wenn der Text als UTF-8 verarbeitet wird. Wähle ISO-8859-15 als deutschen Single Byte Zeichensatz. Was du dazu konkret tun musst hängt von Betriebssystem/Compiler/IDE ab.



  • Hi(gh)!

    @manni66 sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    scanf("%s", station); du schreibst ins Nirvana.

    Wieso? station ist ein char-Array, also gleichzeitig Zeiger auf char!

    Was verstehst du unter einer Binärdatei?

    Eine Datei, in der z. B. eine int-Zahl nicht 10 oder sogar 11 Byte wie in Textdarstellung, sondern nur 4 Byte beansprucht

    Die wird jedenfalls mit read/write verarbeitet und nicht mit fscanf/fprintf. Sie ist dann auch nicht „lesbar“.

    Aha... muss ich mir mal näher ansehen!

    Umlaute bestehen aus 2 Byte, wenn der Text als UTF-8 verarbeitet wird. Wähle ISO-8859-15 als deutschen Single Byte Zeichensatz. Was du dazu konkret tun musst hängt von Betriebssystem/Compiler/IDE ab.

    Ich weiß im Moment auch noch nicht, ob ich diese Möglichkeit wirklich brauche, eventuell stören die 2 Byte gar nicht weiter...

    Bis bald im Khyberspace!

    Yadgar



  • @yadgar sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    @manni66 sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    scanf("%s", station); du schreibst ins Nirvana.

    Wieso? station ist ein char-Array, also gleichzeitig Zeiger auf char!

    char* station; Da ist von einem Array nichts zu sehen.

    Eine Datei, in der z. B. eine int-Zahl nicht 10 oder sogar 11 Byte wie in Textdarstellung, sondern nur 4 Byte beansprucht

    -2.300000 usw. sieht jetzt aber nicht danach aus.



  • Hi(gh)!

    @manni66 sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    -2.300000 usw. sieht jetzt aber nicht danach aus.

    Das mit int war nur ein allgemeines Beispiel, bei float fällt der Unterschied zwischen Text und binär nicht so stark ins Gewicht... ich habe bis jetzt noch nicht die Lesefunktion programmiert und weiß von daher nicht, wie die gelesenen Daten dann dargestellt werden... aber mir kommt die binäre Lösung irgendwie eleganter vor!

    Und dann ist da ja noch das Problem mit den Rundungsfehlern: wenn z. B. aus 60.1 erst 60.099998 und beim Zurücklesen dann 60.0 wird wäre das nicht so schön... sofern ich an der Textdarstellung festhalte (die ist ja auch nachträglich leichter zu bearbeiten): wie bekomme ich es denn hin, dass die Werte tatsächlich nur mit einer Nachkommastelle in die Datei eingetragen werden?

    Das Problem mit den deutschen Sonderzeichen hat sich vorerst als kein Problem herausgestellt: auch "Köln" wird korrekt eingetragen und nicht etwa zu köln. oder köl verstümmelt!

    Bis bald im Khyberspace!

    Yadgar



  • @manni66 sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    char* station; Da ist von einem Array nichts zu sehen.

    Der eingegebene String wird aber trotzdem korrekt in die Datei geschrieben!



  • @yadgar sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    bei float fällt der Unterschied zwischen Text und binär nicht so stark ins Gewicht.

    Äh, nein! "-2.300000" sind 9 Byte als Text, aber 4 als binäres float. Und selbstverständlich ist ist eine binäres float nicht "lesbar"!



  • @yadgar sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    @manni66 sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    char* station; Da ist von einem Array nichts zu sehen.

    Der eingegebene String wird aber trotzdem korrekt in die Datei geschrieben!

    Dann hast du Pech, denn dein Programm ist fehlerhaft und kann jederzeit abstürzen.



  • @manni66 sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    Dann hast du Pech, denn dein Programm ist fehlerhaft und kann jederzeit abstürzen.

    Ich habe die Eingabe im switch-Zweig "1" durch die Routine in Zweig "2" ersetzt... jetzt sollte es problemlos laufen!



  • Hi(gh)!

    Bevor ich mich gleich ins Reich der Träume verabschiede: ich habe die Schreib-Funktion, write() auf binäres Schreiben mit fwrite() umgestellt - und es scheint zu funktionieren! Jedenfalls wird es anstandslos kompiliert und Laufzeitfehler habe ich auch keine... heute Mittag werde ich die dazu passende Lesefunktion ergänzen, dann werde ich sehen, ob meine neue Version etwas taugt! Jetzt könnte ich allerdings auch wieder das Problem mit den Sonderzeichen bekommen, denn fwrite() geht ja von einheitlicher Zeichengröße aus...

    Ganz dumm gefragt: gibt es g_utf8_strlen() auch für C, und wenn ja, in welcher Bibliothek finde ich es?

    Bis bald im Khyberspace!

    Yadgar



  • @yadgar sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    Ganz dumm gefragt: gibt es g_utf8_strlen() auch für C, und wenn ja, in welcher Bibliothek finde ich es?

    Im C Standard nicht. Hier zum Beispiel.

    Aber das brauchst Du nicht. read() und write() lesen und schreiben eine gegebene Anzahl an Bytes und kümmern sich nicht um den Inhalt.

    Quizfrage @Yadgar: Welchen Wert liefert g_utf8_strlen() für "äüöß"?



  • @swordfish sagte in Zwei neue Programmierprobleme: Behandlung von deutschen Sonderzeichen und Binärdatei:

    Quizfrage @Yadgar: Welchen Wert liefert g_utf8_strlen() für "äüöß"?

    Wenn du so fragst: 4! Wäre es 8, hätte man es auch gleich bleiben lassen können...


Log in to reply