c# Programm zum Klammerausdrücke prüfen (Anfängerfrage)

empsim

Cadet 4th Year
Registriert
Juli 2018
Beiträge
69
Hallo zusammen,

Ich muss ein Programm schreiben dass Klammerausdrücke auf die korrekte Schreibweise prüft.
Ich schiebe also die Klammer erstmal auf einen Stack und lese die einzelnen Zeichen schrittweise aus, und erhöhe/verringere einen Zähler bei offener/geschlossener Klammer.

Mein Problem ist dass z.b. ")(" als richtig angegeben wird da erst nach Durchlauf der kompletten Schleife der Zähler auf 0 geprüft wird. wenn ich aber die "if (count == 0)" Bedingung in die foreach schleife schreibe passiert garnichts.

Wäre für eine kleine Hilfe sehr dankbar :)

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public class Program
{
   public static void Main()
    {
        Console.WriteLine("Bitte Klammerausdruck eingeben");
        string str = Console.ReadLine();                        // Klammerausdruck einlesen
        char[] ch = str.ToCharArray();
        int count = 1;
        Stack<char> myStack = new Stack<char>();
        foreach (char c in str)                                 // Klammerausdruck auf Stack schieben
        {
            myStack.Push(c);
        }
        foreach (char c in myStack)                             // Klammern prüfen und Zähler erhöhen/verringern
        {
            if (c == ')')
                count -= 1;
            else if (c == '(')
                count += 1;                       
        }
        if (count == 0)
            Console.WriteLine("Der Klammerausdruck ist falsch");
        else
            Console.WriteLine("Der Klammerausdruck ist richtig");
        Console.WriteLine(count);                              // Dummy um zu sehen ob richtig gezählt wird
        Console.ReadKey();
    }
}
 
Poste mal bitte deinen Code mit dem if in der Schleife, weil der Ansatz ist natürlich korrekt.

Zudem empfehle ich die Verwendung von Leerzeilen und die Kommentare über die Zeilen zu schreiben statt dahinter. Das verbessert die Lesbarkeit.
 
Sicher, dass dein Lösungsweg im Sinne der Aufgabenstellung ist? Normalerweise legst du bei der Aufgabe nicht den ganzen Ausdruck auf den Stack und holst zudem alles, was du auf den Stack packst, auch wieder runter. Zählen (Variable "count") musst du dann nicht.

Das sollte eigentlich reichen um die Lösung zu finden.
 
Zuletzt bearbeitet:
0 ist ein gültiger Wert, zB wenn es gar keine Klammern im AUsdruck gibt. < 0 sollte die korrekte Bedingung sein,
(innerhalb der Schleife). Count initialisierst du mit 0 und muss am Ende 0 sein. Den Code kannst du auch noch vereinfachen, den Stack brauchst du nicht.
 
Der Stack wird benutzt, um sich zu merken, welche die letzte Klammerart gewesen ist, um zu entscheiden, ob diese in der richtigen Reihenfolge geschlossen werden.

C#:
data[0] = data[i] > 0 ? (int)Math.Round((4 + 2) * 0.5 - data[i + 1]) : 0;

Bei jeder Klammer die auftritt muss zuerst entschieden werden, ob es eine öffnende Klammer ist: ( { [
Oder eine schließende Klammer: ) } ]

Wie man schon sieht, ergibt sich daraus eine Regel, für die man den Stack braucht.

