代码之家  ›  专栏  ›  技术社区  ›  David Watson

C自定义数据库写入错误

  •  0
  • David Watson  · 技术社区  · 14 年前

    我有一个类的任务,我必须写一个程序来读写密钥,值对到磁盘。我使用一个链表来存储密钥,并在需要时从磁盘读取值。但是,我在更改和删除值时遇到问题。我用这个来测试它: http://gaming.jhu.edu/~phf/2010/fall/cs120/src/sdbm-examples.tar.gz . 代码如下。基本上,我需要一些帮助来找出错误,因为这是我们必须使用指针的第一个赋值,而我只是在所有的segfaults和其他东西中死去。如果你能给我一些建议,我将不胜感激。

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <string.h>
    #include <stdbool.h>
    
    #include "sdbm.h"
    
    FILE *db;
    bool opened = false, needNewDB = false;
    int err = 0, keyLen = 0;
    char *filename;
    
    typedef struct Key_{
      char *name;
      char *val;
      long offset;
      struct Key_ *next;
    } Key;
    
    Key *head = NULL,*tail = NULL, *lastHas = NULL, *beforeLastHas = NULL;
    /**
     * Create new database with given name. You still have
     * to sdbm_open() the database to access it. Return true
     * on success, false on failure.
     */
    void listAdd() {
      if (tail != NULL) {
        tail->next = (Key *) malloc(sizeof(Key));
        tail = tail->next;
      }
      else {
        tail = (Key *)malloc(sizeof(Key));
        head = tail;
      }
      tail->next = NULL;
      tail->name = NULL;
      tail->val = NULL;
    }
    
    bool sdbm_create( const char *name ) { //Errors: 1) fopen failed 2) fclose failed on new db
      filename = malloc(sizeof(*name));
      strcpy(filename,name);
      FILE *temp = fopen(name, "w");
      if (temp == NULL) {
        printf("Couldn't create file %s\n",name);
        err = 1;
        return false;
      }
      if (fclose(temp) == EOF) {
        printf("Couldn't close created file %s\n",name);
        err = 2;
        return false;
      }
      return true;
    }
    /**
     * Open existing database with given name. Return true on
     * success, false on failure.
     */
    bool sdbm_open( const char *name ) { //Errors: 3) couldn't open database
      db = fopen(name,"r+");
      if (db == NULL) {
        err = 3;
        printf("Couldn't open database file %s\n",name);
        return false;
      }
      opened = true;
      int c;
      bool inKey = true;
      char currKey[MAX_KEY_LENGTH];
      while ((c = getc(db)) != EOF)  {
        if (!inKey && c == '\0') {
          inKey = true;
        }
        else if (inKey && c == '\0') {
          currKey[keyLen] = '\0';
          listAdd();
          tail->offset = ftell(db);
          tail->name = malloc(sizeof(*currKey));
          strcpy(tail->name,currKey);
          keyLen = 0;
          inKey = false;
        }
        else if (inKey) { 
          currKey[keyLen] = c;
          keyLen++;
        }
      }
      Key *curr = head;
      while (curr != NULL) {
        printf("Key: %s\n",curr->name);
        curr = curr->next;
      }
    
      return true;
    }
    void readVal(char *value, long offset) {
      fseek(db,offset,SEEK_SET);
      int c;
      for (int i = 0; (c = getc(db)) != '\0'; i++) {
        *(value + i) = c;
      }
    }
    
    /**
     * Synchronize all changes in database (if any) to disk.
     * Useful if implementation caches intermediate results
     * in memory instead of writing them to disk directly.
     * Return true on success, false on failure.
     */
    bool sdbm_sync() {
      if (!needNewDB) {
        Key *curr = head;
        fseek(db,0,SEEK_END);
        while (curr != NULL) {
          if (curr->val != NULL) {
            fprintf(db,"%s%c%s%c",curr->name,'\0',curr->val,'\0');
          }
          curr = curr->next;
        }
      }
      else {
        FILE *temp;
        sdbm_create("tRpdxD.p4ed");
        temp = fopen("tRpdxD.p4ed","w");
        Key *curr = head;
        while (curr != NULL) {
          if (curr->val != NULL) {
            fprintf(temp,"%s%c%s%c",curr->name, '\0', curr->val, '\0');
          }else {
            char *tempS = malloc(MAX_VALUE_LENGTH);
            readVal(tempS, curr->offset);
            fprintf(temp,"%s%c%s%c",curr->name,'\0',tempS,'\0');
            free(tempS);
          }
          fflush(temp);
          fflush(db);
          curr = curr->next;
        }
        fclose(db);
        remove(filename);
        rename("tRpdxD.p4ed",filename);
        db = fopen(filename,"r+");
      }
      fflush(db);
      return true;
    }
    /**
     * Close database, synchronizing changes (if any). Return
     * true on success, false on failure.
     */
    bool sdbm_close() { // Errors: 5) Couldn't close database
      sdbm_sync();
      Key *tmp = head;
      while (head->next != NULL) {
        tmp = head;
        head = head->next;
        free(tmp->name);
        if (tmp->val != NULL) {
          free(tmp->val);
        }
        free(tmp);
      }
      if (fclose(db) == EOF) {
        err = 5;
        printf("Couldn't close database.\n");
        return false;
      }
      return true;
    }
    /**
     * Return error code for last failed database operation.
     */
    int sdbm_error() {
      return err;
    }
    
    /**
     * Is given key in database?
     */
    bool sdbm_has( const char *key ) {
      if (head == NULL) {
        return false;
      }
      Key *curr = head;
      lastHas = NULL;
      beforeLastHas = NULL;
      while (curr != NULL) {
        if (!strcmp(curr->name,key)) {
    
          lastHas = curr;
          return true;
        }
        beforeLastHas = curr;
        curr = curr->next;
      }
      return false;
    }
    
    /**
     * Get value associated with given key in database.
     * Return true on success, false on failure.
     *
     * Precondition: sdbm_has(key)
     */
    bool sdbm_get( const char *key, char *value ) { //Errors: 6)Don't have key
      if (!sdbm_has(key)) {
        printf("Precondition sdbm_has(%s) failed", key);
        err = 6;
        return false;
      }
      readVal(value, lastHas->offset);
      return true;
    }
    
    /**
     * Update value associated with given key in database
     * to given value. Return true on success, false on
     * failure.
     *
     * Precondition: sdbm_has(key)
     */
    bool sdbm_put( const char *key, const char *value ) {
      if (!sdbm_has(key)) {
        printf("Precondition !sdbm_has(%s) failed",key);
        err = 7;
        return false;
      }
      sdbm_remove(key);
      sdbm_insert(key,value);
      return true;
    }
    /**
     * Insert given key and value into database as a new
     * association. Return true on success, false on
     * failure.
     *
     * Precondition: !sdbm_has(key)
     */
    bool sdbm_insert( const char *key, const char *value ) { //Errors: 7)Already have key 8)Invalid key or value length
      if (sdbm_has(key)) {
        printf("Precondition !sdbm_has(%s) failed",key);
        err = 7;
        return false;
      }
      if (strlen(key) < MIN_KEY_LENGTH || strlen(key) > MAX_KEY_LENGTH || strlen(value) < MIN_VALUE_LENGTH || strlen(value) > MAX_VALUE_LENGTH) {
        printf("Invalid key or value length");
        err = 8;
        return false;
      }
      listAdd();
      tail->name = (char *)key;
      tail->val = malloc(sizeof(*value));
      strcpy(tail->val,value);
      return true;
    }
    
    /**
     * Remove given key and associated value from database.
     * Return true on success, false on failure.
     *
     * Precondition: sdbm_has(key)
     */
    bool sdbm_remove( const char *key ) {
      if (!sdbm_has(key)) {
        printf("Precondition !sdbm_has(%s) failed",key);
        err = 7;
        return false;
      }
      needNewDB = true;
      if (beforeLastHas == NULL) {
        head = lastHas->next;
      }
      else if (lastHas->next == NULL) {
        tail = beforeLastHas;
      }
      else {
        beforeLastHas->next = lastHas->next;
      }
      if (lastHas->val != NULL) {
        free(lastHas->val);
      }
      free(lastHas->name);
      free(lastHas);
      return true;
    }
    
    2 回复  |  直到 14 年前
        1
  •  2
  •   Fred Foo    14 年前

    这段代码有很多错误。仅举一个:

    filename = malloc(sizeof(*name)); 
    

    *name name char ,所以 sizeof(*name) == 1 . 要获得字符串的大小,请使用 strlen(name) + 1 . 更好的是,利用 strdup 如果你的系统有它。

        2
  •  0
  •   Peter Miehle    14 年前

    因此,所有sdbm_xxx函数都应该自己获取(或推断)所有必要的值。