Чтение, которое вы пытаетесь получить из файла, является нетривиальным, но его можно обработать довольно просто, установив флаг, сообщающий вам, видели ли вы уже 'a'
или 'b'
, пропуская все пробелы и ':'
символы,сохраняя все остальные символы в вашем буфере, перераспределяя при необходимости, а затем, когда будет найден второй 'a'
или 'b'
, помещая этот символ обратно в поток FILE*
с помощью ungetc
, завершая nul и возвращая ваш буфер.
Звучит достаточно просто - верно?Ну, вот и все.Давайте посмотрим, что потребуется в вашей функции readword()
.
Во-первых, поскольку вы выделяете buffer
в readword()
, нет необходимости передавать char **buffer
в качестве параметра.Вы уже объявили readword
как char *readword(...)
, поэтому просто передайте указатель FILE*
в качестве параметра и верните указатель на свой выделенный, заполненный и оконченный нулем буфер.
ВыВы можете обрабатывать схему перераспределения любым удобным для вас способом. Вы можете либо начать с некоторого разумного количества выделенных символов, а затем удвоить (или добавить несколько кратных) текущий размер, либо просто добавить фиксированное количество каждый раз, когда у вас заканчивается.Приведенный ниже пример просто начинается с 32-символьного буфера, а затем добавляет еще 32-символьный каждый раз, когда необходимо перераспределение.(если бы размер данных был действительно неизвестен, я бы, вероятно, начал с 32-х символов, а затем удваивал каждый раз, когда заканчивал - полностью за вами).
Использование функции isspace()
, найденной в ctype.h
гарантирует, что все пробелы обрабатываются правильно.
Последние несколько проблем просто гарантируют, что вы вернете строку nul-terminated в буфере и убедитесь, что вы повторно инициализируете свой указатель на конец буферав каждом новом блоке памяти, когда вызывается realloc
.
В целом, вы можете сделать что-то похожее на следующее.Программа простого примера добавляется после функции readword()
для чтения файла примера и вывода комбинированных строк, считанных из файла,
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define NCHR 32
char *readword (FILE *fp)
{
int c, /* current character */
firstline = 0; /* flag for 'a' or 'b' found at 1st char */
size_t n = 0, nchr = NCHR; /* chars read, number of chars allocated */
char *buffer = NULL, *p; /* buffer to fill, pointer to buffer */
buffer = malloc (nchr); /* allocate initial NCHR */
if (!buffer) { /* validate */
perror ("malloc-buffer");
return NULL;
}
p = buffer; /* set pointer to buffer */
while ((c = fgetc (fp)) != EOF) { /* read each char */
if (isspace (c) || c == ':') /* skip all whitespace and ':' */
continue;
if (c == 'a' || c == 'b') { /* begins with 'a' or 'b' */
if (firstline) { /* already had a/b line */
ungetc (c, fp); /* put the char back */
*p = 0; /* nul-terminate */
return buffer; /* return filled buffer */
}
firstline = 1; /* set firstline flag */
continue;
}
else {
if (n == nchr - 2) { /* check if realloc needed */
void *tmp = realloc (buffer, nchr + NCHR);
if (!tmp) /* validate */
exit (EXIT_FAILURE);
buffer = tmp; /* assign new block to buffer */
p = buffer + n; /* set p at buffer end */
nchr += NCHR; /* update no. chars allocated */
}
*p++ = c; /* assign the current char and advance p */
n++; /* increment your character count */
}
}
*p = 0; /* nul-terminate */
return buffer;
}
int main (int argc, char **argv) {
char buf[NCHR], *word;
int nwords, toggle = 0;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!fgets (buf, NCHR, fp)) {
fputs ("error: read of line 1 failed.\n", stderr);
return 1;
}
if (sscanf (buf, "%d", &nwords) != 1) {
fputs ("error: invalid file format.\n", stderr);
return 1;
}
nwords *= 2; /* actual number of words is twice the number of pairs */
while (nwords-- && (word = readword (fp))) {
printf ("%c: %s\n", toggle ? 'b' : 'a', word);
free (word);
if (toggle) {
putchar ('\n');
toggle = 0;
}
else
toggle = 1;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
( note: выше toggle
это просто флаг 1
или 0
, используемый для вывода "a:"
или "b:"
в начале соответствующей строки и добавления '\n'
между прочитанными парами строк.)
Пример использования / вывода
$ ./bin/read_multiline_pairs dat/pairsbinline.txt
a: 010101000001010111110100101010000000111100000000000011110000
b: 0000011111000001000110101010100111110001
a: 0000001111111111110000111111111111000
b: 00000001111001010101
Проверка использования памяти / ошибок
Всегда проверяйте использование памяти при динамическом выделении памяти и убедитесь, что вы освободиливсю память, которую вы выделяете.
$ valgrind ./bin/read_multiline_pairs dat/pairsbinline.txt
==14257== Memcheck, a memory error detector
==14257== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14257== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==14257== Command: ./bin/read_multiline_pairs dat/pairsbinline.txt
==14257==
a: 010101000001010111110100101010000000111100000000000011110000
b: 0000011111000001000110101010100111110001
a: 0000001111111111110000111111111111000
b: 00000001111001010101
==14257==
==14257== HEAP SUMMARY:
==14257== in use at exit: 0 bytes in 0 blocks
==14257== total heap usage: 8 allocs, 8 frees, 872 bytes allocated
==14257==
==14257== All heap blocks were freed -- no leaks are possible
==14257==
==14257== For counts of detected and suppressed errors, rerun with: -v
==14257== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Посмотрите вещи и дайте мне знать, если у вас есть вопросы.Самая большая часть проблемы заключалась в обработке чтения и объединения всех строк для каждой пары.Остальная часть кодирования остается за вами.