z.B. darf eine ) nur dann kommen, wenn die letzte öffnende Klammer eine ( war. Das sieht man, wenn man stack.Pop sich die letzte Klammer holt. Ist die letzte Klammer aber eine andere Klammerart, dann ist es fehlerhaft. Ansonsten muss der Stack am Ende eines Ausdrucks immer 0 sein.


Also öffnende Klammern erhöhen den Stack, schließende Klammern vermindern den Stack.
 
Mal eine Nebenläufige Frage: was ist eigentlich der Unterschied zwischen Stack und List?
 
Macht es denn Sinn noch einen Stack aufzubauen, wenn du eh schon ein char Array hast?
Ich würde das Array durchlaufen und dann halt bei ( i++ und bei ) i-- setzen und dann prüfen, ob i < 0. Wenn i < 0 ist irgendwo ein ) vor einem ( gekommen, was ja nicht erlaubt ist. Und dann am Ende prüfen, ob i == 0 ist.

//edit:
Dir fehlt der Abbruch bei count < 0 in der Schleife, kann das sein?

//edit2:
Und count == 0 am Ende ist doch genau das, was du eigentlich haben willst. count > 0 oder < 0 wären schlecht, wobei du count < 0 schon in der Schleife prüfen müsstest.
 
Stack ist ein Stapel, man kann immer nur auf den letzten hinzugefügten Wert zugreifen und neue Werte werden immer "auf den Stapel gelegt", also an das Ende des Stacks angefügt. Man hat bei einem Stack keinen wahlfreien Zugriff, z.B. kann man sich nicht einfach den Wert an der Stelle 2 geben lassen sondern muss immer das letzte Element erst vom Stack herunternehmen um an den Vorgängerwert zu kommen.

Eine Liste dagegen hat wahlfreien Zugriff auf jede Position darin, der Wert an der Stelle 2 lässt sich jederzeit abfragen.
Ergänzung ()

Die Verwendung eines Stacks macht die Sache recht einfach wenn man die Nutzung nur auf die Klammern begrenzt:

C#:
            Stack<char> stack = new Stack<char>();

            String str = "data[0] = data[i] > 0 ? (int)Math.Round((4 + 2) * 0.5 - data[i + 1]) : 0;";

            foreach (var c in str)
            {
                switch (c)
                {
                    case '(':
                    case '{':
                    case '[':
                        stack.Push(c); // <- Nur die öffnenden Klammern auf den Stack packen!
                        break;

                    case ')':
                    case '}':
                    case ']':
                        if (stack.Count <= 0)
                        {
                            Console.WriteLine("Fehler! Es gibt mehr schließende Klammern als öffnende!");
                        }
                        else
                        {
                            char klammerDavor = stack.Pop(); // <- Letzte Klammer vom Stapel herunternehmen (sprich entfernen)!
                            if (c == '(' && klammerDavor != ')')
                            {
                                Console.WriteLine("Fehler! Runde Klammer wird geschlossen, aber nicht geöffnet!");
                            }

                            if (c == '{' && klammerDavor != '}')
                            {
                                Console.WriteLine("Fehler! Geschweifte Klammer wird geschlossen, aber nicht geöffnet!");
                            }

                            if (c == '[' && klammerDavor != ']')
                            {
                                Console.WriteLine("Fehler! Eckige Klammer wird geschlossen, aber nicht geöffnet!");
                            }
                        }

                        break;
                }
            }

            if (stack.Count != 0)
            {
                Console.WriteLine("Fehler! Es gibt mehr öffnende Klammern als schließende, nicht alle Klammern wurden geschlossen!");
            }
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: new Account()
Ich hätte es vielleicht eher so gelöst.
Hab sogar kleine Auswertung dabei, bei welchen Klammernpaar was nicht stimmt.

C#:
        static void Main(string[] args)
        {
            var s = "data[0] = data[i] > 0 ? (int)Math.Round((4 + 2) * 0.5 - data[i + 1]) : 0;";

            var klammerPaare = new List<KlammerPaar>
            {
                new KlammerPaar('(', ')'),
                new KlammerPaar('[', ']'),
                new KlammerPaar('{', '}')
            };

            foreach (var c in s)
            {
                foreach (var klammerPaar in klammerPaare)
                {
                    if (c.Equals(klammerPaar.Aufklammer)) klammerPaar.AufklammerCount++;
                    if (c.Equals(klammerPaar.Zuklammer) && klammerPaar.AufklammerCount > klammerPaar.ZuklammerCount) klammerPaar.ZuklammerCount++;
                    else if (c.Equals(klammerPaar.Zuklammer)) klammerPaar.FehlklammerCount++;
                }
            }

            foreach (var klammerPaar in klammerPaare)
            {
                Console.WriteLine($"Auswertung - Klammerpaar {klammerPaar.Aufklammer}{klammerPaar.Zuklammer}");
                Console.WriteLine($"{klammerPaar.AufklammerCount}  ({klammerPaar.ZuklammerCount} + {klammerPaar.FehlklammerCount})");
                Console.WriteLine(klammerPaar.AufklammerCount == klammerPaar.ZuklammerCount + klammerPaar.FehlklammerCount ? "Korrekt" : "Fehlerhaft");
                Console.WriteLine();
            }
            
            Console.ReadLine();
        }
    }

    public class KlammerPaar
    {
        public KlammerPaar(char aufklammer, char zuklammer)
        {
            Aufklammer = aufklammer;
            Zuklammer = zuklammer;
        }

        public char Aufklammer { get; private set; }
        public char Zuklammer { get; private set; }
        public int AufklammerCount { get; set; }
        public int ZuklammerCount { get; set; }
        public int FehlklammerCount { get; set; }
    }
 
isiprimax schrieb:
Ich hätte es vielleicht eher so gelöst.
Hab sogar kleine Auswertung dabei, bei welchen Klammernpaar was nicht stimmt.

