fread返回0并且无法在Windows机器上打开db文件。可以找不到文件吗?或者是权限?

时间:2016-04-14 01:12:43

标签: c windows fopen fread

当fread加载db文件时,我在C(Learn C The Hard Way: Ex17)和Database_load函数中做了一个教程" ERROR!无法加载数据库。"被扔了。为什么不能读取db?该文件位于目录中。可以找不到文件吗?或者是否需要更改权限?

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAX_DATA 512
#define MAX_ROWS 100

struct Address {
    int id;
    int set;
    char name[MAX_DATA];
    char email[MAX_DATA];
};
struct Database {
    struct Address rows [MAX_ROWS];
};
struct Connection {
    FILE *file;
    struct Database *db;
};
void die (const char *message)
{
    if(errno) {
        perror(message);
    } else {
        printf("ERROR!\t%s\n", message);
    }

exit(1);
}
void Address_print(struct Address *addr)
{
    printf("%d %s %s\n", addr->id, addr->name, addr->email);
}
void Database_load(struct Connection *conn)
{
    // Error shows up here!
    int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
    if(rc != 1) {
        die("Failed to load database.");
    }
}
struct Connection *Database_open(const char *filename, char mode)
{
    struct Connection *conn = malloc(sizeof(struct Connection));

    if(!conn){
        die("Memory error.");
    }

    conn->db = malloc(sizeof(struct Database));

    if (!conn->db) {
        die("Memory error");
    }

    if(mode == 'c') {
        conn->file = fopen(filename, "w"); // Problem is here! See @fluter's solution

    if (conn->file == NULL) {
        fprintf(stderr, "open failed, errno %d: %s\n", errno, strerror(errno));
    }

} else {

    conn->file = fopen(filename, "r+"); // And the problem is here!

    if (conn->file == NULL) {
        fprintf(stderr, "open failed, errno %d: %s\n", errno, strerror(errno));
    } else {
        Database_load(conn);
    }
}

if(!conn->file){
    die("Failed to open the file.");
}
    return conn;
}
void Database_close(struct Connection *conn)
{
    if(conn) {
        if(conn->file) fclose(conn->file);
        if(conn->db) free(conn->db);
        free(conn);
    }
}
void Database_write(struct Connection *conn)
{
    rewind(conn->file);

    int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);

    if(rc != 1) {
        die("Failed to write database.");
    }

    rc = fflush(conn->file);

    if(rc == -1) {
        die("Cannot flush database.");
    }
}
void Database_create(struct Connection *conn)
{
    int i = 0;

    for (i = 0; i < MAX_ROWS; i++) {
        // make a prototype to initialize the address
        struct Address addr = {.id = i, .set = 0};

        // then assign the prototype to the database
        conn->db->rows[i] = addr;
    }
}
void Database_set(struct Connection *conn, int id, const char *name, const char *email)
{
    struct Address *addr = &conn->db->rows[id];

    if (addr->set) {
        die("Address already set, delete it first");
    }

    addr->set = 1;

    // WARNING!! Strncpy has a bug - needs to be fixed ...
    char *res = strncpy(addr->name, email, MAX_DATA);

    if(!res) {
           die("Name copy failed.");
    }

    // ... by adding NULL to the end of the array
    addr->name[sizeof(addr->name)-1] = '\0';

    res = strncpy(addr->email, email, MAX_DATA);

    if(!res) {
        die("Email copy failed.");
    }
}

void Database_list(struct Connection *conn)
{
    int i = 0;
    struct Database *db = conn->db;

    for(i = 0; i < MAX_ROWS; i++) {
        struct Address *cur = &db->rows[i];

        if(cur->set) {
            Address_print(cur);
        }
    }
}

int main (int argc, char *argv[]){


if(argc < 3) {
    die("USAGE: ex17.exe <dbfile> <action> [action params]");
} 

char *filename = argv[1];
char action = argv[2][0];

struct Connection *conn = Database_open(filename, action);

int id = 0;

if(argc > 3) {
    id = atoi(argv[3]);
}

if(argc > MAX_ROWS) {
    die("There aren't that many records");
}

switch(action) {
    case 'c':
        Database_create(conn);
        Database_write(conn);
        break;
    case 's':
        if(argc != 4) {
            die("Id, name, and email address are needed.");
        }

        Database_set(conn, id, argv[4], argv[5]);
        Database_write(conn);
        break;
    case 'l':
        Database_list(conn);
        break;
    default:
        die("\tInvalid action.\n\
                \tPlease use:\n\
                \tPlease use:\n\
                \tc = create\n\
                \td = delete\n\
                \tl = list\n\
                \ts = set\n");
    }
Database_close(conn);
return 0;
}

问题是,即使错误显示&#34;无法加载数据库。&#34;,文件肯定在文件夹中。

Screen Shot ex17_exe

我想知道它是否具有权限(参见上面的屏幕截图)。对我来说,我有rw,但Admin只有r。当我在运行终端时,不应该这样,因为管理员没有w,不应该吗?

1 个答案:

答案 0 :(得分:5)

首先,您打印的是conn-&gt;文件的地址,而不是conn-&gt;文件本身,当然conn-&gt;文件的地址不是NULL。请尝试以下方法:

printf("conn->file address: %p\n", conn->file);

如果打开失败,这将打印NULL,并且对于确切的错误,打印errno以找出错误的根本原因:

if (conn->file == NULL) {
    fprintf(stderr, "open failed, errno %d: %s\n", errno, strerror(errno);
} else {
    // open db
}

对于Windows上的故障,这是因为Windows直接处理文本和二进制打开的文件,而在Linux上,它们是相同的。如果在文本模式下打开文件,Windows会将CRLF转换为LF并将ctrl-z控制字符视为EOF标记,这正是您在Windows上看到的错误,因此在Database_open中,使用以下命令打开文件:

if(mode == 'c') {
    conn->file = fopen(filename, "wb");
} else {
    conn->file = fopen(filename, "rb+");
    if(conn->file) {
        Database_load(conn);
    }
}

这也适用于Linux,因为Linux上的文件是二进制模式。