当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;,文件肯定在文件夹中。
我想知道它是否具有权限(参见上面的屏幕截图)。对我来说,我有rw,但Admin只有r。当我在运行终端时,不应该这样,因为管理员没有w,不应该吗?
答案 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上的文件是二进制模式。