www.mikrocontroller.net

Forum: Compiler & IDEs Überprüfen ob String ein Zahl ist


Autor: Mike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich würde gerne Strings überprüfen, ob sie eine Zahl sind.

So ein String kann z.B. so aussehen: "234.331"


Ich habe folgende Funktion geschrieben:

uint8_t is_number( char* string){

  uint8_t i = 0;

  while(i < sizeof(string) ){

    if( !isdigit(string[i]) ){
    
      if( string[i] == '.' ){ break;}
      if( string[i] == '\0' ){ return 1;}

      return 0; //String ist keine Zahl
    } 

  i++;
  
  }

  return 1;
}



Was haltet ihr davon? Gibt es eine geschicktere Möglichkeit dies 
umzusetzen?

Gruß Mike

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mike schrieb:
> Was haltet ihr davon?

Nicht viel.
Voller Fehler.

> Gibt es eine geschicktere Möglichkeit dies
> umzusetzen?

Machs erst mal richtig, ehe du dich um Eleganz und Geschwindigkeit 
sorgst.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
 while(i < sizeof(string) ){

du kannst hier nicht sizeof benutzen. sizeof gibt dir an dieser Stelle 
die Größe des Pointers, also 2

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>uint8_t is_number( char* string){
>  while(i < sizeof(string) ){

Woher soll sizeof hier wissen wie groß der String ist?
sizeof vom Pointer wird 2 oder sowas sein.
strlen ist da wohl die bessere Wahl.

Autor: Ingo W. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die Funktion strchr durchsucht einen String nach dem Vorkommen von einem 
Zeichen und gibt einen Zeiger auf das Zeichen zurück, andernfalls 0.
Du könntest also schreiben:

if strchr(string,'.')
 {
 /* String enthält punkt*/
 }
else
 {
 /* String enthält keinen punkt*/
 }

mfg Ingo

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In einer Funktion bearbeitet man Strings am Besten, indem man direkt den 
Pointer hochzählt und so von einem Zeichen zum nächsten kommt. Dies 
macht man solange, bis man auf das String-abschliessende '\0' Zeichen 
stösst. Die Sichtweise über Arrays und einem demenstprechenden Index ist 
zwar naheliegend, verkompliziert die Dinge aber oft nur. Abgesehen davon 
ist es ineffizient, weil man vorher die Stringlänge feststellen muss und 
das geht in der Funktion nur über strlen. strlen muss aber selbst das 
Array durchgehen und die Zeichen bis zum \0 character zählen, d.h. man 
hat 2 Durchläufe durch den String: Einmal um die Länge festzustellen und 
das 2te mal um dann die tatsächliche Arbeit zu machen.

uint8_t is_number( const char* string )
{
  if( *string == '+' || *string == '-' )
    string++;

  while( *string && ( isdigit( *string ) || *string == '.' ) )
    string++;

  return *string == '\0';
}

Der Code berücksichtigt nicht, dass es nur einen Dezimalpunkt in einer 
Zahl geben darf.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>      if( string[i] == '.' ){ break;}

Da würde ich

      if( string[i] == '.' ){ continue;}

machen. Das i++ ist dann aber an der falschen Stelle
bzw die dauernde Abfrage auf string[i] falsch;)
string[i] temporär in Variable, dann wirds einfacher.

Autor: Mike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Tipps.

@Karl heinz Buchegger:

Deinen Code verstehe ich nicht ganz

[c]
  if( *string == '+' || *string == '-' )
    string++;
[\c]

OK. Wenn das erste Zeichen ein Vorzeichen ist, geht es weiter.


[c]
while( *string && ( isdigit( *string ) || *string == '.' ) )
    string++;
[\c]

Solange das Zeichen eine Zahl oder ein Punkt ist, geht es weiter.

Nun verstehe ich aber den Return-Wert nicht.

Was bekomme ich den zurück wenn es eine Zahl ist bzw. wenn es keine Zahl 
ist?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mike schrieb:

> Solange das Zeichen eine Zahl oder ein Punkt ist, geht es weiter.
>
> Nun verstehe ich aber den Return-Wert nicht.

Die Idee des Codes ist Folgende:
Solange den Pointer hochzählen, solange Zeichen vorgefunden werden, die
eine Zahl bilden können.
Aus der while Schleife fällt man daher in 2 Fällen raus
* wenn das Ende des Strings vorgefunden wurden
* wenn ein Zeichen vorgefunden wurde, welches nicht zu einer Zahl
gehören kann.

Kommt der Code daher aus der while Schleife raus, stellt sich nur die
Frage: Sind wir am Ende des Strings? Wenn ja, dann waren im String nur
Zeichen, die eine Zahl bilden können. Ansonsten wäre die while Schleife
ja schon vorher beendet worden und strign zeigt nicht auf das
String-Ende, sondern auf dieses nicht dazugehörende Zeichen.

Und genau das wird hier geprüft
  return *string == '\0';

zurückgegeben wird das Ergebnis des Vergleichs, ob string auf das 
string-abschliessende '\0' Zeichen zeigt.

Genau genommen ist das hier
  while( *string && ( isdigit( *string ) || *string == '.' ) )
ein wenig doppelt gemoppelt
   while( isdigit( *string ) || *string == '.' )
würde es auch tun, denn '\0' ist weder ein Digit, noch ist es '.' und 
führt daher ebenfalls von ganz von alleine zum Abbruch der Schleife.

Autor: Mike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja klar. Ich habe die letzte Zeile irgendwie als Zuweisung gesehen, aber 
ist ja ein Vergleich. Das zweite Gleichzeichen hab ich wohl übersehen.

Danke.

Autor: MaWin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> So ein String kann z.B. so aussehen: "234.331"


Auch so 99999999999999999999999.88888888888888888 ?


Werd dir also erst mal klar, was du willst.

double-Zahlen ?

In irgendwas musst du die Ziffernfolge doch wandeln.

Und genau das definiert, was als Ziffernfolge akzeptiert wird.

Wenn du Nachkommastellen willst, also ein double.

char *end;
double x;

x=strtod(string,&end)

if(*string!='\0'&&*end=='\0') dann war es eine Zahl und nicht mehr als 
eine Zahl und x ist ihr Wert.

Autor: Mike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, wandeln möchte ich den String nicht. Ich möchte ihn nur nicht 
ausgeben, sollter er keine Zahl enthalten.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier mit den Mitteln der Standardbibliothek, führende Leerzeichen
und ein initiales Plus- oder Minuszeichen sind gestattet.
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int
main(void)
{
  char b[80];
  long l1, l2;
  char *endptr;
  size_t s;
  bool firstpartempty;

  for (;;) {
    if (fgets(b, sizeof b, stdin) == NULL)
      return 0;
    if ((s = strlen(b)) == 0) {
      printf("string is empty\n");
      continue;
    }
    if (b[s - 1] == '\n') b[s - 1] = '\0';
    l1 = strtol(b, &endptr, 10);
    if (*endptr != '\0') {
      if (*endptr != '.') {
        printf("string is not a valid number, remainder: %s\n",
               endptr);
        continue;
      }
      firstpartempty = endptr == b;
      if (endptr[1] != '\0') {
        /* decimals follow */
        if (isspace(endptr[1]) ||
            endptr[1] == '-' || endptr[1] == '+') {
          printf("string is not a valid number, remainder: %s\n",
                 endptr + 1);
          continue;
        }
        l2 = strtol(endptr + 1, &endptr, 10);
        if (*endptr != '\0') {
          printf("string is not a valid number, remainder: %s\n",
                 endptr);
          continue;
        }
      } else if (firstpartempty) {
          printf("string not valid (consists of dot only)\n");
          continue;
      }
    }
    printf("string is a valid number\n");
  }
  return 0;
}

Autor: Ohforf Sake (ohforf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Billige Lösung : wenn alle Zeichen im Bereich 48-57 (ASCII dezimal) 
liegen, ist es eine Zahl (Ganzzahl ohne Vorzeichen).
Vorteil : braucht keine fette String Library.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohforf Sake schrieb:
> Billige Lösung : wenn alle Zeichen im Bereich 48-57 (ASCII dezimal)
> liegen,

Oder - ohne ASCII-Abhängigkeit, dafür mit besserer Lesbarkeit - im 
Bereich '0' bis '9'.

> ist es eine Zahl (Ganzzahl ohne Vorzeichen).
>
> Vorteil : braucht keine fette String Library.

Hast du dir mal angeschaut, was die Funktion isdigit macht und wie 
"fett" die tatsächlich ist?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Insbesondere hat er sich die Aufgabenstellung ganz offensichtlich
nicht angeschaut.  Vermutlich nur die Überschrift gelesen...

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Oder - ohne ASCII-Abhängigkeit, dafür mit besserer Lesbarkeit - im
> Bereich '0' bis '9'.

Und wer garantiert dir, dass die Ziffer-Zeichen in dem verwendeten 
Zeichensatz direkt aufeinanderfolgend, ohne Lücken (bzw. anderen 
Zeichen) dazwischen, und mit '0' an niedrigster und '9' an höchster 
Stelle sind?

Zugegeben, in der Praxis ist das selbst bei EBCDIC der Fall, aber 
schon mit dem vordergründig ähnlichen "im Bereich 'a' bis 'z' -> 
Kleinbuchstabe" fällst du bei EBCDIC auf die Nase...

Andreas

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Ferber schrieb:
> Und wer garantiert dir, dass die Ziffer-Zeichen in dem verwendeten
> Zeichensatz direkt aufeinanderfolgend, ohne Lücken (bzw. anderen
> Zeichen) dazwischen, und mit '0' an niedrigster und '9' an höchster
> Stelle sind?

ISO-9899.

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> ISO-9899.

OK, überzeugt. Die Passage war mir wohl bisher durch die Lappen 
gegangen.

Dennoch der Vollständigkeit halber: es gibt (bzw. gab) durchaus auch 
Zeichenkodierungen, bei denen die Ziffern nicht aufeinanderfolgend 
sind. Diverse Beispiele gibt es hier: 
http://www.science.uva.nl/museum/DWcodes.html

Auf einem (hypothetischen) Computersystem mit einer solchen Kodierung 
dürfte eine standardkonforme C-Implementierung dann allerdings einige 
Verrenkungen vor allem im IO-Bereich mit sich bringen.

Andreas

PS: nicht so hypothetisch: http://en.wikipedia.org/wiki/IBM_7030_Stretch

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Ferber schrieb:
> Auf einem (hypothetischen) Computersystem mit einer solchen Kodierung
> dürfte eine standardkonforme C-Implementierung dann allerdings einige
> Verrenkungen vor allem im IO-Bereich mit sich bringen.

Dafür gibt es ja dann die Unterscheidung zwischen Text- und Binary-Modus 
in Streams. Da muß man halt beim Lesen und Schreiben umkodieren, so wie 
es unter Windows auch schon mit den Zeilenenden gemacht wird. So groß 
wären diese Verrenkungen also eigentlich gar nicht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Andreas Ferber schrieb:
>> Auf einem (hypothetischen) Computersystem mit einer solchen Kodierung
>> dürfte eine standardkonforme C-Implementierung dann allerdings einige
>> Verrenkungen vor allem im IO-Bereich mit sich bringen.
>
> Dafür gibt es ja dann die Unterscheidung zwischen Text- und Binary-Modus
> in Streams. Da muß man halt beim Lesen und Schreiben umkodieren, so wie
> es unter Windows auch schon mit den Zeilenenden gemacht wird. So groß
> wären diese Verrenkungen also eigentlich gar nicht.


Na ja, schon.
Das geht ja auch noch weiter.
Die Zusicherung im Standard enthält ja zb auch, dass

  '0' < '1'

true ergeben muss. Mit einer wilden Codetabelle könnte das alles dann 
ausarten :-)

Autor: Ohforf Sake (ohforf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Insbesondere hat er sich die Aufgabenstellung ganz offensichtlich
> nicht angeschaut.  Vermutlich nur die Überschrift gelesen...

Stimmt nicht. Die Sache mit dem".", der ja auch erlaubt ist, hab ich 
weggelassen, weils zu einfach ist.
Wenn man die Sache etwas weniger präzise lösen will, könnte man den 
ganzen Bereich von "." bis "9" als Ziffer betrachten - wenn der "/" 
nicht stört.

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Dafür gibt es ja dann die Unterscheidung zwischen Text- und Binary-Modus
> in Streams. Da muß man halt beim Lesen und Schreiben umkodieren, so wie
> es unter Windows auch schon mit den Zeilenenden gemacht wird. So groß
> wären diese Verrenkungen also eigentlich gar nicht.

Jein. Streng gesehen dürfte das passen. Aber nimm z.B. Dateinamen. Die 
Regeln für selbige sind implementation defined, von daher könnte man 
Ziffern hier einfach verbieten, es dürften aber die meisten Anwender 
erstmal davon ausgehen, dass Ziffern in Dateinamen erlaubt sind, und 
dass diese dann auch in anderen, nicht in C geschriebenen, Programmen 
als Ziffern auftauchen. Auch die Portabilität von vielen Programmen 
dürfte auf eine harte Probe gestellt werden, wenn man Ziffern im 
Dateinamen verbieten würde.

Deshalb müsste in der Praxis z.B. auch bei fopen() umkodiert werden, was 
man sich bei \n eher schenken kann, da in letzterem Fall nach meiner 
Erfahrung die meisten Leute eher überrascht sind, dass Newlines im 
Dateinamen auf manchen Systemen überhaupt erlaubt sind, respektive 
Newlines eben sowieso vom System verboten werden.

Andreas

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohforf Sake schrieb:
> Stimmt nicht. Die Sache mit dem".", der ja auch erlaubt ist, hab ich
> weggelassen, weils zu einfach ist.

So einfach isses eben auch wieder nicht.  Beispielsweise ist ja
0.0.0.0 keine gültige Zahl im Sinne des OP.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.