C 中的关系数据库,具有简单的查询

Relational Database in C with simple queries

我正在尝试用 C 语言实现一个关系数据库。该程序提供了一个 config_file.txt,其中包含数据库中的关系数,后面是关系的名称。然后我为每个关系提供了一个 InsertRelationNameHere.sch 文件,其中包含元组的结构(第一行的属性数量,后跟属性和它们包含的数据格式)。此外,我还获得了一个 InsertRelationNameHere.dat 文件,它是一个二进制文件,每个关系中都存储了实际信息。

到目前为止我有三个问题:

nattr InsertRelationNameHere -(打印出“InsertRelationNameHere”中属性的数量)

tuplen InsertRelationNameHere -(打印每个元组在 .dat 文件中占用的字节数,元组的长度)

count InsertRelationNameHere -(打印每个关系中的元组数)

我遇到的问题是在计数查询中:

当我使用此查询时出现段错误,我不确定原因。

这是我的 func.c,其中包含所有查询的代码:

#include "globals.h"

FILE* check;

void nattr(char * str){
    char* token;
    const char s[2] = ".";
    strcpy(tempstr2, str);
    strncat(tempstr2,".sch", 4); // appends '.sch' extension to the file name
    if((check =(FILE*)fopen(tempstr2, "r")) == NULL){  // opens "InsertFileNameHere.sch"
        printf("", token);
        strcpy(token, strtok(tempstr2, s));
        fprintf(stdout,"Error: Invalid relation -- %s\n", token);
    }else{
        fscanf(check, "%d", &numattr);
        fprintf(stdout, "%d\n",numattr);
    }

    fclose(check);
}

void tuplen(char * str){

    char* token;
    const char s[2] = ".";
    strcpy(tempstr2, str);
    strncat(tempstr2,".sch", 4); // appends '.sch' extension to the file name
    if((check =(FILE*)fopen(tempstr2, "r")) == NULL){  // opens "InsertFileNameHere.sch"

        printf("", token);
        strcpy(token, strtok(tempstr2, s));
        fprintf(stdout,"Error: Invalid relation -- %s\n", token);
    }else{
        sumattr = 0;
        dummy = 0;
        fscanf(check, "%d", &numattr); // reads first line, gets number of lines to read
        for(i = 0; i < numattr; i++){
            fscanf(check, "%s", tempstr3); // reads first string on line, does nothing with it
            fscanf(check, "%s", tempstr3); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            fscanf(check, "%d", &dummy); // reads the int in third position on line, this is the number of bytes of the attribute
            fgetc(check);   // moves past the '\n'
            sumattr += dummy; // adds the amount of bytes for each attribute to the total
        }
        fprintf(stdout, "%d\n", sumattr);
    }

    fclose(check);
}

void count(char * str){
    char* token;
    const char s[2] = ".";
    strcpy(tempstr2, str);
    nattr(tempstr2);// sets numattr to the number of attributes in file: tempstr2
    tuplen(tempstr2);
    puts("here");
    printf("%s %s", numattr, sumattr);
    strncat(tempstr2,".dat", 4); // appends '.sch' extension to the file name
    if((check =(FILE*)fopen(tempstr2, "rb")) == NULL){  // opens "InsertFileNameHere.sch"
        printf("", token);
        strcpy(token, strtok(tempstr2, s));
        fprintf(stdout,"Error: Invalid relation -- %s\n", token);
    }else{

        for(i = 0; i< numattr; i++){    // these fscanf functions are only called to move through
            fread(tempstr3, 1, sumattr, check);
            fprintf(stdout, "%d\n", i);
            if(feof(check)){
                break;
            }
        }
        fprintf(stdout, "%d\n", i);
    }
    fclose(check);
}

这是我的 globals.h 头文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN 15
#define MAX_REL 10

FILE* files[MAX_REL*2];  // Array of FILES that are the '.sch' and '.dat' files for relations
char tempstr1[MAX_LEN+1], tempstr2[MAX_LEN+1], tempstr3[MAX_LEN], tempstr4[MAX_LEN], tempstr5[MAX_LEN], temp;
int num_rel, i, numattr, sumattr, dummy;
FILE* conf; // configure file

