2010-07-18 115 views
7

我試圖用char *字符串將結構保存到文件中。將一個char *字符串的C結構保存到一個文件中

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

問題是,當我這樣做時,我顯然只會保存該指針的地址而不是字符串。所以我所做的只是使用一個字符數組,但我不得不設置字符串的最大大小。這工作正常,但我想知道是否有存儲結構與char *(我malloc在某些時刻)在一個文件中,然後檢索它。我可以保存字符串和結構分開,然後檢索它們,但它是相當混亂。如果我可以加載並保存整個結構(上面的結構)到文件中,那將會更好。謝謝!

與字符數組的代碼如下:

#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:我不使用流功能,只是因爲這實際上意味着要在不具備他們的嵌入式操作系統上運行。我剛剛修改了* BSD/Linux的代碼,所以在提問時更有意義。

+0

您使用fread的方式非常錯誤。你不需要聲明char緩衝區並讀入它,然後在它指向一個結構指針。只需聲明一個結構體並將指針傳遞給該結構體即可。你做這件事的方式並不是有效的C,並且可能會在RISC架構上出現嚴格的對齊要求。 – 2010-07-18 09:35:22

+1

好的,謝謝你。但目前這不是主要問題。 – theprole 2010-07-18 09:36:43

回答

7

我知道可移植性不是問題,因爲您正在爲嵌入式系統工作。在其他情況下,你應該使用類似XML的東西。

你可以將你的回代碼:

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

然後保存每個數據塊獨立:

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); 

閱讀,你必須separatedly讀取每個項目,然後文件名:

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

謝謝,最後三條評論對答案有所幫助 – theprole 2010-07-18 10:13:09

2

序列化從來不漂亮。如何將字符串的長度存儲在指針中,並讓字符串跟隨文件中的結構?像這樣的東西(警告,大腦編譯的代碼):

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

我並不完全同意將名字的長度放在'char *'中。這是一個務實的解決方案並且很有效,但是令人困惑並被認爲是「糟糕的風格」。如果'int'證明大於'char *',它甚至會炸燬。 – 2010-07-18 09:47:10

+0

@Carl,儘管我原則上同意,而'int'理論上可以大於'char *',但是'strlen'的返回值永遠不會比'char *'大一些,只需一個簡單的計數參數。 :-) 這就是說,而不是強制轉換,我會使用'(char *)0 + strlen(s- 2010-07-18 10:25:54

1

不,沒有直接在語言中爲你做這件事的魔法。

你需要做的是編寫幾個函數將數據轉換爲可寫形式,並從文件中讀回。這被稱爲「序列化」/「反序列化」的語言,使它更多的小題大做。

你的特定結構,你可以做一些事情,比如直接從結構中寫入二進制文件到文件中,然後用字符緩衝區的內容進行跟蹤。如果在字符數據前加上指定長度的int,則可以使自己的閱讀時間更容易。

當你讀到那些東西時,你會想自己想要一個內存塊來存放char數據;如果你存儲了大小,你會知道該malloc有多大。將二進制數據讀入結構,將char數據讀入malloc'd內存,將指向malloc塊的指針存儲到結構中,然後重新創建原始數據。

沒有魔力。只是數據和一點點的潤滑脂。

編輯

雖然我寫了這樣做,托馬斯編碼的一個例子。我認爲我們的答案非常好,一起他們應該告訴你你需要知道的一切。

+0

謝謝,最後三條評論對答案有幫助 – theprole 2010-07-18 10:11:40

2

這裏的問題實際上是一個更大的問題的症狀:你不應該在內存和文件之間讀/寫二進制數據結構。不僅沒有明確的方式來讀/寫指針指向其他數據的結構(這不僅適用於字符串,還適用於嵌套數據,鏈表等),但磁盤上的數據格式取決於您的主機和C實現並且不能移植到其他環境中。

相反,您應該爲磁盤上的數據設計一種格式,並編寫用於保存和加載數據的函數,或使用現有格式並查找使用它的庫代碼。這通常被稱爲「序列化」。

如果您的數據是分層的,那麼JSON,XML或EBML可能是合適的。如果相當簡單,平面文本文件或自制二進制格式(逐字節書寫,因此它是可移植的)可能是合適的。

由於您似乎並不熟悉這些問題,所以在嘗試設計某些內容之前,編寫一些加載/保存一些簡單的二進制文件格式(如.tga或.wav)的代碼可能是值得的你自己的數據。

+0

我知道它不會攜帶,因爲它不需要。此代碼在嵌入式操作系統(contiki)上運行,內存爲10k!你可以想象,你提到的所有技術都不能遠程工作,更不用說在這個微小的操作系統上有支持。我應該在問題中明確表達這一點。無論如何感謝您的建議。我仍然試圖看看如何序列化數據 – theprole 2010-07-18 09:51:02

+0

文本文件可能是我認爲的最佳解決方案。或者,如果可移植性不是問題,您認爲將直接二進制文件寫入文件可以嗎? – theprole 2010-07-18 09:52:01

2

現在我知道你的問題的本質,爲什麼不嘗試靈活數組?而不是使用char *filename;,請使用char filename[1]malloc(sizeof struct d_object + filename_len)分配您的結構。添加一個大小成員,您可以輕鬆地將對象寫入磁盤,只需一次調用write,並從磁盤加載2個調用(首先讀取大小元素,第二次讀取分配給它的整個對象)。

需要注意的是「官方」的方式做靈活的陣列,C99是[]而非[1],但[1]保證爲標準的其他要求的結果,以工作和過上C89的作品。 [1]雖然會浪費幾個字節,所以如果你的編譯器支持[],你可能想要使用它。

相關問題