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


von Mike (Gast)


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:
1
uint8_t is_number( char* string){
2
3
  uint8_t i = 0;
4
5
  while(i < sizeof(string) ){
6
7
    if( !isdigit(string[i]) ){
8
    
9
      if( string[i] == '.' ){ break;}
10
      if( string[i] == '\0' ){ return 1;}
11
12
      return 0; //String ist keine Zahl
13
    } 
14
15
  i++;
16
  
17
  }
18
19
  return 1;
20
}


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

Gruß Mike

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


Lesenswert?

1
 while(i < sizeof(string) ){

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

von holger (Gast)


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.

von Ingo W. (Gast)


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

von Karl H. (kbuchegg)


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.

1
uint8_t is_number( const char* string )
2
{
3
  if( *string == '+' || *string == '-' )
4
    string++;
5
6
  while( *string && ( isdigit( *string ) || *string == '.' ) )
7
    string++;
8
9
  return *string == '\0';
10
}

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

von holger (Gast)


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.

von Mike (Gast)


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?

von Karl H. (kbuchegg)


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
1
  return *string == '\0';

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

Genau genommen ist das hier
1
  while( *string && ( isdigit( *string ) || *string == '.' ) )
ein wenig doppelt gemoppelt
1
   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.

von Mike (Gast)


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.

von MaWin (Gast)


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.

von Mike (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hier mit den Mitteln der Standardbibliothek, führende Leerzeichen
und ein initiales Plus- oder Minuszeichen sind gestattet.
1
#include <stdbool.h>
2
#include <stdlib.h>
3
#include <stdio.h>
4
#include <string.h>
5
#include <ctype.h>
6
7
int
8
main(void)
9
{
10
  char b[80];
11
  long l1, l2;
12
  char *endptr;
13
  size_t s;
14
  bool firstpartempty;
15
16
  for (;;) {
17
    if (fgets(b, sizeof b, stdin) == NULL)
18
      return 0;
19
    if ((s = strlen(b)) == 0) {
20
      printf("string is empty\n");
21
      continue;
22
    }
23
    if (b[s - 1] == '\n') b[s - 1] = '\0';
24
    l1 = strtol(b, &endptr, 10);
25
    if (*endptr != '\0') {
26
      if (*endptr != '.') {
27
        printf("string is not a valid number, remainder: %s\n",
28
               endptr);
29
        continue;
30
      }
31
      firstpartempty = endptr == b;
32
      if (endptr[1] != '\0') {
33
        /* decimals follow */
34
        if (isspace(endptr[1]) ||
35
            endptr[1] == '-' || endptr[1] == '+') {
36
          printf("string is not a valid number, remainder: %s\n",
37
                 endptr + 1);
38
          continue;
39
        }
40
        l2 = strtol(endptr + 1, &endptr, 10);
41
        if (*endptr != '\0') {
42
          printf("string is not a valid number, remainder: %s\n",
43
                 endptr);
44
          continue;
45
        }
46
      } else if (firstpartempty) {
47
          printf("string not valid (consists of dot only)\n");
48
          continue;
49
      }
50
    }
51
    printf("string is a valid number\n");
52
  }
53
  return 0;
54
}

von Ohforf S. (ohforf)


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.

von Rolf Magnus (Gast)


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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

von Andreas F. (aferber)


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

von Rolf Magnus (Gast)


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.

von Andreas F. (aferber)


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

von Rolf Magnus (Gast)


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.

von Karl H. (kbuchegg)


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

von Ohforf S. (ohforf)


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.

von Andreas F. (aferber)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.