Betrags-Vereinheitlichung (internationales Komma-Problem)



  • hallo,
    setze mich gerade mit dem problem auseinander,
    dass unterschiedliche betrags-schreibweisen einheitlich
    zum weiterrechnen aus einem string in

    double Wert;
    

    zu bekommen.

    gibt es hierfür evtl. eine funktion oder einen befehl über c, der
    mir eine zahl, die in einem string steht, automatisch richtig
    umwandelt über atof(string)
    mal ein beispiel:
    EUR 3,12 -> soll zu 3.12 werden
    EUR 3.12 -> kann 3.12 bleiben
    EUR 6.543,21 -> soll zu 6543.21 werden
    EUR 6,543.21 -> soll zu 6543.21 werden
    (mehr noch-Möglichkeiten fallen mir jetzt gar nicht ein)

    kann mir da jemand helfen?
    😕



  • Hab mal schnell was zusammengebastelt (soll nur als Denkanstoß dienen).
    Falls auch Zahlen mit mehreren Trennern (z.B. 2,333,444.45) verarbeitet werden sollen, müsstest du die Funktion noch anpassen.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    double s2d(const char* s) {
       short separator=0, i=0;
       char tmp[1000];
       strcpy(tmp, s);
       for(; i < strlen(s); i++) {
          if(tmp[i] == ',') {
             tmp[i] = '.';
             separator++;
          }
          else if(tmp[i] == '.')
             separator++;
       }
       if(separator==2) {
          char* ptls = strrchr(tmp, '.'); 
    
          while(*ptls && *(ptls+1)) {
             *ptls=*(ptls+1);
             ptls++;
          }
          if(*ptls)
             *ptls=0;
       }
       return atof(tmp);
    }
    
    int main() {
       const char* s1 = "6.543,21";
       const char* s2 = "6,543.21";
       const char* s3 = "3,12";
       const char* s4 = "3.12";
       const char* s5 = "-5,655.4466";
       printf("%s->%f\n", s1, s2d(s1));
       printf("%s->%f\n", s2, s2d(s2));
       printf("%s->%f\n", s3, s2d(s3));
       printf("%s->%f\n", s4, s2d(s4));
       printf("%s->%f\n", s5, s2d(s5));
    
       return 0;
    }
    


  • vielen dank interpreter -

    was du machst, ist ein "suche nach , ersetze durch ."

    sowas ähnliches hatte ich auch,
    doch wenn du dir die ausgabe ansiehst wird aus 6.543,21
    eben nur das float 6.54321
    hätte aber werden sollen: 6543.21

    genau da liegt ja das problem.

    hab auch schon versucht (ich drücks mal so aus, sorry):
    gehe den string durch, wenn der zeiger auf ein '.' stösst und zeiger+4 gleich ',' geh mit dem zeiger wieder 4 zurück und schreib dort ein ''

    also ungefähr so:

    bool Betrag(const string& Line,string& Waehrung,double& Wert)
    {
      char buf[100]; char *p=strcpy(buf,Line.c_str());
      while(*p) 
      { 
             // if (*p==',' && *p+4=='.') { *p-4=' ';} 
             // funktioniert nicht, gemeint ist: springe vom zeiger 4 stellen 
              // nach vorne, guck nach ob dort ein . ist, wenn ja dann 
              // lösche das erste komma......
    
    	  if (*p==',')   *p='.';   p++; 
      }
      p=buf;
       while (*p &&  buchstab(*p)) p++; 
      if (IsSpace(*p)) 
      { *p++=0; Waehrung=buf; }
       while (*p && !zahlen(*p)) p++;
       Wert = atof(p);
      return true;
    }
    
    int buchstab(int c)
    {
      return(c >= 'a' && c <= 'z' ||
             c >= 'A' && c <= 'Z' ||
             c == '„' || c == 'Ž' || c == 'ä' || c == 'Ä' ||
             c == '' || c == 'š' || c == 'ö' || c == 'Ö' ||
             c == '”' || c == '™' || c == 'ü' || c == 'Ü' ||
             c == 'á' || c == 'ß');
    }
    
    int zahlen(int c)
    {
      return(c >= '0' && c <= '9');
    }
    


  • Das kommt davon, wenn man seinen Code nicht testet 🙂
    Ersetze doch mal in der Funktion den Aufruf von strrchr durch strchr und probiers nochmal...



  • ... in der tat - phantastisch.
    das strrchr hab ich auch ganz überlesen.

    was sagst du denn zu meiner lösung?
    da werd ich nicht weit kommen, oder?

    zum verständnis für mich,
    die while-schleife versteh ich noch nicht ganz.
    ist *(ptls+1) der zeiger auf den zeiger ein zeichen weiter? (reimt sich sogar)

    while(*ptls && *(ptls+1)) {
    *ptls=*(ptls+1);
    ptls++;
    }
    if(*ptls)
    *ptls=0;



  • enterbreak schrieb:

    ... in der tat - phantastisch.

    Und man musste nur 1 Zeichen ändern 🤡

    was sagst du denn zu meiner lösung?
    da werd ich nicht weit kommen, oder?

    Dein Algorithmus ist nicht flexibel genug. Du suchst ja nach nach einem Komma, gefolgt von 3 beliebigen Zeichen und danach einem Punkt.
    Damit wirst du jedoch nicht so einen Wert verarbeiten können:
    EUR 6.543,21
    Abgesehen davon stimmt deine Semantik nicht. mit *p+4 bekommst du NICHT den Wert 4 Stellen weiter sondern den Wert, auf den p zeigt um 4 erhöht (der Dereferenzierungsoperator bindet stärker).

    zum verständnis für mich,
    die while-schleife versteh ich noch nicht ganz.
    ist *(ptls+1) der zeiger auf den zeiger ein zeichen weiter? (reimt sich sogar)

    Fast. Das ist der Wert an der nächsten Stelle. ptls+1 ist die nächste Adresse. Durch das * wird diese Adressse dereferenziert.
    Diese Schleife:

    while(*ptls && *(ptls+1)) {
       *ptls=*(ptls+1);
        ptls++;
    }
    

    macht also nichts anderes, als alle Werte um 1 Stelle nach vorne zu rücken.

    Nach Beendigung der Schleife zeigt der Zeiger auf das letzte Zeichen (das wir nicht mehr brauchen, da es gerade eine Stelle nach vorne kopiert wurde). Deshalb überschreiben wir es mit der bin. 0 und terminieren damit den string.

    if(*ptls)
       *ptls=0;
    


  • Da ich gerade gesehen habe, dass du die Klasse string benutzt, hab ich noch schnell ne C++ Variante geschrieben. Die Funktion sollte (das Testen überlasse ich wieder dir;-) mit allen Schreibweisen zurecht kommen:

    #include <iostream>
    #include <string>
    #include <iomanip>
    using namespace std;
    
    double s2d(string s) {
       int c=0,p=0;
       for(string::iterator it = s.begin(); it != s.end(); it++) {
          if(*it == ',') {
             *it = '.';
             c++;
          }
          else if(*it == '.')
             p++;
       }
       if(c > 1 || p > 1) {
          string::size_type end;
          if(!c || !p)
             end = s.length() -1;
          else 
             end = s.rfind(".");
          for(string::size_type ip = 0; ip < end; ) {
             if(s[ip] == '.') {
                s.erase(ip,1);
                end--;
             }
             else
                ip++;
          }
       }
       return atof(s.c_str());  
    }
    
    int main() {
       string s[] = { "1,23", "1.23", "1,234,567", "1,234,567.89", 
          "1.234.567", "1.234.567,89", "123" , "123.4567", "-234,555,666.544"};
       cout.precision(16);
       for(int i=0; i < sizeof(s)/sizeof(string); i++)
          cout << setw(16) <<  s[i] << " -> " << s2d(s[i]) << endl;
       return 0;
    }
    


  • @interpreter...
    wenn ich da sowas gepostet hätte unter www.fun-soft.de was die kommas durch punkte ersetzt dann hätte mir der virtual eins mit der keule übergezogen nachdem er gesagt hätte man soll setlocale verwenden



  • @windalf:
    wat is nu schon wieder "setlocale "??

    @interpreter:
    genial - super!
    das posting sollte mal von 'nem OP in die FAQ geschoben werden!



  • Windalf schrieb:

    @interpreter...
    wenn ich da sowas gepostet hätte unter www.fun-soft.de was die kommas durch punkte ersetzt dann hätte mir der virtual eins mit der keule übergezogen nachdem er gesagt hätte man soll setlocale verwenden

    Is mir schon klar. ich hab setlocale zwar noch nie benutzt aber glaube nicht, dass das gereicht hätte. Im Grunde setzt man damit ja nur Einstellungen wie Tausendertrennzeichen etc. Der OP wollte jedoch das Umwandeln von verschiedenen Formaten "x,xxx.xx", "x.xxx,xx" usw.
    Außerdem scheint es ihm ja offensichtlich doch geholfen zu haben 😉

    Zu setlocale:
    http://home.fhtw-berlin.de/~junghans/cref/MAN/setlocale.htm



  • #include <stdlib.h>
    #include <locale.h>
    
    int main()
    {
        setlocale(LC_NUMERIC, "German");
        printf("%f\n", atof("3,14"));
    }
    

    bevor mich wieder alle kielholen... ja der zweite parameter ist nicht standardisiert...



  • ...setlocale is dann wohl eher linux-spezifisch und nicht windoofs

    hat auf jeden fall geholfen - läuft auch schon - die c++ variante scheint mir irgendwie schneller in der Verarbeitung zu sein (vielleicht auch nur einbildung?)

    danke auf jeden fall - > und ab damit in die FAQs sag ich



  • ..



  • Windalf schrieb:

    #include <stdlib.h>
    #include <locale.h>
    
    int main()
    {
        setlocale(LC_NUMERIC, "German");
        printf("%f\n", atof("3,14"));
    }
    

    bevor mich wieder alle kielholen... ja der zweite parameter ist nicht standardisiert...

    Wie ich in meinem letzten Posting bereits sagte, würde ihm das aber nicht reichen... 🙄



  • @interpreter...
    ja muss zu meienr schande gestehen ich hab den post nicht richtig gelesen 😉



  • Windalf schrieb:

    @interpreter...
    ja muss zu meienr schande gestehen ich hab den post nicht richtig gelesen 😉

    Du Schwein!1 🤡 😉


Anmelden zum Antworten