Ошибка сегментации при использовании strtok и fgets - PullRequest
0 голосов
/ 29 сентября 2019

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

Например, в терминале вы могли бы написать что-то вроде этого:

./main cat nap dog

Тогда будет сказано, что вы ищете 3 слова, и вы вводите слова, после чего следует период, когда вы хотите, чтобы он заканчивался:

Looking for words:
cat
cat
nap
.

Затем программа возвращает:

Result:
cat: 2
nap: 1
dog: 0

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

У меня есть следующая функция:

int process_stream(WordCountEntry entries[], int entry_count)
{
  short line_count = 0;
  char buffer[30];

  while (fgets(buffer, 30, stdin)) {
    buffer[strlen(buffer)-1]='\0';
    char* token = strtok(buffer, " \n");
    while (token != NULL){
      token = strtok(NULL, " \n"); 
      int i = 0;
      while (i < entry_count+1) {
        if (!strcmp(entries[i].word, token))
          entries[i].counter++;
        i++;
      }
      line_count++;
    }
  }
  return line_count;
}

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

Ответы [ 3 ]

2 голосов
/ 29 сентября 2019

Есть много способов, как это неправильно:

  1. i < entry_count+1: Предположим, entry_count равно 1;начиная с 1 <2, вы будете проверять неинициализированный и, возможно, выход за пределы <code>entries[1].Я думаю, что вы хотели написать < entry_count.
  2. buffer[strlen(buffer)-1]= - поэтому, если strlen равен 0, вы пишете в buffer[-1] - тут есть еще один segfault.Я также не знаю, какова может быть цель этой строки.
  3. Вы проверяете, чтобы token не был нулевым, прежде чем снова установить его на strtok().Почти наверняка во второй раз это будет ноль, и вы будете делать strcmp против ноля.Там есть еще один segfault.Вам нужно переместить цикл на следующий strtok к концу цикла.
  4. fgets в любом случае останавливается на символах новой строки, поэтому использование strtok в любом случае не требуется.
0 голосов
/ 29 сентября 2019

Ваша функция обрабатывает токены не по порядку.Вы пропускаете первую token, полностью перезаписывая указатель token, прежде чем он будет использован с:

