как читать текстовые файлы - PullRequest
0 голосов
/ 02 июня 2018

Я пытаюсь извлечь ключевые значения из текстового файла, но мне не удается определить, как найти конец значения.Вот короткий фрагмент текстового файла.

GIRRAFE: A tall spotted animal
LION: A short carnivore.
Prince: The son of a king.
Princess: The daughter of a king.

Это мой код:

FILE *fp;
char line[20], word[20];
int i = 0, endind;

  fp = fopen(file, "r");
  if (fp==NULL){
    printf("Error parsing the file\n");
    exit(1);
  }
while (!feof(fp)){
  fgets(line, 100, fp);
      for (i;i<strlen(line);i++){
        if (line[i]=='.'){
          endind = i;
        }
      }
      for (i;i<endind;i++){
        word[i] = line[i];
          printf("%s\n",word);
      }


}

Код не очень хорош, так как я не могу получить значение, заканчивающееся наполный пустой перевод строки.

Ответы [ 2 ]

0 голосов
/ 02 июня 2018

Вы на правильном пути.Простой способ определить, есть ли у вас пустая строка (в вашем случае):

fgets(line, 100, fp);
if (*line == '\n')
    // the line is empty

( примечание: if (line[0] == '\n') эквивалентно. В каждом случае вы просто проверяете,1-й символ в line равен '\n'. Индексная нотация line[x] эквивалентна нотации указателя *(line + x), а поскольку вы проверяете 1-й символ (например, x=0), нотация указателя просто *line)

Пока вы можете свободно использовать strtok или любые другие средства для определения местоположения 1-го '.', используя strchr() или просто используя указатель для итерации (обхода) буфера, пока не найдетеПервый '.', вероятно, более легкий путь.Ваш процесс синтаксического анализа должен выглядеть примерно так:

readdef = 0;  // flag telling us if we are reading word or definition
offset = 0;   // number of chars copied to definition buffer

read line {

    if (empty line (e.g. '\n')) {  // we have a full word + definition
        add definition to your list
        reset readdef flag = 0
        reset offset = 0
    }
    else if (readdef == 0) {  // line with word + 1st part of definiton
        scan forward to 1st '.'
        check number of chars will fit in word buffer
        copy to word buffer (or add to your list, etc..)
        scan forward to start of definition (skip punct & whitespace)
        get length of remainder of line (so you can save offset to append)
        overwrite \n with ' ' to append subsequent parts of definition
        strcpy to defn (this is the 1st part of definition)
        update offset with length
        set readdef flag = 1
    }
    else {  // we are reading additional lines of definition
        get length of remainder of line (so you can save offset to append)
        check number of chars will fit in definition buffer
        snprintf to defn + offset (or you can use strcat)
        update offset with length
    }
}

add final defintion to list

Ключ цикличен и обрабатывает различные состояния вашего ввода (либо пустая строка - у нас есть слово + полное определение, readdef = 0 нам нужноначать новое слово + определение, или readdef = 1 мы добавляем строки к текущему определению) Это можно представить как цикл состояния .Вы просто обрабатываете различные условия (или состояния), представленные вашим входным файлом.Примечание: вы должны добавить окончательное определение после вашего цикла чтения (у вас все еще остается последнее определение в буфере определений, когда fgets возвращает EOF)

Ниже приведена короткаяпример работы с вашим файлом данных.Он просто выводит пары слово / определение - где бы вы добавляли их в свой список.Вы можете использовать любую комбинацию strtok, strchr или обход указателя, как я делаю ниже, чтобы разобрать файл данных на слова и определения.Помните, что если вы когда-нибудь обнаружите проблему, когда вы не можете настроить strtok для своих данных - вы всегда можете навести указатель на буфер, сравнивая каждый символ по ходу и отвечая, как требуется, для анализа ваших данных.

Вы также можете использовать snprintf или strcat, чтобы сложить несколько строк определений (или просто указатель и цикл), но избегайте strncpy, особенно для больших буферов - у него есть несколько потерь производительности, так каккаждый раз он обнуляет неиспользуемое пространство.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXW  128   /* max chars in word or phrase */
#define MAXC 1024   /* max char for read buffer and definition */