这也是我的 Prog.c 文件,我的主要方法是:

#include "globals.h"

int nattr(char * str);
//void fileOpener(FILE * conf);

int main(int argc, char * argv[]) {
    // Error checks for correct arguments
    if( argc != 4){
        fprintf(stdout, "Check usage");exit(1);
    }
    if((conf = fopen(argv[2], "r")) == NULL){
        fprintf(stdout, "could not open file %s\n", argv[1]);exit(1);
    }
    if((quef = fopen(argv[3], "r")) == NULL){
            fprintf(stdout, "could not open file %s\n", argv[2]);exit(1);
    }
    //fileOpener(conf);
    scanf("%s", tempstr1);                  // maybe you could try a switch here, getting some funky input characteristics
    while(strcmp(tempstr1, "exit") != 0){
        if(strcmp(tempstr1, "exit") == 0){
            strcpy(tempstr2, tempstr1);
            strcpy(tempstr3, tempstr1);
            strcpy(tempstr4, tempstr1);
            strcpy(tempstr5, tempstr1);
            exit(1);
        }

            while(strcmp(tempstr1, "nattr") == 0){
                scanf("%s", tempstr2);
                strcpy(tempstr3, tempstr1);
                strcpy(tempstr4, tempstr1);
                strcpy(tempstr5, tempstr1);
                nattr(tempstr2);
                scanf("%s", tempstr1);
                if(strcmp(tempstr1, "exit") == 0){
                    exit(1);
                }if(strcmp(tempstr1, "tuplen") != 0){
                    break;
                }
            }
            while(strcmp(tempstr1, "tuplen") == 0){
                scanf("%s", tempstr2);
                strcpy(tempstr3, tempstr1);
                strcpy(tempstr4, tempstr1);
                strcpy(tempstr5, tempstr1);
                tuplen(tempstr2);
                scanf("%s", tempstr1);
                if(strcmp(tempstr1, "exit") == 0){
                    exit(1);
                }if(strcmp(tempstr1, "tuplen") != 0){
                    break;
                }
            }
            while(strcmp(tempstr1, "count") == 0){
                scanf("%s", tempstr2);
                strcpy(tempstr3, tempstr1);
                strcpy(tempstr4, tempstr1);
                strcpy(tempstr5, tempstr1);
                count(tempstr2);
                scanf("%s", tempstr1);
                if(strcmp(tempstr1, "exit") == 0){
                    exit(1);
                }if(strcmp(tempstr1, "tuplen") != 0){
                    break;
                }
            }
    }
    return 0;
}

这是我用来测试这些功能的文件 Student.sch:

5
Name   S  25
Major  S  4
Minor  S  4
Totcr  I  4
Majcr  I  4

Student.dat:

536d 6974 682c 526f 6265 7274 0000 0000
0000 0000 0000 0000 0050 5359 0043 5349
0000 0000 3900 0000 2757 6f6f 6473 2c4a
616e 6500 0000 0000 0000 0000 0000 0000
0000 4353 4900 4255 5300 0000 0061 0000
0044 5261 6d73 6579 2c45 6c61 696e 6500
0000 0000 0000 0000 0000 0042 5553 0050
5359 0000 0000 6b00 0000 5857 6861 7274
6f6e 2c54 6f6d 0000 0000 0000 0000 0000
0000 0000 4255 5300 5053 5900 0000 0075
0000 0062 4261 6b65 722c 4e6f 726d 6100
0000 0000 0000 0000 0000 0000 0042 494f
0043 5349 0000 0000 2700 0000 19

config_file.txt:

2
Students
Courses

query_file.txt:(此文件尚未使用,但代码需要正确 运行)

nattr     Students
nattr     Norelation
tuplen    Badrelation
tuplen    Students  
infattr   Students  Minor
infattr   Students  Majorcr
infattr   Students  Totcr
infattr   Courses   Badattr
infattr   Courses   CName
infattr   Courses   Credits
count     Students
count     Student
count     Corses
count     Courses
project   Students     Name
project   courses      Credits
project   Students     Minor
project   Students     Majcredit
project   Courses      Credits
project   Courses      Instr
select    Students     Major !=  "BUS"
select    Students     Totcr   <   39
select    Courses      Credits  ==  3
select    Courses      Instr  ==  "KELLER"
select    Students     Name  >  40
select    Courses      Instr  ==  "Keller"
select    Courses      Credits  ==  "Keller"
select    Courses      Instructor  ==  "Keller"
quit

