Как правильно прочитать определенные строки из файла в c? - PullRequest
0 голосов
/ 03 марта 2019

Я пытаюсь написать программу на языке c, которая сравнивает строки.Строки даны парами, а в верхней части файла указано количество пар.Файл имеет вид, подобный следующему:

2
a: 01010100000101011111
   01001010100000001111
   00000000000011110000
b: 00000111110000010001
   10101010100111110001
a: 00000011111111111100
   00111111111111000
b: 00000001111001010101

Моя проблема заключается в правильном чтении строк для выполнения сравнений и т. Д.

Вот мой код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#define NCHAR 32

int main (int argc, char **argv) {
    char *word1 = NULL;
    FILE *fp = NULL;
    for (int i = 0; i<pairs; i++){

        if (i == 0)
        {
            word1 = readWord(fp, &word1);//read a:
            while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
        }

        word1 = readWord(fp, &word1);//read string
        while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);

        aline = malloc(amaxsize);
        strncpy(aline, word1, amaxsize);

        word1 = readWord(fp, &word1); 
        while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);

        while (strcmp(word1, "b:")!=0){
            aline = concat(aline, word1);

            word1 = readWord(fp, &word1); 
            while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
        }

        fprintf(fpw, "a: %s\n", aline); //write to the file..
        free (word1);
        word1 = NULL;

        word1 = readWord(fp, &word1); //read string after b:
        while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
        bline = malloc(bmaxsize);
        strncpy(bline, word1, bmaxsize);

        word1 = readWord(fp, &word1); 
        while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);

        if (i == (pairs-1))
        {

            while (strcmp(word1, "")!=0){
                bline = concat(bline, word1);
                word1 = readWord(fp, &word1);

            }
        }
        else 
        {
            while (strcmp(word1, "a:")!=0){
                bline = concat(bline, word1);
                word1 = readWord(fp, &word1);
                while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
            }
        }
        fprintf(fpw, "b: %s\n", bline); //write to the file..
        free (word1);
        word1 = NULL;

        fprintf(fpw,"\n");
}

    char *readWord(FILE *fp, char **buffer)
    {
        int ch, nchar = NCHAR;
        int buflen = 0;
        *buffer = malloc (nchar);

        if(*buffer){
            while ((ch = fgetc(fp)) != '\n' && ch != EOF && ch != '\t' && ch != ' ') 
            {
                if (ch!='\t' && ch!= ' ' && ch != '\n') (*buffer)[buflen++] = ch;

                if (buflen + 1 >= nchar) {  /* realloc */
                    char *tmp = realloc (*buffer, nchar * 2);
                    if (!tmp) {

                        (*buffer)[buflen] = 0;

                        return *buffer;
                    }
                    *buffer = tmp;
                    nchar *= 2;
                }
            }
            (*buffer)[buflen] = 0;           /* nul-terminate */

            if (buflen == 0 && ch == EOF) {  /* return NULL if nothing read */
                free (*buffer);
                *buffer = NULL;
            }
            return *buffer;
        }
        else {
            fprintf (stderr, "Error...\n");
            return NULL;
        }
    }

Функция readWord читает слово за раз.Что я пытаюсь сделать, так это прочитать файл словами и объединить их, чтобы получить полную строку a и сохранить ее в aline, чтобы я мог работать с ней.То же самое с б.Проблема в том, что файл не читается должным образом, например, вместо получения целого а первой пары, я получаю только первую его часть.Есть ли идея?

1 Ответ

0 голосов
/ 03 марта 2019

Чтение, которое вы пытаетесь получить из файла, является нетривиальным, но его можно обработать довольно просто, установив флаг, сообщающий вам, видели ли вы уже '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)

Посмотрите вещи и дайте мне знать, если у вас есть вопросы.Самая большая часть проблемы заключалась в обработке чтения и объединения всех строк для каждой пары.Остальная часть кодирования остается за вами.

...