2010-07-18 31 views
7

Tôi đang cố gắng lưu cấu trúc có chuỗi char * vào tệp.Lưu cấu trúc C với chuỗi char * vào một tệp

struct d_object { 
    int flags; 
    int time; 
    int offset; 
    char *filename; 
}; 

Vấn đề là khi làm điều đó, tôi rõ ràng sẽ chỉ lưu địa chỉ của con trỏ đó chứ không phải chuỗi. Vì vậy, những gì tôi đã làm chỉ đơn giản là sử dụng một mảng ký tự và tôi buộc phải thiết lập kích thước tối đa của chuỗi. Điều này hoạt động tốt, tuy nhiên tôi đã tự hỏi nếu có anyway của lưu trữ các struct với một char * (mà tôi malloc tại một số điểm) trong một tập tin và sau đó lấy nó. Tôi có thể lưu chuỗi và cấu trúc riêng biệt và sau đó lấy chúng nhưng nó là một mớ hỗn độn. Sẽ tốt hơn nếu tôi có thể tải và lưu toàn bộ cấu trúc (phần trên) vào tệp. Cảm ơn!

Mã với mảng char là dưới đây:

#include <stdio.h> 
#include <string.h> 
#include <fcntl.h> 

struct d_object { 
    int flags; 
    int time; 
    int offset; 
    char filename[255]; 
}; 

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

    struct d_object fcb; 

    fcb.flags=5; 
    fcb.time=100000; 
    fcb.offset=220; 
    strncpy(fcb.filename,"myfile",255); 


    int fd=open("testfile",O_RDWR); 
    write(fd,&fcb,sizeof(fcb)); 
    close(fd); 


    int fd2 = open("testfile",O_RDONLY); 
    struct d_object new_fcb; 
    read(fd2,&new_fcb,sizeof(new_fcb)); 

    printf("read from file testfile: %s\n",new_fcb.filename); 

    return 0; 

} 

P.S .: tôi không sử dụng các chức năng STREAM đơn giản vì đây là thực sự có nghĩa là để được chạy trên một hệ điều hành nhúng mà không có họ. Tôi vừa điều chỉnh mã cho * BSD/Linux để nó có ý nghĩa hơn khi đặt câu hỏi.

+0

Cách bạn đang sử dụng fread là rất sai. Bạn không khai báo một bộ đệm char và đọc nó, sau đó trỏ một con trỏ struct vào nó. Đơn giản chỉ cần khai báo một struct và chuyển con trỏ tới cấu trúc đó để fread. Cách bạn đã thực hiện nó không phải là hợp lệ C và có khả năng gặp sự cố trên các kiến ​​trúc RISC với các yêu cầu liên kết chặt chẽ. –

+1

ok, cảm ơn vì điều đó. Tuy nhiên đó không phải là vấn đề chính tại thời điểm này. – theprole

Trả lời

7

Tôi hiểu tính di động mà không phải là một vấn đề, kể từ khi bạn đang làm việc cho một hệ thống nhúng. Trong trường hợp khác, bạn nên sử dụng một cái gì đó như XML.

Bạn có thể chuyển đổi lại mã của bạn để:

struct d_object { 
    int flags; 
    int time; 
    int offset; 
    char * filename; 
}; 

Và sau đó lưu mỗi phần dữ liệu riêng lẻ:

write(fd, &record.flags, sizeof(int)); 
write(fd, &record.time, sizeof(int)); 
write(fd, &record.offset, sizeof(int)); 
int filename_length = strlen(filename); 
write(fd, &filename_length, sizeof(int)); 
write(fd, record.filename, filename_length); 

Để đọc, bạn sẽ phải đọc từng mục separatedly, và sau đó tên tệp:

int filename_length; 

read(fd, &emptyRecord.flags, sizeof(int)); 
read(fd, &emptyRecord.time, sizeof(int)); 
read(fd, &emptyRecord.offset, sizeof(int)); 

read(filename_length, sizeof(int), 1, file); 
emptyRecord.filename = (char *) malloc(sizeof(char) * (filename_length +1)); 
read(fd, emptyRecord.filename, filename_length); 
*(emptyRecord.filename + filename_length) = 0; 
+0

cảm ơn, ba bình luận cuối cùng đã giúp giải đáp – theprole

2

Việc tuần tự hóa không bao giờ đẹp. Làm thế nào về lưu trữ chiều dài của chuỗi trong con trỏ, và để cho chuỗi theo cấu trúc trong tập tin? Nội dung như thế này (cảnh báo, mã được biên dịch bởi não):

void write_object(struct d_object *s, int fd) { 
    struct d_object copy = *s; 
    copy.filename = (char*)strlen(s->filename); 
    write(fd, &copy, sizeof(copy)); 
    write(fd, s->filename, (size_t)copy.filename); 
} 

void read_object(struct d_object *s, int fd) { 
    read(fd, s, sizeof(struct d_object)); 
    char *filename = malloc(((size_t)s->filename) + 1); 
    read(fd, filename, (size_t)s->filename); 
    filename[(size_t)s->filename] = '\0'; 
    s->filename = filename; 
} 
+1

Tôi không hoàn toàn đồng ý với việc đặt chiều dài của tên trong một 'char *'. Đó là một giải pháp thực tế và hoạt động, nhưng là khó hiểu và được coi là "phong cách xấu". Nó thậm chí có thể nổ tung nếu 'int' từng chứng minh lớn hơn' char * '. –

+0