Dein Code überprüft leider nicht die Ebenen korrekt, also nicht die Reihenfolge der Klammern.

Zum Beispiel wird das als richtig erkannt:
C#:
var s = "[({   )]}";

Dabei muss die Reihenfolge genau eingehalten werden, auf eine öffnende Klammer muss immer eine weitere öffnende oder eine vom selben Typ kommen, die schließt.


Oder ein realistischeres Beispiel wo dein Code Problem macht:
C#:
var s = "data[0] = data[(i + 1]) * 2;";
 
MisC schrieb:
Poste mal bitte deinen Code mit dem if in der Schleife, weil der Ansatz ist natürlich korrekt.

Zudem empfehle ich die Verwendung von Leerzeilen und die Kommentare über die Zeilen zu schreiben statt dahinter. Das verbessert die Lesbarkeit.

Ich glaube jetzt funktioniert es, ich bin mir nicht mehr sicher was der Fehler war.

C#:
public class Program
{
   public static void Main()
    {
        Console.WriteLine("Bitte Klammerausdruck eingeben");

        // Klammerausdruck einlesen

        string str = Console.ReadLine();                     
        char[] ch = str.ToCharArray();
        int count = 1;
        Stack<char> myStack = new Stack<char>();

        // Klammerausdruck auf Stack schieben

        foreach (char c in str)                               
        {
            myStack.Push(c);
        }
        foreach (char c in myStack)

        // Klammern prüfen und Zähler erhöhen/verringern
                                
        {
                  
            if (c == ')')
                count += 1;
            else if (c == '(')
                count -= 1;
            if (count == 0)
            {
                Console.WriteLine("Der Klammerausdruck ist falsch");
                break;
            }
            
        }
        if (count == 1)
            Console.WriteLine("Der Klammerausdruck ist richtig");
        else if (count >= 1)
            Console.WriteLine("Der Klammerausdruck ist falsch");
            

        Console.WriteLine(count);                             
        Console.ReadKey();
    }
}

Smagjus schrieb:
Sicher, dass dein Lösungsweg im Sinne der Aufgabenstellung ist? Normalerweise legst du bei der Aufgabe nicht den ganzen Ausdruck auf den Stack und holst zudem alles, was du auf den Stack packst, auch wieder runter. Zählen (Variable "count") musst du dann nicht.

Das sollte eigentlich reichen um die Lösung zu finden.

Es stand nur dabei dass die Aufgabe mit einem Stack gelöst werden soll, ich dachte alles muss drauf damit die Reihenfolge der Klammern gleich bleibt

umask007 schrieb:
0 ist ein gültiger Wert, zB wenn es gar keine Klammern im AUsdruck gibt. < 0 sollte die korrekte Bedingung sein,
(innerhalb der Schleife). Count initialisierst du mit 0 und muss am Ende 0 sein. Den Code kannst du auch noch vereinfachen, den Stack brauchst du nicht.

den Startwert habe ich mit 1 definiert, sollte dann keinen Unterschied machen oder?

..Gast schrieb:
Stack ist ein Stapel, man kann immer nur auf den letzten hinzugefügten Wert zugreifen und neue Werte werden immer "auf den Stapel gelegt", also an das Ende des Stacks angefügt. Man hat bei einem Stack keinen wahlfreien Zugriff, z.B. kann man sich nicht einfach den Wert an der Stelle 2 geben lassen sondern muss immer das letzte Element erst vom Stack herunternehmen um an den Vorgängerwert zu kommen.

Eine Liste dagegen hat wahlfreien Zugriff auf jede Position darin, der Wert an der Stelle 2 lässt sich jederzeit abfragen.
Ergänzung ()

Die Verwendung eines Stacks macht die Sache recht einfach wenn man die Nutzung nur auf die Klammern begrenzt:

C#:
            Stack<char> stack = new Stack<char>();

            String str = "data[0] = data[i] > 0 ? (int)Math.Round((4 + 2) * 0.5 - data[i + 1]) : 0;";

            foreach (var c in str)
            {
                switch (c)
                {
                    case '(':
                    case '{':
                    case '[':
                        stack.Push(c); // <- Nur die öffnenden Klammern auf den Stack packen!
                        break;

                    case ')':
                    case '}':
                    case ']':
                        if (stack.Count <= 0)
                        {
                            Console.WriteLine("Fehler! Es gibt mehr schließende Klammern als öffnende!");
                        }
                        else
                        {
                            char klammerDavor = stack.Pop(); // <- Letzte Klammer vom Stapel herunternehmen (sprich entfernen)!
                            if (c == '(' && klammerDavor != ')')
                            {
                                Console.WriteLine("Fehler! Runde Klammer wird geschlossen, aber nicht geöffnet!");
                            }

                            if (c == '{' && klammerDavor != '}')
                            {
                                Console.WriteLine("Fehler! Geschweifte Klammer wird geschlossen, aber nicht geöffnet!");
                            }

                            if (c == '[' && klammerDavor != ']')
                            {
                                Console.WriteLine("Fehler! Eckige Klammer wird geschlossen, aber nicht geöffnet!");
                            }
                        }

                        break;
                }
            }

            if (stack.Count != 0)
            {
                Console.WriteLine("Fehler! Es gibt mehr öffnende Klammern als schließende, nicht alle Klammern wurden geschlossen!");
            }

