C - Segmentation fault

Guten Abend.
Da bin ich auch schon wieder ._.
Nun, leider war es mit der Funktion von letztem Mal nicht getan.
Hier ist nun eine neue, bei der es wohl ein programmiertechnischer Fehler ist.
Nicht so wie letztes Mal^^"
Ich bekomme bei der Funktion ein "Segmentation fault". Ich weiß was das ist.
Nur bin ich wohl zu blöd den Fehler zu beheben...
Das ganz spielt sich wieder auf Linux, genauer gesagt Debian3.1 ab, mit gcc.
Auf Windows mit Dev-Cpp ist es etwas kurios. Wenn ich kompilieren und ausführen mache geht alles.
Wenn ich aber die Exe in der Eingabeaufforderung ausführe stürzt das Programm auch bei der Funktion ab.
Bitte helft einem verzweifelten Jungen, der einfach nicht mehr weiter weiß... ._.

Hier die Funktion:

Code:
int getchariddb(char *suchtext){
    FILE* fp;
    char line[80];
    char *name,*crap;
    long int id;
    
    if(strncmp(suchtext,"",20)!=0){
        fp=fopen("./test.txt","r");
            while(fgets(line, 80, fp)){
                id = strtol(strtok(line,"\t"),NULL,10);
                crap = strtok(NULL,"\t");
                name = strtok(NULL,"\t");
                if(strncmp(name,suchtext,20)==0){
                    fclose(fp);
                    return id;
                }
            }
        fclose(fp);
    }
    return 0; 
}

In der Text Datei sieht jede Linie ungefähr so aus:
102345<tab>2046008,5<tab>tails<tab>VieleSachenDieIchNichtBrauche
Ich brauche den 3. String um zu überprüfen ob der Suchtext gefunden wurde.
Der erst Wert ist eine ID, die ich mit dieser Funktion erfahren möchte.

mfg, exec
 
so auf anhieb seh ich nicht wo das problem liegt. waere vielleicht hilfreich den ganzen code zu sehen. aber ich sag mal was mir zu dem code sonst so einfaellt.

keine fehlerbehandlung! lies zu jeder funktion die du verwendest die man page, oder zumindest die wichtigsten stellen (rueckgabewerte, moegliche sicherheitsloecher, etc.). und pruefe bei jeder funktion die fehlschlagen kann auf erfolg.