@Carl, nhiều như tôi đồng ý về nguyên tắc, trong khi 'int' có thể lớn hơn' char * ', giá trị trả về của' strlen' không bao giờ có thể lớn hơn 'char *' bởi một đối số đếm đơn giản. :-) Điều đó nói rằng, thay vì đúc, tôi sẽ sử dụng '(char *) 0 + strlen (s-

1

Không, không có phép thuật được xây dựng trực tiếp vào ngôn ngữ để thực hiện việc này cho bạn.

Điều bạn cần làm là viết một vài hàm để chuyển đổi dữ liệu thành dạng có thể ghi và đọc lại từ tệp. Điều này được gọi là "tuần tự hóa"/"deserialization" trong các ngôn ngữ mà làm cho nhiều phiền phức về nó.

Cấu trúc cụ thể của bạn, bạn có thể làm một cái gì đó như viết công cụ nhị phân vào tệp trực tiếp từ cấu trúc, và sau đó theo dõi nó với nội dung của bộ đệm ký tự. Bạn có thể làm mọi thứ dễ dàng hơn cho chính mình khi bạn đọc thời gian nếu bạn đặt trước dữ liệu ký tự bằng cách chỉ định độ dài của nó bằng int.

Khi bạn đọc lại nội dung đó, bạn sẽ muốn tự mình malloc/calloc một bộ nhớ để giữ dữ liệu char; nếu bạn lưu trữ kích thước bạn sẽ biết chỉ cần làm thế nào lớn để làm cho malloc đó. Đọc dữ liệu nhị phân vào cấu trúc, đọc dữ liệu char vào bộ nhớ malloc'd, lưu con trỏ vào đoạn malloc vào cấu trúc và bạn đã tạo lại bản gốc.

Không có phép thuật. Chỉ cần dữ liệu và một chút mỡ khuỷu tay.

EDIT

Trong khi tôi đã viết về việc này, Thomas mã hóa một ví dụ. Tôi nghĩ rằng câu trả lời của chúng tôi bổ sung cho nhau rất tốt; họ nên nói với bạn mọi thứ bạn cần biết.

+0

cảm ơn, ba bình luận cuối cùng đã giúp với câu trả lời – theprole

2

Vấn đề của bạn ở đây thực sự là một triệu chứng của vấn đề lớn hơn nhiều: bạn không nên đọc/ghi cấu trúc dữ liệu nhị phân giữa bộ nhớ và tệp. Không chỉ có cách rõ ràng để đọc/ghi cấu trúc với con trỏ đến dữ liệu khác (điều này không chỉ áp dụng cho chuỗi nhưng dữ liệu lồng nhau, danh sách liên kết, v.v.) nhưng định dạng dữ liệu trên đĩa sẽ tùy thuộc vào máy chủ của bạn và C thực hiện và sẽ không được di động đến các môi trường khác.

Thay vào đó, bạn nên thiết kế một định dạng cho dữ liệu của bạn trên đĩa và viết các chức năng để lưu và tải các dữ liệu, hoặc sử dụng một định dạng hiện có và tìm mã thư viện để sử dụng nó. Điều này thường được gọi là "serialization".

Nếu dữ liệu của bạn là thứ bậc, JSON, XML, hoặc EBML có thể thích hợp. Nếu nó khá đơn giản, các tập tin văn bản phẳng hoặc một định dạng nhị phân homebrew (được viết bằng byte-by-byte để nó di động) có thể thích hợp.

Vì bạn có vẻ không quen với những vấn đề này, nên viết một số mã để tải/lưu một vài định dạng tệp nhị phân đơn giản (như .tga hoặc .wav) như một bài tập trước khi bạn cố thiết kế dữ liệu của riêng bạn.

+0

Tôi biết rằng nó sẽ không được di chuyển và nó là tốt vì nó không cần phải được. Mã này đang chạy trên một hệ điều hành nhúng (contiki) với 10k ram! Như bạn có thể tưởng tượng không ai trong số các công nghệ mà bạn đề cập sẽ làm việc từ xa, hãy để một mình có hỗ trợ trong os nhỏ bé này. Tôi nên làm điều này rõ ràng trong câu hỏi. Cảm ơn các đề xuất anyway. Tôi vẫn đang cố gắng để xem làm thế nào để serialize các dữ liệu – theprole

+0

một textfile có thể là giải pháp tốt nhất tôi nghĩ.Hoặc nếu tính di động không phải là một vấn đề, bạn có nghĩ rằng nó là ok để viết thẳng nhị phân vào tập tin? – theprole

2

Bây giờ tôi biết bản chất của vấn đề của bạn, tại sao không thử linh hoạt mảng? Thay vì sử dụng char *filename;, hãy sử dụng char filename[1]malloc(sizeof struct d_object + filename_len) để phân bổ cấu trúc của bạn. Thêm thành viên kích thước và bạn có thể dễ dàng ghi đối tượng vào đĩa bằng một cuộc gọi đến write và tải nó từ đĩa với 2 cuộc gọi (đầu tiên đọc phần tử kích thước, thứ hai để đọc toàn bộ đối tượng sau khi phân bổ nó).

Lưu ý rằng cách "chính thức" để thực hiện mảng linh hoạt trong C99 là [] thay vì [1], nhưng [1] được đảm bảo hoạt động như một hệ quả của các yêu cầu khác trong tiêu chuẩn và hoạt động trên C89. [1] sẽ lãng phí một vài byte mặc dù vậy, vì vậy nếu trình biên dịch của bạn hỗ trợ [], bạn có thể muốn sử dụng nó.

Các vấn đề liên quan