我是C编程的新手,应该创建一个程序,该程序可以将任何文件读入链表,然后将链表的内容打印到屏幕上。到目前为止,这是我想出的:
#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;
}
要测试我的程序是否正确读取了我使用的文件内容(编译后),redirection operator'>'将输出重定向到文件,例如:
./read inputFile > outputFile
此后,我使用cmp比较“ inputFile”和“ outputFile”。
当我对.txt文件执行此过程时,一切正常,并且两个文件都相等,但是当我使用
dd if=/dev/urandom of=inputFile count=20000003 bs=1
使用随机二进制数据创建文件,并将此文件与输出文件进行比较,两个文件不相等。
显然,我的程序更改了二进制文件的内容,如果有人指出我的代码错误,将不胜感激。
提前谢谢
答案 0 :(得分:2)
随机二进制数据可以包含不可打印的字符。或者可能包含零,这是字符串终止符,因此可以尽早终止字符串。只是不要将原始二进制数据读取或写入为字符串或使用字符串函数,否则将无法按预期工作。
答案 1 :(得分:0)
由于使用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 */
}
您可以使用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)
检查是否已读取整个输入流,或者是否有错误。