Вы работаете на 64-битной машине, потому что ваши указатели имеют длину 8 байт.
Вы пытаетесь записать данные в файл, а затем снова прочитать его.проблемы, потому что указатели не очень хорошо пишут.(Точнее: указатели могут быть записаны без каких-либо проблем, но указатели имеют значение только в текущей запущенной программе и редко подходят для записи на диск и еще реже подходят для чтения с диска.)
Эта часть вашего кода иллюстрирует проблему:
struct files {
char *file_name;
int file_size;
};
typedef struct file_header {
int file_count;
struct files file[5];
} metadata;
metadata *create_header();
int main() {
FILE *file = fopen("/home/test/main/Integration/testing/file.txt", "w");
metadata *header;
header = create_header();
if(header != NULL)
{
printf("size of Header is %d\n",sizeof(header));
}
Дополнительные комментарии:
- Превратите имя файла в аргумент
main()
или, по крайней мере, в переменную,Если вы дважды напишите имя, вам будет сложно его изменить. - Хорошо, что вы обнаруживаете ошибки.Тем не менее, я не буду критиковать его, хотя в нем есть значительные возможности для улучшения.
Основные комментарии:
Вы видите size of Header is 8
в выводе, потому что header
является указателем.sizeof(metadata)
(тип, на который указывает header
) намного больше, вероятно, 48 байтов, но это зависит от того, как ваш компилятор выравнивает и упаковывает данные в структуре.
if (file != NULL) {
if (fwrite(&header, sizeof(header), 1, file) < 1) {
puts("short count on fwrite");
}
fclose(file);
}
Этот код записывает 8 байтов данных в файл.Он пишет адрес, по которому хранится ваша переменная header
.Он не записывает ничего из данных, на которые он указывает.
Что ближе к тому, что вы ищете (но все еще не будет работать):
if (fwrite(header, sizeof(*header), 1, file) < 1) {
puts("short count on fwrite");
}
Это будетзаписать 48 байтов или около того в файл.Тем не менее, ваш файл не будет содержать имена файлов;он будет просто содержать указатели на то, где имена файлов были сохранены во время записи файла.Будь здесь очень осторожен.Если вы прочитаете этот файл, вы можете даже увидеть, что он работает, потому что имена, возможно, еще не стерты из памяти.
Чтобы получить имена файлов в файле, вам придется обрабатывать каждое из них отдельно.Вам придется принять решение о конвенции.Например, вы можете решить, что перед именем будет стоять 2-байтовый unsigned short
, который содержит длину имени файла, L, за которым следует L + 1 байт данных, содержащих имя файла и его терминал NUL '\0'
,Затем вы должны написать другие (фиксированный размер) части данных для каждого файла.И вы бы повторили этот процесс для каждого из файлов.Операция обратного чтения файла требует понимания письменной структуры.В точке, где вы ожидаете имя файла, вы прочитаете двухбайтовую длину и можете использовать эту длину, чтобы выделить место для имени файла.Затем вы читаете байты L + 1 во вновь выделенное имя файла.Затем вы читаете другие данные фиксированной длины для файла, а затем переходите к следующему файлу.
Если вы хотите иметь возможность сделать все это за один fwrite()
, а затем fread()
, вывам придется пересмотреть структуру данных:
struct files {
char file_name[MAX_PERMITTED_FILENAME_LENGTH];
int file_size;
};
Вы можете решить, какова максимально допустимая длина имени файла, но она фиксирована.Если ваши имена короткие, вы не используете все пространство;если ваши имена длинные, они могут быть усечены.Ваш metadata
размер структуры теперь значительно увеличивается (по крайней мере, если MAX_PERMITTED_FILENAME_LENGTH
- разумный размер, скажем, между 32 и 1024 байтами).Но вы можете читать и записывать всю структуру metadata
за одну операцию.
Спасибо за ваш ответ, но я новичок в C, так как я могу достичь этой цели?
В конце концов, вы сможете закодировать его примерно так:
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAX_FILES = 5 };
struct files
{
char *file_name;
int file_size;
};
typedef struct file_header
{
int file_count;
struct files file[MAX_FILES];
} metadata;
static void err_exit(const char *format, ...);
static metadata *create_header(const char *directory);
static void release_header(metadata *header);
static void write_header(FILE *fp, const metadata *header);
static metadata *read_header(FILE *fp);
static void dump_header(FILE *fp, const char *tag, const metadata *header);
int main(int argc, char **argv)
{
if (argc != 3)
err_exit("Usage: %s file directory\n", argv[0]);
const char *name = argv[1];
const char *path = argv[2];
FILE *fp = fopen(name, "wb");
if (fp == 0)
err_exit("Failed to open file %s for writing (%d: %s)\n", name, errno, strerror(errno));
metadata *header = create_header(path);
dump_header(stdout, "Data to be written", header);
write_header(fp, header);
fclose(fp); // Ignore error on close
release_header(header);
if ((fp = fopen(name, "rb")) == 0)
err_exit("Failed to open file %s for reading (%d: %s)\n", name, errno, strerror(errno));
metadata *read_info = read_header(fp);
dump_header(stdout, "Data as read", read_info);
release_header(read_info);
fclose(fp); // Ignore error on close
return 0;
}
static metadata *create_header(const char *path)
{
int file_count = 0;
DIR * dirp = opendir(path);
struct dirent * entry;
if (dirp == 0)
err_exit("Failed to open directory %s (%d: %s)\n", path, errno, strerror(errno));
metadata *header = (metadata *)malloc(sizeof(metadata));
if (header == 0)
err_exit("Failed to malloc space for header (%d: %s)\n", errno, strerror(errno));
header->file_count = 0;
while ((entry = readdir(dirp)) != NULL && file_count < MAX_FILES)
{
// d_type is not portable - POSIX says you can only rely on d_name and d_ino
if (entry->d_type == DT_REG)
{ /* If the entry is a regular file */
// Avoid off-by-one under-allocation by using strdup()
header->file[file_count].file_name = strdup(entry->d_name);
if (header->file[file_count].file_name == 0)
err_exit("Failed to strdup() file %s (%d: %s)\n", entry->d_name, errno, strerror(errno));
//Put static but i have logic for this i will apply later.
header->file[file_count].file_size = 10;
file_count++;
}
}
header->file_count = file_count;
closedir(dirp);
//printf("File Count : %d\n", file_count);
return header;
}
static void write_header(FILE *fp, const metadata *header)
{
if (fwrite(&header->file_count, sizeof(header->file_count), 1, fp) != 1)
err_exit("Write error on file count (%d: %s)\n", errno, strerror(errno));
const struct files *files = header->file;
for (int i = 0; i < header->file_count; i++)
{
unsigned short name_len = strlen(files[i].file_name) + 1;
if (fwrite(&name_len, sizeof(name_len), 1, fp) != 1)
err_exit("Write error on file name length (%d: %s)\n", errno, strerror(errno));
if (fwrite(files[i].file_name, name_len, 1, fp) != 1)
err_exit("Write error on file name (%d: %s)\n", errno, strerror(errno));
if (fwrite(&files[i].file_size, sizeof(files[i].file_size), 1, fp) != 1)
err_exit("Write error on file size (%d: %s)\n", errno, strerror(errno));
}
}
static metadata *read_header(FILE *fp)
{
metadata *header = malloc(sizeof(*header));
if (header == 0)
err_exit("Failed to malloc space for header (%d:%s)\n", errno, strerror(errno));
if (fread(&header->file_count, sizeof(header->file_count), 1, fp) != 1)
err_exit("Read error on file count (%d: %s)\n", errno, strerror(errno));
struct files *files = header->file;
for (int i = 0; i < header->file_count; i++)
{
unsigned short name_len;
if (fread(&name_len, sizeof(name_len), 1, fp) != 1)
err_exit("Read error on file name length (%d: %s)\n", errno, strerror(errno));
files[i].file_name = malloc(name_len);
if (files[i].file_name == 0)
err_exit("Failed to malloc space for file name (%d:%s)\n", errno, strerror(errno));
if (fread(files[i].file_name, name_len, 1, fp) != 1)
err_exit("Read error on file name (%d: %s)\n", errno, strerror(errno));
if (fread(&files[i].file_size, sizeof(files[i].file_size), 1, fp) != 1)
err_exit("Read error on file size (%d: %s)\n", errno, strerror(errno));
}
return(header);
}
static void dump_header(FILE *fp, const char *tag, const metadata *header)
{
const struct files *files = header->file;
fprintf(fp, "Metadata: %s\n", tag);
fprintf(fp, "File count: %d\n", header->file_count);
for (int i = 0; i < header->file_count; i++)
fprintf(fp, "File %d: size %5d, name %s\n", i, files[i].file_size, files[i].file_name);
}
static void release_header(metadata *header)
{
for (int i = 0; i < header->file_count; i++)
{
/* Zap file name, and pointer to file name */
memset(header->file[i].file_name, 0xDD, strlen(header->file[i].file_name)+1);
free(header->file[i].file_name);
memset(&header->file[i].file_name, 0xEE, sizeof(header->file[i].file_name));
}
free(header);
}
static void err_exit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
Я скомпилировал его как dump_file
и запустил, как показано:
$ dump_file xyz .
Metadata: Data to be written
File count: 5
File 0: size 10, name .gitignore
File 1: size 10, name args.c
File 2: size 10, name atob.c
File 3: size 10, name bp.pl
File 4: size 10, name btwoc.c
Metadata: Data as read
File count: 5
File 0: size 10, name .gitignore
File 1: size 10, name args.c
File 2: size 10, name atob.c
File 3: size 10, name bp.pl
File 4: size 10, name btwoc.c
$ odx xyz
0x0000: 05 00 00 00 0B 00 2E 67 69 74 69 67 6E 6F 72 65 .......gitignore
0x0010: 00 0A 00 00 00 07 00 61 72 67 73 2E 63 00 0A 00 .......args.c...
0x0020: 00 00 07 00 61 74 6F 62 2E 63 00 0A 00 00 00 06 ....atob.c......
0x0030: 00 62 70 2E 70 6C 00 0A 00 00 00 08 00 62 74 77 .bp.pl.......btw
0x0040: 6F 63 2E 63 00 0A 00 00 00 oc.c.....
0x0049:
$
Мне, вероятно, следовало бы переименовать err_exit()
в err_sysexit()
и изменить обработку ошибок так, чтобы errno
и соответствующая строка обрабатывались внутри этой функции, вместо того, чтобы повторно добавлять errno
и strerror(errno)
к вызовамerr_exit()
.
Информация из комментариев
Перенос некоторых довольно обширных комментариев на вопрос:
Я пробовал приведенный выше код, но после File : 4
появляется ошибка сегментации, что означает, что запись данных работает правильно, но у меня возникли проблемы с чтением данных. Nimit
Я пробовал приведенный выше код, и я получаю ошибку сегментации, когда я читаю данные из файла. user1089679
Упс: valgrind
предупреждает меня о недопустимой записи в release_header()
.Это бы все испортило.Это не трудно решить, хотя - это вторая memset()
в release_header()
, которая причиняет вред;Я случайно опустил амперсанд:
memset( header->file[i].file_name, 0xEE, sizeof(header->file[i].file_name)); // Broken
memset(&header->file[i].file_name, 0xEE, sizeof(header->file[i].file_name)); // Correct
Это исправлено в коде.Обратите внимание, что обе операции memset()
выполняются в коде, чтобы гарантировать, что при повторном использовании памяти она не будет содержать предыдущих действительных данных, что было связано с риском, учитывая, что код первоначально записывал указатели на диск, а затем считывал их снова.Вызов memset()
не будет присутствовать в обычном рабочем коде.
Обратите внимание, что odx
- это программа самодельного шестнадцатеричного дампа (в Mac OS X по умолчанию нет программы hd
).Ваша система может уже иметь hd
для шестнадцатеричного дампа, или вы можете попробовать hd или попробовать свой собственный Google Fu, чтобы найти альтернативы.
Просто хочу спросить, я хочучтобы запустить эту программу на кроссплатформенном, есть ли проблемы с низко битными машинами? Nimit
Нет проблем с этим кодом на машинах с прямым или младшим порядком байтов;могут возникнуть проблемы, если вы перенесете данные с машины с прямым порядком байтов (Intel) на машину с прямым порядком байтов (SPARC, PPC, ...) или наоборот.Код, вероятно, также чувствителен к 32-битной и 64-битной сборкам;Я не определял размеры полей как n-бит, но как удобные типы, такие как int, которые могут меняться между системами.Если вам нужны переносимые данные, определитесь с размерами полей (1, 2, 4, 8 байт, в основном, по крайней мере, для нестроковых данных), а затем запишите их стандартным способом - сначала MSB (big-endian) иливозможно LSB первый (little-endian).