was soll denn strncmp(suchtext,"",20)!=0 tun? pruefen, ob suchtext ein leerer string ist? woher kommt die 20? allgemein finde ich wird code uebersichtlicher, wenn man bei nicht erfuellten bedingungen abbricht. also anstatt
Code:
if (bedingung1) {
    ....
    if (bedingung2) {
        ....
        if (bedingung3) {
            ....
eher sowas wie

Code:
if (!bedingung1) {
    fehlermeldung;
    return;
}
...
if (!bedingung2) {
    fehlermeldung;
    return;
}
...
du hast auch den fall, dass suchtext == NULL ist nicht geprueft (aber moeglicherweise wird vorher in deinem code sichergestellt, dass da nie NULL uebergeben wird). bei vergleichen im stil von if (foo == NULL) kann man auch schreiben if (NULL == foo). das erspart moeglicherweise unnoetige laufzeitfehler wenn man ein gleichheitszeichen vergisst. NULL = foo gibt beim kompilieren einen fehler, foo = NULL aber nicht.

Code:
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <sys/types.h>
#include <limits.h>
#include <stdlib.h>
#define BUFFER_LENGTH 256

int get_id_by_name(char *filename, char *name)
{
    if ((NULL == name) || (strcmp(name, "") == 0)) {
        return -1;
    }    
    FILE *fp = fopen(filename, "r");
    const char *delimiter = "\t";
    char buffer[BUFFER_LENGTH];
    char *token;
    char *errorptr[1];
    unsigned int token_number;
    unsigned int name_length = strlen(name);
    unsigned long int id;

    if (NULL == fp) {
        perror("unable to open file");
        return -1;
    }    
    while (fgets(buffer, BUFFER_LENGTH, fp) != NULL) {
        token_number = 0;
        for (token = strtok(buffer, delimiter); token; token = strtok(NULL, delimiter)) {
            token_number++;
            switch (token_number) {
                case 1 :
                        id = strtol(token, errorptr, 10);
                        if (**errorptr != 0) {
                            perror("encountered an error while parsing the id");
                            continue;
                        }
                        break;
                case 2 :
                        break;
                case 3 :
                        if ((strlen(token) == name_length) && (strcmp(token, name) == 0)) {
                            fclose(fp);
                            return id;
                        }
                        break;
                default : break;
            }
        }
    }    
    if (ferror(fp) != 0) {
        puts("encountered an error while reading the file");
        fclose(fp);
        return -1;
    }
    fclose(fp);
    return 0;
}

int main (int argc, char* argv[])
{
    unsigned long id;

    if (argc < 3) 
        return -1;
        
    id = get_id_by_name(argv[1], argv[2]);

    if (id > 0) {
        printf("the id is: %lu\n", id);
        return 0;
    } else {
        return -1;
    }
}
ist nicht ausgiebig getestet, koennten also jede menge fehler, sicherheitsloecher, whatever drin sein. die funktion ist auch schon etwas lang. das einlesen der datei koennte man in eine eigene funktion packen.
 
Das ist ein ein strncmp kein strcmp. Daher die 20. Dachte es gibt nichts anderes^^"
Bin halt Anfänger in C. Sehts mir nach. Ich bin VB gewohnt, da ist das alles ein wenig komfortabler^^

Ich weiß jetzt wo der Fehler ist. Den konntest du auch gar nicht sehen, weil du die Datenbank nicht hast.
Und zwar sind die Zeilen Teilweise mehrere tausend Zeichen lang...
Muss einem ja auch gesagt werden das der dort ein \n sucht =/
Wenn ich jetzt wollte das es funktioniert müsste ich line mit ~5000 Zeichen deklarieren und bei fgets füllen.
Frage: Macht das den Code sehr viel langsamer? =/
Okay, blöde Frage. Aber anders gehts wohl nicht, oder?
 
Original von .tails
Das ist ein ein strncmp kein strcmp. Daher die 20. Dachte es gibt nichts anderes^^"
Bin halt Anfänger in C. Sehts mir nach.

macht ja nix. :) im allgemeinen ists auch besser strncmp zu verwenden. ich hatte mich nur gefragt, ob die 20 einen grund hat, oder ob das nur ein pi-mal-daumen-wert war. um zu pruefen ob der string leer ist wuerde sich z.b. auch ein if(strlen(suchstring) == 0) eignen.

Ich weiß jetzt wo der Fehler ist. Den konntest du auch gar nicht sehen, weil du die Datenbank nicht hast.

dein code macht ziemlich viele annahmen ueber das format der eingabedatei. wenn die datei aber voellig anders formatiert ist koennte das wieder zum absturz und eventuell zu sicherheitsloechern fuehren. deshalb muss man bei code der dateien parst immer "mit dem schlimmsten rechnen" und versuchen alle faelle die auftreten koennen zu beruecksichtigen.
in meiner funktion wird z.b. strtok() so oft aufgerufen wie es tokens in buffer gibt. bei dir wird strtok() immer genau 3 mal aufgerufen. was passiert aber, wenn in einer zeile ueberhaupt kein <tab> steht?

Und zwar sind die Zeilen Teilweise mehrere tausend Zeichen lang... Muss einem ja auch gesagt werden das der dort ein \n sucht =/

wird einem auch gesagt ;)

DESCRIPTION
The fgets() function reads at most one less than the number of characters
specified by size from the given stream and stores them in the string
str. Reading stops when a newline character is found, at end-of-file or
error. The newline, if any, is retained. If any characters are read and
there is no error, a `\0' character is appended to end the string.


Wenn ich jetzt wollte das es funktioniert müsste ich line mit ~5000 Zeichen deklarieren und bei fgets füllen.
Frage: Macht das den Code sehr viel langsamer? =/

das wohl nicht, aber das programm verbraucht dann unnoetig viel speicher, da die meisten zeilen vermutlich wesentlich kuerzer als 5000 zeichen sind.

Aber anders gehts wohl nicht, oder?

doch, da gibts viele moeglichkeiten. du koenntest einfach trotzdem immer nur eine bestimmte anzahl zeichen lesen. wenn du weisst, dass die daten die dich interessieren in den ersten n zeichen sind, dann kannst du den buffer n zeichen lang machen und nachdem du mit strtok() die daten die du haben willst aus dem buffer geholt hast solange fgets() aufrufen, bis der buffer ein '\n' enthaelt. also einfach den rest der zeile in einer schleife einlesen und ignorieren.

ne andere moeglichkeit ist, zeichenweise mit getc() einzulesen und das gelesene zeichen immer mit '\n' zu vergleichen. dann muesstest du aber deinen speicher selbst auf dem heap verwalten, d.h. mit malloc() und free() arbeiten und den buffer gegebenenfalls mit realloc() vergroessern. das ist wahrscheinlich nicht der angenehmste weg. ;)
dann gibt es noch funktionen die zeilenweise aus dateien auslesen, die aber nicht zum standard "ansi c" gehoeren. in der libc von linux und *bsd gibt es die funktion fgetln(), die dir die ganze arbeit abnimmt. allerdings funktioniert der code dann nicht mehr unter windows.
in der gnu libc gibt es dann noch die funktion getline(), die aehnlich wie fgetln() funktioniert. der code funktioniert dann aber auch nicht mehr auf bsd, weil da nicht die gnu libc verwendet wird.
 
Speicherverschwendend wird es höchstens ein bisschen. Im Durchschnitt haben die Zeilen schon 3000 Zeichen länge.
Wenn sich das Format der DB ändern würde hätte ich ein Problem. Das stimmt.
Aber wenn das passiert funktioniert das ganze Programm nicht mehr. Da ist es egal was die Funktion macht^^
Ich werd mal sehen ob mir der Speicherverbrauch zu hoch ist. Wenn ja dann änder ich das noch.
Ansonsten erstmal danke für deine Hilfe und Tips. So langsam komm ich C n bisschen näher :rolleyes:
 
Zurück
Oben