Forum: PC-Programmierung Berechnungen mit Strings unter C


von OlFi (Gast)


Lesenswert?

Hi,

ich möchte mir selbst ein Matheprogramm unter C schreiben und habe mir 
selbst schon ein Textfeld programmiert in dem der Anwender eine Zeile 
eingeben kann, die berechnet werden soll.

Bsp.:
Benutzereingabe:
(12+3)*2

Nach der Eingabe steht die ganze Zeile in einem String wodurch Ziffern 
die zu einer Zahl gehören auf mehrere Felder verteilt wurden. .

Bsp.: 12
Ziffer 1 steht jetzt in Feld 1 und die Ziffer 2 in Feld 2

Mein Ziel ist es jetzt das ich mit einer Funktion jede Zahl erkenne, 
diese in einem double Feld abspeichere (in der Reihenfolge wie sie 
gefunden wurden) und die ursprüngliche Zahl im char Feld wird durch 
einen Platzhalter ersetzt.

Bsp.: (Trennstriche um die einzelnen Felder sichtbar zu machen)
char math[]
|(|1|2|+|3|)|*|2|\0|

Nach dem alle Ziffern und Zahlen erkannt wurden soll in double zahl[] 
folgendes stehen:
double zahl[]
|12|3|2|\0|

math soll anschließend folgendermaßen aussehen:
|(|a| |+|a|)|*|a|\0|