前两个查询的预期输出应如下所示:

gcc Prog3.c func.c globals.h   
./a.out Prog3.c config_file.txt query_file.txt
nattr Students
5
nattr Norelation
Error: Invalid relation -- Norelation
tuplen Students
41
tuplen Norelation
Error: Invalid relation -- Norelation

然后在使用 "count Students" 时出现段错误。

如果有任何帮助,只有当通过计数函数调用 "tuplen()" 时,我的程序才会在 tuplen 函数 strcpy(token, strtok(tempstr2, s)); 的行出现段错误,但当我在其上使用 tuplen 查询时不会拥有。

我知道这是一个很长的问题,对不起!但我试图在解释自己的同时尽量保持简短。如果有任何不清楚的地方,请告诉我,我可以详细说明。任何帮助表示赞赏!谢谢!

问题出在 count 中的这些行。

nattr(tempstr2);// sets numattr to the number of attributes in file: tempstr2
tuplen(tempstr2);

您应该将其替换为

nattr(str);// sets numattr to the number of attributes in file: tempstr2
tuplen(str);

调用nattr后,缓冲区tempstr2包含"Student.sch"。当您使用此字符串调用 tuplen 时,它会附加 .sch,从而产生 "Student.sch.sch"。打开此文件失败,因为它不存在。 这种情况下执行的代码是

printf("", token);
strcpy(token, strtok(tempstr2, s));
...

第一行什么都不做。由于 token 未初始化且包含一些随机值,因此 strcpy 尝试将 strtok 的输出复制到此随机位置。这会导致分段错误。

您可以通过将 str 传递给 nattrtuplen 来避免此分段错误。但是在 fopen 失败的情况下执行的代码是错误的,需要更改。使用全局变量也不好。


去掉你的全局变量。这是糟糕的编程并且容易出错。 使用结构并传递指向它的指针。明确哪个函数初始化了结构,没有其他函数错误地覆盖它的字段。

这里是一个你应该如何编写 nattr() 的例子。

// returns the nattr value of the given table or -1 if failed getting the value 
int nattr(char * str){
    char tmpstr[MAX_LEN+1+4];
    FILE * f;
    int numattr = -1;

    strcpy(tmpstr, str);
    strncat(tmpstr,".sch", 4); // appends '.sch' extension to the file name
    f = fopen(tmpstr, "r");  // opens "InsertFileNameHere.sch"
    if (f == NULL){ 
        printf("Error: Invalid relation -- %s\n", str);
        return -1;
    }

    // Try read numattr, leave it unchanged if fails. numattr is then -1
    if (fscanf(f, "%d", &numattr) != 1)
        return -1;
    printf("%d\n", numattr);
    fclose(f);
    return numattr;
}

因为我还有一些时间,这里是tuplen

// Return the byte size of a tuple or -1 if failed 
int tuplen(char * str){
    char tmpstr[MAX_LEN+1+4];
    char attrName[MAX_LEN+1];
    char attrType[2];
    int numattr = -1, sumattr = 0, attrSize;
    FILE * f;

    strcpy(tmpstr, str);
    strncat(tmpstr,".sch", 4); // appends '.sch' extension to the file name
    f = fopen(tmpstr, "r");
    if (f == NULL){ 
        printf("Error: Invalid relation -- %s\n", str);
        return -1;
    }

    if (fscanf(f, "%d", &numattr) != 1)
        return -1;
    for (i = 0; i < numattr; i++){
        if (fscanf(f, " %s %1s %d", attrName, attrType, &attrSize) != 3)
            return -1; // try reading the three fields of one line
        sumattr += attrSize; // adds the amount of bytes for each attribute to the total
    }
    printf("%d\n", sumattr);
    fclose(f);
    return sumattr;
}