# (C) bestimmte Zeichen zählen



## Regis Nosferatu (17. Januar 2005)

Hallo Leute!

Hätte da mal wieder `ne Frage:
Ich hab` ein Programm in C geschrieben, das unter anderem eine Text-Datei einliest. Nun soll das Programm im Text die geschweiften Klammern zählen (Öffnende Klammer = Variable++, schließende Klammer = Variable--) und den jeweiligen Zählerstand pro zeile ausgeben. 
Wie zum Teufel setz` ich das um?

Wäre für schnelle Hilfe sehr dankbar...


----------



## RedWing (17. Januar 2005)

Hi,
schau mal da:


```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int getFileSize(FILE* fd){

        int fSize;
        fseek (fd , 0 , SEEK_END); 
        fSize = ftell (fd);
        rewind (fd);
        return fSize;
}
        
bool checkForBrackets(char* input){

        int counter = 0;
        char* seachr_buffer = input - 1;
        while((seachr_buffer = strchr(++seachr_buffer, '{')) != NULL)
                counter++;
        seachr_buffer = input - 1; 
        while((seachr_buffer = strchr(++seachr_buffer, '}')) != NULL)
                counter--;
        if(counter < 0 || counter > 0) return false;
        else return true;
}
        
int main(){

        char* filename = "test.dat";
        char* input;
        char* seachr_buffer;
        int fSize;

        FILE* fd = fopen(filename, "r");
        if(!fd){
                printf("Error while opening file %s!\n", filename);
                exit(1);
        }
        //allocate memory for buffer
        
        fSize = getFileSize(fd);
        if((input = (char*) malloc ( fSize ) ) == NULL) exit(2);
        fread (input, 1, fSize, fd);
        fclose(fd);

        if(!checkForBrackets(input))
                printf("Es gibt unterschiedlich viele schließende wie öffnende Klammern in Datei %s\n", filename);
        else
                printf("Es gibt genau soviel schließende wie öffnende Klammern in Datei %s\n", filename);
}
```

Siehe dazu auch:

http://www.cplusplus.com/ref/cstring/strchr.html

Gruß

RedWing


----------



## Fabian H (17. Januar 2005)

Abgesehen davon, dass es in C kein bool gibt, waere mein Vorschlag:

```
int check_brackets(char *s)
{
    int n;  

    for (;*s;s++) {
        switch (*s) {
        case '{':
            n++;    
            break;
        case '}':
            n--;    
            break;
        }
    }

    return n == 0;
}
```


----------



## RedWing (17. Januar 2005)

Fabian Hofmann hat gesagt.:
			
		

> Abgesehen davon, dass es in C kein bool gibt, waere mein Vorschlag:
> 
> ```
> int check_brackets(char *s)
> ...