Ich habe das mit diesem Quellcode versucht zu programmieren, aber ich 
kriege dauernd Abstürze.
(Ich vermute der Fehler liegt in der pow() Zeile)
1
void Zahl(char *math, double *zahl)
2
{
3
  int laenge=0;
4
  int Schleife=0;
5
  int stelle=0;
6
  //int stelle2=0;
7
  int grad=0;
8
  int Position=0;
9
  int Position2=0;
10
11
  laenge=strlen(math);
12
13
  while (Schleife < laenge)
14
    {
15
      //stelle=0;
16
      Position2=Position;
17
      grad=0;
18
      zahl=0;
19
20
      for (int n=Schleife; n<laenge; n++)
21
        {
22
          //stelle++;
23
          Schleife++;    //Raussprungvariable der while Schleife
24
25
          if (math[n]>='0' && math[n]<='9')
26
            {
27
              grad++;
28
            }
29
30
          else if (grad != 0)
31
            {
32
              n=laenge+1;
33
              Position++;
34
            }
35
        }
36
37
38
      stelle=grad;
39
      //grad--;
40
      //stelle2=stelle-grad;
41
      if (Position > 0 && Position != Position2)
42
      {
43
      for (int n=Schleife-grad-1;n<Schleife-1;n++)
44
        {
45
          zahl[Position-1]=zahl[Position-1]+math[n]*pow(10,grad-1);
46
          zahl[Position]='\0';
47
          grad--;
48
        }
49
50
      if (zahl != 0)
51
        {
52
          math[Schleife-stelle-1]='a';
53
          for (int n=Schleife-stelle;n<Schleife-1;n++)
54
            {
55
              math[n]=' ';
56
            }
57
        }
58
59
      }
60
    }

Würde mich sehr über Hilfe freuen!

Gruß
    Olaf

von Arc N. (arc)


Lesenswert?

Einige Auffälligkeiten:
1. zahl=0;
   zahl ist ein Zeiger auf double, nach der Zuweisung...

2. zahl[Position-1]=zahl[Position-1]+math[n]*pow(10.0, grad-1);
   math[n] ist so noch ASCII-Code...

3. zahl[Position] = '\0';
   ?

4. if (zahl != 0)
   wenn der Zeiger zahl hier wieder null wäre, ...

von OlFi (Gast)


Lesenswert?

Danke für die Hinweise!

Da habe ich mir glaube ich ein paar grobe Fehler geleistet.

Punkt 1. und 4. sind natürlich totaler Quatsch.

2. Da habe ich die nötige Konvertierung übersehen.

3. Ein Stringende Zeichen für ein Feld in dem eine unbestimmte Anzahl 
double Werte steht fand ich schon sinnvoll.

Gruß
    Olaf

von Karl H. (kbuchegg)


Lesenswert?

Du zäumst das Pferd am falschen Ende auf.

Du kannst diesen Parser hier verwenden.
Er funktioniert nach dem Prinzip des rekursiven Abstiegs.
Die Hauptfunktion heist Parse. Du übergibst ihr einen
String, der den auszuwertenden Ausdruck enthält, und
kriegst als Ergebnis den Zahlenwert der diesem Ausdruck
entspricht.

Bsp:
  long Result = Parse( "(3+2)*4" );
liefert in Result den Wert 20

  long Result = Parse( "3+2)*4" );
setzt die globale Variable HaveError auf einen Wert ungleich
NO_SYNTAX_ERROR, weil in diesem Ausdruck ein Fehler enthalten ist

Achtung: Es findet ausschliesslich Ganzzahlarithmetik statt!
1
const char* pInput;
2
char  NextChar;
3
4
unsigned char HaveError;
5
const char* Errors[] =
6
{
7
  ") expected",
8
  "Number expected",
9
  "illegal character at or near"
10
};
11
12
#define NO_SYNTAX_ERROR          0xFF
13
#define PAREN_EXPECTED_ERROR     0
14
#define NUMBER_EXPECTED_ERROR    1
15
#define ILLEGAL_CHARACTER_ERROR  2
16
17
long Expr();
18
19
void GetNextChar()
20
{
21
  pInput++;
22
  while( *pInput == ' ' ||
23
         *pInput == '\t' ||
24
         *pInput == '\n' ||
25
         *pInput == '\r' )
26
    pInput++;
27
28
  NextChar = *pInput;
29
}
30
31
long Faktor()
32
{
33
  // Faktor := [ '-' ] ( Number | ( '(' Expression ')' ) ).
34
35
  long Result;
36
  unsigned char Sign = FALSE;
37
38
  if( NextChar == '-' ) {
39
    Sign = TRUE;
40
    GetNextChar();
41
  }
42
43
  if( NextChar >= '0' && NextChar <= '9' ) {
44
    Result = NextChar - '0';
45
    GetNextChar();
46
    while( NextChar >= '0' && NextChar <= '9' ) {
47
      Result = 10 * Result + NextChar - '0';
48
      GetNextChar();
49
    }
50
  }
51
52
  else if( NextChar == '(' ) {
53
    GetNextChar();
54
    Result = Expr();
55
    if( NextChar != ')' ) {
56
      HaveError = PAREN_EXPECTED_ERROR;
57
      Result = 0;
58
    }
59
    else
60
      GetNextChar();
61
  }
62
63
  else {
64
    HaveError = NUMBER_EXPECTED_ERROR;
65
    Result = 0;
66
  }
67
68
  if( Sign )
69
    return -Result;
70
71
  return Result;
72
}
73
74
long Term()
75
{
76
  // Term := Faktor { [ '*' | '/' | '%' ] Faktor }.
77
78
  long Result;
79
  char c;
80
81
  Result = Faktor();
82
83
  while( ( c = NextChar ) == '*' || c == '/' || c == '%' ) {
84
    GetNextChar();
85
    if( c == '*' ) {
86
      Result *= Faktor();
87
    }
88
    else if( c == '/' ) {
89
      Result /= Faktor();
90
    }
91
    else
92
      Result %= Faktor();
93
  }
94
95
  return Result;
96
}
97
98
long Expr()
99
{
100
  // Expression := Term { [ '+' | '-' ] Term }.
101
102
  long Result;
103
  char c;
104
105
  Result = Term();
106
107
  while( ( c = NextChar ) == '+' || c == '-' ) {
108
    GetNextChar();
109
    if( c == '+' ) {
110
      Result += Term();
111
    }
112
    else {
113
      Result -= Term();
114
    }
115
  }
116
117
  return Result;
118
}
119
120
121
long Parse(const char* Input)
122
{
123
  long Result;
124
125
  HaveError = NO_SYNTAX_ERROR;
126
  pInput = Input - 1;
127
  GetNextChar();
128
129
  Result = Expr();
130
131
  if( HaveError == NO_SYNTAX_ERROR && NextChar != '\0' ) {
132
    HaveError = ILLEGAL_CHARACTER_ERROR;
133
  }
134
135
  return Result;
136
}

Verwendet werden kann das Ganze zb. so
1
  char Expr[] = "( 2 + 3 ) * ( 4 / 2 )"
2
3
  long Result = Parse( Expr);
4
5
  if( HaveError != NO_SYNTAX_ERROR ) {
6
    char Msg[60];
7
    strcpy( Msg, Errors[HaveError] );
8
    strcat( Msg, " \'" );
9
    strcat( Msg, pInput );
10
    strcat( Msg, "\'" );
11
12
    printf( "%s\n", Msg );
13
  }
14
  else
15
    printf( "%ld\n", Result );

Für printf musst du natürlich dann eine Ausgabe an deine
Oberfläche einbauen.

von OlFi (Gast)


Lesenswert?

@Karl Heinz:

Danke für den Tipp! Das wäre wirklich eine elegante Lösung, aber ich 
will im Endeffekt darauf hinaus das der Anwender eine mathematische 
Funktion mit einer Variablen (z.B. 3x+2) eingibt und das Programm diese 
zeichnet.

Natürlich könnte da auch eine Funktion wie 2/3x+(-3)*2 stehen.

Ich muß ausprobieren ob ich es schaffe so eine Gleichung in berechenbare 
Teilblöcke umzuwandeln, dann könnte ich deine Parse Funktion verwenden.

Gruß
    Olaf

von Karl H. (kbuchegg)


Lesenswert?

OlFi wrote:
> @Karl Heinz:
>
> Danke für den Tipp! Das wäre wirklich eine elegante Lösung, aber ich
> will im Endeffekt darauf hinaus das der Anwender eine mathematische
> Funktion mit einer Variablen (z.B. 3x+2) eingibt und das Programm diese
> zeichnet.
>
> Natürlich könnte da auch eine Funktion wie 2/3x+(-3)*2 stehen.
>
> Ich muß ausprobieren ob ich es schaffe so eine Gleichung in berechenbare
> Teilblöcke umzuwandeln, dann könnte ich deine Parse Funktion verwenden.

Den kannst du sowieso verwenden. Du musst lediglich das x noch
zur Verfügung stellen:
1
long Faktor()
2
{
3
  // Faktor := [ '-' ] ( Number | ( '(' Expression ')' ) | 'x' ).
4
5
  long Result;
6
  unsigned char Sign = FALSE;
7
8
  //
9
  // Schau an, schau an: Ein '-' als Vorzeichen
10
  //
11
  if( NextChar == '-' ) {
12
    Sign = TRUE;
13
    GetNextChar();
14
  }
15
16
  //
17
  // Fall: Eine hundsordinaere Zahl
18
  //
19
  if( NextChar >= '0' && NextChar <= '9' ) {
20
    Result = NextChar - '0';
21
    GetNextChar();
22
    while( NextChar >= '0' && NextChar <= '9' ) {
23
      Result = 10 * Result + NextChar - '0';
24
      GetNextChar();
25
    }
26
  }
27
28
  //
29
  // Fall: Ein Ausdruck in Klammern
30
  //
31
  else if( NextChar == '(' ) {
32
    GetNextChar();
33
    Result = Expr();
34
    if( NextChar != ')' ) {
35
      HaveError = PAREN_EXPECTED_ERROR;
36
      Result = 0;
37
    }
38
    else
39
      GetNextChar();
40
  }
41
42
  //
43
  // Fall: die einzige Variable, die in Funktionen erlaubt ist
44
  //       nämlich 'x'
45
  //       wird von der Umgebung global zur Verfügung gestellt
46
  //
47
  //       Wenn mehrere Variablen möglich sein sollen, muesste
48
  //       man hier eine Variablenverwaltung einbauen
49
  //
50
  else if( NextChar == 'x' ) {
51
    GetNextChar();
52
    Result = ValueOfX;
53
  }
54
55
  //
56
  // Fall: Nichst von alledem. Ein Fehler
57
  //
58
  else {
59
    HaveError = NUMBER_EXPECTED_ERROR;
60
    Result = 0;
61
  }
62
63
  if( Sign )
64
    return -Result;
65
66
  return Result;
67
}

Du willst also einen Funktionsplotter bauen.
OK.
D.h. du bestimmst, oder lässt dir vom Benutzer die Grenzen für X geben,
sowie eine Schrittweite.
Dann gehst du in einer Schleife die X von der unteren Grenze bis
zur oberen Grenze durch, und rufst jedesmal den Formelparser auf
(der laut oben schon für die Behandlung einer Variablen 'x' vorbereitet
wurde)
1
long ValueOfX;  // Variable 'x' für den Parser
2
3
int main()
4
{
5
  char Expr[] = "( 2 + 3 ) * ( 4 / 2 )"
6
  ...
7
8
  for( x = Minimum; x < Maximum; x += Schrittweite ) {
9
    ValueOfX = x;
10
    Result = Parse( Expr );
11
12
    PlotPoint( x, Result );
13
  }
14
  ...
15
}

Allerdings wäre es für einen Funktionsplotter besser, die ganzen
Berechnungen von long auf double umzustellen.

Die andere Sache ist: Klarerweise ist der Parser in der jetzigen
Form nicht optimal für einen Funktionsplotter. Es ist natürlich
eine Verschwendung von Rechenzeit, wenn die Formel jedesmal erneut
geparst werden muss, nur um sie auszuwerten.
Für einen Funktionsplotter würde man den Parser soweit umbauen,
dass er Maschinencode für eine gedachte, selbst implementierte,
fiktive CPU generiert und ein zusätzliches Programmmodul machen,
welches diese CPU implementiert und den generierten Code dann
immer wieder abarbeitet.
Aber: Soweit bist du noch nicht. Das wäre der übernächste Schritt.

Der logische nächste Schritt wäre, den Parser so wie er jetzt ist
in einen lexikalischen Scanner und den eigentlichen Parser zu trennen.
Denn: math. Standard-Funktionen stehen bereits in den Startlöchern
und ohne lexikalischen Scanner ist es eher mühsam, die Funktionsnamen
aus der Formel zu parsen. Es geht, ist aber mühsam.
1
  //
2
  // Fall: die einzige Variable, die in Funktionen erlaubt ist
3
  //       nämlich 'x'
4
  //       wird von der Umgebung global zur Verfügung gestellt
5
  //
6
  //       Wenn mehrere Variablen möglich sein sollen, muesste
7
  //       man hier eine Variablenverwaltung einbauen
8
  //
9
  else if( NextChar == 'x' ) {
10
    GetNextChar();
11
    Result = ValueOfX;
12
  }
13
14
  //
15
  // Fall: die Funktionen sqrt, sin
16
  //       Also Funtkionsnamen, die mit 's' anfangen
17
  //
18
  else if( NextChar == 's' ) {
19
    GetNextChar();
20
    if( NextChar == 'q' ) {
21
      GetNextChar();
22
      if( NextChar == 'r' ) {
23
        GetNextChar();
24
          if( NextChar == 't' ) {
25
            GetNextChar();
26
27
            if( NextChar == '(' ) {
28
              GetNextChar();
29
              Result = sqrt( Expr() );
30
              if( NextChar != ')' ) {
31
                HaveError = PAREN_EXPECTED_ERROR;
32
                Result = 0;
33
              }
34
              else
35
                GetNextChar();
36
            }
37
          }
38
        }
39
      }
40
    }
41
42
    else if( NextChar == 'i' ) {
43
      GetNextChar();
44
      if( NextChar == 'n' ) {
45
        GetNextChar();
46
47
        if( NextChar == '(' ) {
48
          GetNextChar();
49
          Result = sin( Expr() );
50
          if( NextChar != ')' ) {
51
            HaveError = PAREN_EXPECTED_ERROR;
52
            Result = 0;
53
          }
54
          else
55
            GetNextChar();
56
        }
57
      }
58
    }
59
  }

Sinnvollerweise wird man natürlich den Abschnitt, der einen
geklammerten Ausdruck parst und berechnet, in eine eigene
Funktion herausziehen.

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.