char* token = strtok(buffer, " \n");
while (token != NULL){
  token = strtok(NULL, " \n"); 

Следующая следующая строка является лишней, если ваш разделитель равен " \n".'\n', который вы пытаетесь перезаписать с помощью следующего кода, никогда не являясь частью token:

buffer[strlen(buffer)-1]='\0';

Вы используете '.', чтобы отметить конец ввода, поэтому вам не нужнообработать последнюю строку.Чтобы избежать этой проблемы, вы разрываете вложенный цикл с помощью простого оператора goto, например:

    while (fgets (buffer, MAXC, stdin)) {
        ...
        while (token != NULL) {
            if (!strcmp (token, "."))               /* compare != '.' */
                goto done;
            ...
    }
    done:;

    return line_count;
}

Предполагая, что entry_count - это количество элементов в entries, тогда ваш +1 заставляет васчитать за пределами entries, что может привести к SegFault.Хотя невозможно проверить, предоставив полный код, который отсутствует в вашем вопросе, похоже, что вы намеревались:

        while (i < entry_count) {               /* +1 causeses UB */

Ваш звонок на token = strtok(NULL, " \n"); для получения следующего токена должен быть последним утверждением в вашем вопросе.цикл, а не первый, иначе вы пропустили токен.

Если сложить его целиком, похоже, вам нужно что-то похожее на:

#define MAXC 128    /* max number of characters per word/line */
...
int process_stream (WordCountEntry entries[], int entry_count)
{
    short line_count = 0;
    char buffer[MAXC];
    const char *delim = " \n";                      /* set delim once */

    while (fgets (buffer, MAXC, stdin)) {
        char *token = strtok(buffer, delim);
        while (token != NULL) {
            if (!strcmp (token, "."))               /* compare != '.' */
                goto done;
            int i = 0;
            while (i < entry_count) {               /* +1 causeses UB */
                if (!strcmp(entries[i].word, token))
                    entries[i].counter++;
                i++;
            }
            line_count++;
            token = strtok(NULL, delim);            /* now get next token */
        }
    }
    done:;

    return line_count;
}

Short Example

В соответствии с вашими комментариями вы хотели иметь возможность обрабатывать отдельные слова или несколько слов в каждой строке.Функция выше делает это, но у вас могут быть другие проблемы в коде, который вы не опубликовали.Чтобы проверить вышеописанную функцию, была написана короткая реализация.До тех пор, пока ваш код обеспечивает аналогичный ввод, приведенная выше функция будет удовлетворять ваши потребности.Был использован короткий пример:

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

#define NENT  16    /* max number of entries (no. elements in array) */
#define MAXC 128    /* max number of characters per word / line */

typedef struct {
    char word[MAXC];
    int counter;
} WordCountEntry;

int process_stream (WordCountEntry entries[], int entry_count)
{
    short line_count = 0;
    char buffer[MAXC];
    const char *delim = " \n";                      /* set delim once */

    while (fgets (buffer, MAXC, stdin)) {
        char *token = strtok(buffer, delim);
        while (token != NULL) {
            if (!strcmp (token, "."))               /* compare != '.' */
                goto done;
            int i = 0;
            while (i < entry_count) {               /* +1 causeses UB */
                if (!strcmp(entries[i].word, token))
                    entries[i].counter++;
                i++;
            }
            line_count++;
            token = strtok(NULL, delim);            /* now get next token */
        }
    }
    done:;

    return line_count;
}

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

    WordCountEntry wce[NENT] = { { .word = "" } };
    int n = 0;

    if (argc < 2) {
        fputs ("error: insufficient arguments.\n", stderr);
        return 1;
    }

    for (int i = 1; i < (argc < NENT ? argc : NENT); i++) {
        for (int j = 0; j < i; j++) {
            if (!strcmp (wce[j].word, argv[i]))
                goto next;
        }
        strcpy (wce[i-1].word, argv[i]);
        n++;
        next:;
    }

    puts ("Looking for words:");

    if (!process_stream (wce, n))
        fputs ("(user canceled input)\n", stderr);

    puts ("\nResult:");
    for (int i = 0; i < n; i++)
        printf ("%s: %d\n", wce[i].word, wce[i].counter);
}

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

$ ./bin/wordcountentry cat nap dog
Looking for words:
cat
cat
nap
.

Result:
cat: 2
nap: 1
dog: 0

или с несколькими словами в строке:

$ ./bin/wordcountentry cat nap dog
Looking for words:
cat dog dog
cat dog
nap .

Result:
cat: 2
nap: 1
dog: 3

Могут быть другие проблемы в вашем коде, и невозможно быть уверенным, что это исправит все проблемы из-за отсутствия Минимального, Полного и Проверяемого примера (MCVE) , но с учетом того, что у вас былоэто должно решить проблемы, которые можно идентифицировать, хотя могут быть и другие.

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

0 голосов
/ 29 сентября 2019

Скорее всего, проблема в том, что цикл разбит входной строки на токен.'Strtok' вызывается дважды для входных данных, только с одной проверкой NULL.

Если второй вызов strtok вернет NULL, код потерпит неудачу (сбой) в strcmp.Рассмотрите возможность изменения тела цикла:

  while (fgets(buffer, 30, stdin)) {
    buffer[strlen(buffer)-1]='\0';
    char* token = strtok(buffer, " \n");
    while (token != NULL){
      int i = 0;
      while (i < entry_count+1) {
        if (!strcmp(entries[i].word, token))
          entries[i].counter++;
        i++;
      }
      line_count++;
      // Place strtok here
      token = strtok(NULL, " \n");   }
    }

и обратите внимание, что опубликованный код исправит проблему, которая приведет к игнорированию первого токена в каждой строке.

...