Uups kann sein :-( 

Irgendwie hab ich ne Abstraktionsstufe höher gedacht und nicht berücksichtigt wie strchr()
implementiert ist. Wenn strchr() alle Zeichen durchläuft ist deins natürlich wesentlich
eleganter und schneller

Gruß

RedWing


----------



## Regis Nosferatu (19. Januar 2005)

Hi Leute!

Ich hab mich nochmal damit beschäftig und bin jetzt hier angelangt:



```
# include <stdio.h>
 
int counter (char *s, char z) // Fkt zum Zaehlen der Klammern
{
int zeichen; // Deklaration eines Integers `zeichen`
int anzahl=0; // Deklaration + Initialisierung eines Integers `anzahl`
for (zeichen=0; zeichen <= strlen(s); zeichen++)
{
	if (s[zeichen]==z)
	{
	 anzahl++;
	}
}
return anzahl; // Rueckgabe von `anzahl` an main-Fkt
}
 
int main (int argc, char *argv[]) // Kommandozeilenparametern im Argument `argv`
{
char zeile[100]; // angenommene Zeilenlaenge
int zeilen_nr=1; // Deklaration + Initialisierung eines Integers `zeilen_nr`
char c;
struct klammern // eigener strukturierter Datentyp 'Klammern'
	{
	 int gkl; // gkl = Geschweifte KLammern
	 int rkl; // rkl = Runde KLammern
	};
struct klammern ausgabe_klammern = {0, 0};
printf ("\n"); // Optik...
while (fgets(zeile, 100, stdin) != NULL) // Zeilen (max 100 Zeichen) aus `stdin` einlesen
	{
	 ausgabe_klammern.gkl = ausgabe_klammern.gkl + counter(zeile, '{');
	 ausgabe_klammern.gkl = ausgabe_klammern.gkl - counter(zeile, '}');
	 ausgabe_klammern.rkl = ausgabe_klammern.rkl + counter(zeile, '(');
	 ausgabe_klammern.rkl = ausgabe_klammern.rkl - counter(zeile, ')');
	 printf ("%2i: {%i} (%i) | ",zeilen_nr++,ausgabe_klammern.gkl,ausgabe_klammern.rkl);
	 fputs(zeile, stdout); // Ausgabe der Zeilen auf `stdout`
	}
printf("\n"); // Optik...
return 0; // Rueckgabewert an `main` = 0
}
```
 
Das Programm wird unter Linux mit 'cat <dateiname> | <programmname>' aufgerufen und zählt bei der jeweiligen Datei die Zeilen, die geschweiften Klammern und die runden Klammern. 
So weit, so gut...
Das Problem ist jetzt nur noch: Kommentare sollen außen vor bleiben. Sprich: Das Programm soll zwar den gesamten Text ausgeben, sämtliche Klammern jedoch nur bis zum Kommentaranfang zählen. (Bei Blockkommentaren soll dem entsprechend der gesamte Kommentar übersprungen werden...)

Ich werd noch wahnsinnig!
Wie verflixt nochmal setz ich das um?


Wäre für schnelle Hilfe sehr sehr dankbar...


----------



## Regis Nosferatu (24. Januar 2005)

Moin allerseits!

Kann mir denn wirklich keiner bei meinem Problem helfen?
Wäre für mich sehr wichtig und ich komm` einfach nicht weiter...

Regis


----------



## Kachelator (24. Januar 2005)

Das ist auch ein kniffliges Thema. Ich selbst habe mich damit auch schon herumgeärgert. Was das Ignorieren von Kommentaren anbelangt: Da hilft nichts Anderes als den Text auf das Vorkommen von Kommentarkennzeichnern wie "/*" zu untersuchen und alles bis zum "*/" zu ignorieren. Wenn du genug Speicher zur Verfügung hast, arbeite doch einfach in mehrere Schritten: 
1. Die ganze Datei in ein char-Array saugen (vorher Dateilänge checken und entsprechend gross allozieren)
2. Blockkommentare entfernen. Tatsächlich alles rausschmeissen, was zwischen "/*" und "*/" steht; falls Zeilenzahl wichtig, dafür Leerzeilen stehenlassen.  
3.Einzeilige Kommentare entfernen, falls gewünscht. Tatsächlich alles rausschmeissen, was zwischen "//" und "\n" steht; falls Zeilenzahl wichtig, dafür ggf. Leerzeilen stehenlassen.
4. Den nun kommentarfreien Text analysieren wie gehabt.  

Ansonsten habe ich nur noch zwei Anmerkungen:
1. Informier dich mal über "Regex" bzw. "Reguläre Ausdrücke" und Libraries dafür. Das ist eine Möglichkeit, derartige Aufgaben stark zu vereinfachen. Allerdings ist die anfängliche Lernschwelle recht hoch.
2. Ein Optimierungsvorschlag: "hab`",  "komm`" -- Wär´ es nicht einfacher, statt dem "´" einfach direkt das viel bessere "e" zu tippen? Ja? Dacht´ ich mir!


----------



## Fabian H (24. Januar 2005)

Ich hab mich mal dran versucht.
Was noch nicht funktioniert:
* Wenn eine Zeile laenger als BUFSIZE ist, und genau an dieser Stelle ein Kommentar anfaengt... 
* Und die zwei Wiederholungen in der For-Schleife sind absolut unschoen
* C++ Kommentare

```
#include <stdio.h>
#include <string.h>

#define BUFLEN 2048

struct brack {
    int co; /* curly braces open */
    int cc; /* curly braces closed */
    int pa; /* parenthesis */
    int rp; /* right parenthesis */
    int cm; /* are we in a comment? */
};

void check_brackets(char *, struct brack *);

int main(void)
{
    char buf[BUFLEN];
    struct brack bk;

    memset(&bk, 0, sizeof(bk));

    while (fgets(buf, BUFLEN, stdin) != NULL) { 
        check_brackets(buf, &bk);
        memset(buf, 0, BUFLEN*sizeof(char));
    }

    printf("Curlies open:\t%d\n", bk.co); 
    printf("Curlies closed:\t%d\n", bk.cc); 
    printf("Parenthesis:\t%d\n", bk.pa); 
    printf("Right par.:\t%d\n", bk.rp); 

    return 0;
}

void check_brackets(char *str, struct brack *bk)
{
    int nc; 

    for (; *str; str++) {
        if (!bk->cm) {
            switch (*str) {
            case '{': bk->co++; break;
            case '}': bk->cc++; break;
            case '(': bk->pa++; break;
            case ')': bk->rp++; break;
            case '/':
                nc = *(str+1);
                if (nc && nc == '*') {
                    bk->cm = 1;
                    str++;  
                    continue;
                }       
                break;  
            }
        }
        if (bk->cm && *str == '*') {
            nc = *(str+1);
            if (nc && nc == '/') {
                bk->cm = 0;
                str++;
                continue;
            }
        }
    }
}
```


----------



## colblake (25. Januar 2005)

Hi,
@Kachelator


> 1. Informier dich mal über "Regex" bzw. "Reguläre Ausdrücke" und Libraries dafür. Das ist eine Möglichkeit, derartige Aufgaben stark zu vereinfachen. Allerdings ist die anfängliche Lernschwelle recht hoch.



[off topic on]
Hab mal einen Parser für Quelltext geschrieben, der mir u.a. die Komentare heraus filtert(mit Reg-Ex's). Was mir aber nicht gelungen ist, ist mehrzeilige Komentare mit einem Regexausdruck zu filtern. Das mußte ich dann mit einer Schleife "nachbessern". 
Frage: Kennst du dich gut aus mit regulären Ausdrücken? Könntest du mir evtl. sagen wie ein solcher Regausdruck aussehen muss?
ZB: Anfagsbedingung: "/*" und Endbedingung "*/"
[off topic off]

Danke schonmal.


----------



## Kachelator (25. Januar 2005)

@colblake:
Leider kenne ich mich nicht besonders gut damit aus, was schade ist, weil es sich wirklich um sehr sehr mächtiges Werkzeug handelt. Ich habe ein wenig mit dem Regex von .Net und der Regex-Lib von Boost gearbeitet. 

Ich habe gesehen, das es in unserem Coders-Talk-Forum folgenden angepinnten Thread gibt: http://www.tutorials.de/tutorials182256.html
Vielleicht hilft dir das ja weiter.


----------

