[英]How can I read any file into a linked list?
I'm supposed to create a program that can read any file into a linked list.我应该创建一个可以将任何文件读入链接列表的程序。 This is what I came up with so far:
这是我到目前为止想出的:
#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;
}
When comparing the contents of the linked list with the contents of the input file this comparison succeeds when using a .txt file but fails when using a file with binary data.将链接列表的内容与输入文件的内容进行比较时,这种比较在使用 .txt 文件时会成功,但在使用带有二进制数据的文件时会失败。 This suggests that my program changes the contents of the binary file.
这表明我的程序更改了二进制文件的内容。
What am I doing wrong?我究竟做错了什么?
Random binary data can contain characters that are not printable.随机二进制数据可以包含不可打印的字符。 Or might contain zeroes, which is the string terminator and thus terminate your strings early.
或者可能包含零,这是字符串终止符,因此提前终止您的字符串。 Just don't read and write raw binary data as strings or using string functions, it will simply not work as you expect.
只是不要将原始二进制数据作为字符串读取和写入或使用字符串函数,它不会像您期望的那样工作。
If you want to read and write arbitrary data of any kind, use eg fread
and fwrite
instead, and open your files in binary mode.如果您想读取和写入任何类型的任意数据,请改用
fread
和fwrite
,并以二进制模式打开您的文件。
Since you are using Linux, you can use POSIX.1 getline()
to read lines, including lines with embedded NUL bytes;由于您使用的是 Linux,您可以使用 POSIX.1
getline()
来读取行,包括带有嵌入 NUL 字节的行; you do need to write those lines using fwrite()
.您确实需要使用
fwrite()
编写这些行。
For the linked list, you should include a length field for fwrite()
.对于链表,您应该为
fwrite()
包含一个长度字段。 I'd also make the linked list data element a flexible array member:我还将使链表数据元素成为灵活的数组成员:
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;
}
When reading lines, it is easiest to prepend the lines to the list:阅读行时,最容易将行添加到列表中:
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);
When done, you reverse the list, to get the first read line at the beginning of the 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;
}
To write each line to a stream, you use for example fwrite(node->data, node->size, 1, stdout)
.要将每一行写入流,例如使用
fwrite(node->data, node->size, 1, stdout)
。
If the output stream is not a local file, but a pipe or socket, fwrite()
can return a short count.如果输出流不是本地文件,而是管道或套接字,则
fwrite()
可以返回一个短计数。 It is not an error;这不是错误; it only means that only part of the data could be written.
这仅意味着只能写入部分数据。 To cater for those cases, you can use two helper functions: one to ensure all of the data is written, even when writing to a pipe, and another to scan through the list, using the first one to output each line:
为了满足这些情况,您可以使用两个辅助函数:一个确保所有数据都被写入,即使在写入管道时,另一个用于扫描列表,使用第一个输出每一行:
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 */
}
Instead of getline()
, you can read chunks of some predefined size using fread()
:您可以使用
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);
}
The function returns the reversed list (ie, with first line first in list).该函数返回反向列表(即,第一行在列表中)。 After calling the function, you should check using
ferror(in)
whether the entire input stream was read, or if there was an error.调用该函数后,您应该使用
ferror(in)
检查是否读取了整个输入流,或者是否存在错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.