geht die Reihenfolge der Klammern nicht verloren wenn man nur die geöffneten auf den Stack schiebt?

Also ))(( wäre dann auch richtig oder?
 
Es stand nur dabei dass die Aufgabe mit einem Stack gelöst werden soll, ich dachte alles muss drauf damit die Reihenfolge der Klammern gleich bleibt

Ja, ein Stack ist hier für die Lösung das Paradebeispiel schlechthin, es geht bei der Aufgabe ein wenig um die "Eleganz". Leider ist in deinem Code zwar ein Stack enthalten, der wird aber nicht so verwendet, als das du davon einen Nutzen hättest. Also im Sinne der Aufgabe nutzt du die Eigenschaften eines Stapels (Stacks) nicht.


geht die Reihenfolge der Klammern nicht verloren wenn man nur die geöffneten auf den Stack schiebt?

Nein, die Reihenfolge bleibt bei einem Stack erhalten, man legt (stapelt) die Werte "übereinander". Ganz oben liegt immer der zuletzt dem Stack hinzugefügte werden. Es ist wie ein Stapel Papier bei dem man immer nur das obere Blatt nehmen kann und die Reihenfolge davon bestimmt wird, in welcher Reihenfolge man das Papier aufeinander gelegt hat.


Also ))(( wäre dann auch richtig oder?

Nein, das wäre eine falsche Reihenfolge, dein Code funktioniert dahingehend nicht richtig, weil durch das einfache mitzählen ja eben die Reihenfolge nicht mehr beachtet wird. Würdest du den Stack nutzen dann würde die Reihenfolge fürs zählen erhalten bleiben.


Gültig sind z.B.:
( ) ( )
(((( )) ))

Ungültig sind z.B.:
)) ((
((
))
)
(
( ( ( ) ( )
( ( ) ) ) ( ) )
Ergänzung ()

geht die Reihenfolge der Klammern nicht verloren wenn man nur die geöffneten auf den Stack schiebt?

Ich glaube, ich weiß wo das Verständnisproblem ist.

Der Stack nimmt mit stack.Pop() das letzte auf den Stapel gelegte Element herunter und gibt es zurück. Der Trick ist nun, das geschlossene Klammergruppen dich nicht mehr interessieren brauchen, weil du ja schon sichergestellt hast, das die korrekt sind. Letztlich merkt man sich im Stack nur die Klammergruppen die noch nicht wieder geschlossen worden sind. Klammern, die geschlossen wurden nimmt man einfach vom Stack runter, die sind ja irrelevant weil die schon geprüft wurden.

Also jede öffnende Klammer ( vergrößert den Stack um sich zu merken, das noch eine Klammergruppe offen ist.
Jedes schließende Klammer ) verkleinert den Stack weil ja jetzt eine Klammergruppe geschlossen worden ist (also gültig abgehakt werden konnte).

Am Ende muss der Stack leer sein, da öffnende und schließende Klammern sich aufheben und "nie" alleine vorkommen.
 
Zuletzt bearbeitet:
..Gast schrieb:
Ja, ein Stack ist hier für die Lösung das Paradebeispiel schlechthin, es geht bei der Aufgabe ein wenig um die "Eleganz". Leider ist in deinem Code zwar ein Stack enthalten, der wird aber nicht so verwendet, als das du davon einen Nutzen hättest. Also im Sinne der Aufgabe nutzt du die Eigenschaften eines Stapels (Stacks) nicht.




Nein, die Reihenfolge bleibt bei einem Stack erhalten, man legt (stapelt) die Werte "übereinander". Ganz oben liegt immer der zuletzt dem Stack hinzugefügte werden. Es ist wie ein Stapel Papier bei dem man immer nur das obere Blatt nehmen kann und die Reihenfolge davon bestimmt wird, in welcher Reihenfolge man das Papier aufeinander gelegt hat.




Nein, das wäre eine falsche Reihenfolge, dein Code funktioniert dahingehend nicht richtig, weil durch das einfache mitzählen ja eben die Reihenfolge nicht mehr beachtet wird. Würdest du den Stack nutzen dann würde die Reihenfolge fürs zählen erhalten bleiben.


Gültig sind z.B.:
( ) ( )
(((( )) ))

Ungültig sind z.B.:
)) ((
((
))
)
(
( ( ( ) ( )
( ( ) ) ) ( ) )
Ergänzung ()



Ich glaube, ich weiß wo das Verständnisproblem ist.

Der Stack nimmt mit stack.Pop() das letzte auf den Stapel gelegte Element herunter und gibt es zurück. Der Trick ist nun, das geschlossene Klammergruppen dich nicht mehr interessieren brauchen, weil du ja schon sichergestellt hast, das die korrekt sind. Letztlich merkt man sich im Stack nur die Klammergruppen die noch nicht wieder geschlossen worden sind. Klammern, die geschlossen wurden nimmt man einfach vom Stack runter, die sind ja irrelevant weil die schon geprüft wurden.

Also jede öffnende Klammer ( vergrößert den Stack um sich zu merken, das noch eine Klammergruppe offen ist.
Jedes schließende Klammer ) verkleinert den Stack weil ja jetzt eine Klammergruppe geschlossen worden ist (also gültig abgehakt werden konnte).

Am Ende muss der Stack leer sein, da öffnende und schließende Klammern sich aufheben und "nie" alleine vorkommen.

Danke für die Hilfe

Aber warum sollte mein Code nicht funktionieren? Wenn mehr Klammern geschlossen werden als bisher geöffnet wurden geht ja der Zähler auf 0 und die schleife bricht ab.
 
Wenn die Aufgabe war, einen Stack mustergültig zu verwenden, dann ist die Lösung nicht korrekt. Für deinen Weg, obwohl er das Ziel erreicht, hättest du auch eine stinknormale Liste verwenden können.

Ich glaube, du hast noch nicht in der Tiefe durchdrungen, was einen Stack ausmacht. Ich weiß nicht, wie diese Übung in dein Studium integriert ist, aber möglicherweise wollte dein Dozent, dass du durch diese Übung den theoretischen Unterbau von Pushdown-Automaten und Kontextfreien Sprachen verstehst.

https://de.wikipedia.org/wiki/Kellerautomat#Beispiel erklärt es eigentlich recht gut.
 
mental.dIseASe schrieb:
Wenn die Aufgabe war, einen Stack mustergültig zu verwenden, dann ist die Lösung nicht korrekt. Für deinen Weg, obwohl er das Ziel erreicht, hättest du auch eine stinknormale Liste verwenden können.

Ich glaube, du hast noch nicht in der Tiefe durchdrungen, was einen Stack ausmacht. Ich weiß nicht, wie diese Übung in dein Studium integriert ist, aber möglicherweise wollte dein Dozent, dass du durch diese Übung den theoretischen Unterbau von Pushdown-Automaten und Kontextfreien Sprachen verstehst.

https://de.wikipedia.org/wiki/Kellerautomat#Beispiel erklärt es eigentlich recht gut.

Das Problem ist dass es sich um ein Fernstudium handelt und die Studienhefte so theoretisch und trocken geschrieben sind dass ich nach dem Durcharbeiten von 5 kompletten Heften Schwierigkeiten hatte überhaupt ein paar Zeilen Code zu schreiben.

Ich versuche mir das jetzt nach und nach durch Ausprobieren und Beispiele im Internet anzueignen. Das Programmieren ist eigentlich nur ein Nebenfach in dem Studium.
 
empsim schrieb:
Aber warum sollte mein Code nicht funktionieren?


Es gibt meist zig unterschiedliche Wege in der Programmierung ein Ziel zu erreichen, in diesem speziellen Fall ist es aber vor allem wie mental.dIseASe schon schreibt:

mental.dIseASe schrieb:
Wenn die Aufgabe war, einen Stack mustergültig zu verwenden, dann ist die Lösung nicht korrekt. Für deinen Weg, obwohl er das Ziel erreicht, hättest du auch eine stinknormale Liste verwenden können.




empsim schrieb:
Das Problem ist dass es sich um ein Fernstudium handelt und die Studienhefte so theoretisch und trocken geschrieben sind dass ich nach dem Durcharbeiten von 5 kompletten Heften Schwierigkeiten hatte überhaupt ein paar Zeilen Code zu schreiben.

... dranbleiben und durchbeißen, ich drücke dir die Daumen!
 
  • Gefällt mir
Reaktionen: empsim
Zurück
Oben