![](/img/trans.png)
[英]How can I create a (C) function to read data from a file into a linked-list using 'read'?
[英]How can I read any file into a linked list?
我應該創建一個可以將任何文件讀入鏈接列表的程序。 這是我到目前為止想出的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BUFFER_SIZE 1024
typedef struct list {
char *string;
struct list *next;
} LIST;
void print_list(LIST *head) {
LIST *current = head;
while (current != NULL) {
printf("%s", current->string);
current = current->next;
}
}
void push(LIST **head, FILE **fp) {
char line[MAX_BUFFER_SIZE];
LIST *node, *current = *head;
while(fgets(line, sizeof(line), *fp)) {
node = malloc(sizeof(LIST));
if (node == NULL) exit(1);
node->string = strdup(line);
node->next = NULL;
if(current == NULL) {
*head = node;
current = node;
} else {
current->next = node;
current = current->next;
}
}
}
int main(int argc, char *argv[]) {
FILE *fp = fopen(argv[1], "r");
LIST *head = NULL;
push(&head, &fp);
fclose(fp);
print_list(head);
return 0;
}
將鏈接列表的內容與輸入文件的內容進行比較時,這種比較在使用 .txt 文件時會成功,但在使用帶有二進制數據的文件時會失敗。 這表明我的程序更改了二進制文件的內容。
我究竟做錯了什么?
由於您使用的是 Linux,您可以使用 POSIX.1 getline()
來讀取行,包括帶有嵌入 NUL 字節的行; 您確實需要使用fwrite()
編寫這些行。
對於鏈表,您應該為fwrite()
包含一個長度字段。 我還將使鏈表數據元素成為靈活的數組成員:
struct node {
struct node *next;
size_t size;
char data[];
/* Note: data[size+1], data[size] == '\0'.
This is not necessary for correct operation,
but allows one to assume there is always at
least one char in data, and the data is followed
by a nul byte. It makes further use of this
structure easier. */
};
struct node *node_new(const char *data, size_t size)
{
struct node *n;
n = malloc(sizeof (struct node) + size + 1);
if (!n) {
fprintf(stderr, "node_new(): Out of memory.\n");
exit(EXIT_FAILURE);
}
n->next = NULL;
n->size = size;
if (size > 0)
memcpy(n->data, data, size);
n->data[size] = '\0';
return n;
}
閱讀行時,最容易將行添加到列表中:
struct node *list = NULL;
struct node *curr;
char *line = NULL;
size_t size = 0;
ssize_t len;
while (1) {
len = getline(&line, &size, stdin);
if (len < 0)
break;
curr = node_new(line, (size_t)len);
curr->next = list;
list = curr;
}
list = list_reverse(list);
完成后,您反轉列表,以獲取列表開頭的第一個讀取行:
struct node *list_reverse(struct node *curr)
{
struct node *root = NULL;
struct node *next;
while (curr) {
next = curr->next;
curr->next = root;
root = curr;
curr = next;
}
return root;
}
要將每一行寫入流,例如使用fwrite(node->data, node->size, 1, stdout)
。
如果輸出流不是本地文件,而是管道或套接字,則fwrite()
可以返回一個短計數。 這不是錯誤; 這僅意味着只能寫入部分數據。 為了滿足這些情況,您可以使用兩個輔助函數:一個確保所有數據都被寫入,即使在寫入管道時,另一個用於掃描列表,使用第一個輸出每一行:
static int fwriteall(const char *data, size_t size, FILE *out)
{
size_t n;
while (size > 0) {
n = fwrite(data, 1, size, out);
if (n > 0) {
data += n;
size -= n;
} else
return -1; /* Error */
}
return 0; /* Success */
}
int list_writeall(FILE *out, struct node *list)
{
for (; list != NULL; list = list->next)
if (list->size > 0)
if (fwriteall(list->data, list->size, out)
return -1; /* Error */
return 0; /* Success */
}
您可以使用fread()
讀取某些預定義大小的塊,而不是getline()
fread()
:
struct node *read_all(FILE *in, const size_t size)
{
struct node *list = NULL;
struct node *curr;
size_t used;
while (1) {
curr = malloc(sizeof (struct node) + size + 1);
if (!curr) {
fprintf(stderr, "read_all(): Out of memory.\n");
exit(EXIT_FAILURE);
}
size = fread(curr->data, 1, size, in);
if (used > 0) {
/* Optional: Optimize memory use. */
if (used != size) {
void *temp;
temp = realloc(curr, sizeof (struct node) + used + 1);
/* Reallocation failure is not fatal. */
if (temp) {
curr = temp;
curr->size = used;
}
}
}
curr->data[used] = '\0';
curr->next = list;
list = curr;
}
return list_reverse(list);
}
該函數返回反向列表(即,第一行在列表中)。 調用該函數后,您應該使用ferror(in)
檢查是否讀取了整個輸入流,或者是否存在錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.