Поскольку вы используете 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)
, был ли прочитан весь входной поток или произошла ошибка.