int main (int argc, char **argv) {

    int readdef = 0;        /* flag for reading definition */
    size_t offset = 0,      /* offset for each part of definition */
        len = 0;            /* length of each line */
    char buf[MAXC] = "",    /* read (line) buffer */
        word[MAXW] = "",    /* buffer storing word */
        defn[MAXC] = "";    /* buffer storing definition */
    /* open filename given as 1st argument, (or read stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, MAXC, fp)) { /* read each line */

        char *p = buf;      /* pointer to parse word & 1st part of defn */

        if (*buf == '\n') {     /* empty-line, output definition */
            defn[offset-1] = 0; /* remove trailing ' ' left for append */
            printf ("defn: %s\n\n", defn);
            readdef = 0;        /* reset readdef flag - 0 */
            offset = 0;         /* reset offset - 0 */
        }
        else if (readdef == 0) {    /* line contais word + 1st part of defn */
            while (*p && *p != '.') /* find the first '.' */
                p++;
            if (p - buf + 1 > MAXW) {   /* make sure word fits in word */
                fprintf (stderr, "error: word exceeds %d chars.\n", MAXW - 1);
                return 1;
            }
            snprintf (word, p - buf + 1, "%s", buf);    /* copy to word */
            printf ("word: %s\n", word);                /* output word */
            while (ispunct (*p) || isspace (*p))   /* scan to start of defn */
                p++;
            len = strlen (p);               /* get length 1st part of defn */
            if (len && p[len - 1] == '\n')  /* chk \n, overwrite with ' ' */
                p[len - 1] = ' ';
            strcpy (defn, p);       /* copy rest of line to defn */
            offset += len;          /* update offset (no. of chars in defn) */
            readdef = 1;            /* set readdef flag - 1 */
        }
        else {                      /* line contains next part of defn */
            len = strlen (buf);                 /* get length */
            if (len && buf[len - 1] == '\n')    /* chk \n, overwite w/' ' */
                buf[len - 1] = ' ';
            if (offset + len + 1 > MAXC) {      /* make sure it fits */
                fprintf (stderr, "error: definition excees %d chars.\n",
                        MAXC - 1);
                return 1;
            }
            snprintf (defn + offset, len + 1, "%s", buf);   /* append defn */
            offset += len;  /* update offset */
        }
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    defn[offset-1] = 0;     /* remove trailing ' ' left for append */
    printf ("defn: %s\n\n", defn);      /* output final definition */

    return 0;
}

Пример входного файла

$ cat dat/definitions.txt
ACTE. A peninsula; the term was particularly applied by the ancients to
the sea-coast around Mount Athos.

ACT OF COURT. The decision of the court or judge on the verdict, or the
overruling of the court on a point of law.

TELEGRAPH, TO. To convey intelligence to a distance, through the medium
of signals.

TELESCOPIC OBJECTS. All those which are not visible to the unassisted
eye.

TELL OFF, TO. To divide a body of men into divisions and subdivisions,
preparatory to a special service.

TELL-TALE. A compass hanging face downwards from the beams in the cabin,
showing the position of the vessel's head. Also, an index in front of
the wheel to show the position of the tiller.

Пример использования / Вывод

$ /bin/read_def <dat/definitions.txt
word: ACTE
defn: A peninsula; the term was particularly applied by the ancients to the sea-coast around Mount Athos.

word: ACT OF COURT
defn: The decision of the court or judge on the verdict, or the overruling of the court on a point of law.

word: TELEGRAPH, TO
defn: To convey intelligence to a distance, through the medium of signals.

word: TELESCOPIC OBJECTS
defn: All those which are not visible to the unassisted eye.

word: TELL OFF, TO
defn: To divide a body of men into divisions and subdivisions, preparatory to a special service.

word: TELL-TALE
defn: A compass hanging face downwards from the beams in the cabin, showing the position of the vessel's head. Also, an index in front of the wheel to show the position of the tiller.

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 02 июня 2018

Исходя из данных примера, похоже, что ключ заканчивается на первой '.' в строке.Используйте strchr (3), чтобы найти его.Но похоже, что значение и весь элемент заканчиваются двумя символами новой строки.Для этого вам нужно написать код для чтения абзаца в строку.Для этого будут полезны malloc (3) и realloc (3).Если у вас есть известный максимальный размер, вы, конечно, можете использовать буфер фиксированного размера.

Разбейте проблему на части.Сначала прочитайте параграф, затем найдите, где заканчивается ключ, а затем найдите, где начинается значение.Решите, являются ли две новые строки частью значения, и является ли точка частью ключа.

Чтобы прочитать абзац, прочитайте строку.Если строка пуста, которую вы можете определить с помощью strcmp(line, "\n"), тогда вы закончили чтение значения и можете двигаться дальше.В противном случае добавьте строку в буфер абзацев.

Как только вы получите целый абзац в виде одной строки, найдите конец ключа с помощью char *keyend = strchr(para, '.'), который вернет указатель на '.'персонаж.Вы можете заменить этот символ нулем (*keyend = 0), и теперь para - это строка с ключом.Затем передвиньте ключевой указатель на первый непробельный символ.Есть несколько способов сделать это.На этом этапе keyend теперь будет указывать на значение.Что дает вам para в качестве указателя на ключ, а keyend в качестве указателя на значение.Имея это, вы можете обновить вашу хеш-таблицу.

Я также проверил бы ошибки на этом пути и, вероятно, использовал бы отдельные переменные, лучше названные для абзаца, ключа и значения.Обрезка завершающего символа новой строки и других данных не является обязательной.Например, что если абзац вообще не содержит '.' символа?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...