Проблема токенизации строки возникает при удалении повторяющихся слов из строки - PullRequest
0 голосов
/ 18 декабря 2018

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

Этот код работает, пока я не введу строку, содержащую два одинаковых слова.Например, строка «this this» также будет хранить второе слово, даже если оно одинаковое.Но если я вместо этого введу «this this is», он удалит второе «this» и полностью проигнорирует последнее слово строки, чтобы он не удалялся, если в строке есть дубликат.

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

#define dim 70


typedef struct string {
  char* token[25];
} string;

int main() {

  string* New = malloc(dim*sizeof(string));

  char* s;
  char* buffer = NULL;
  int i = 0, r = 0;

  s = malloc(dim * sizeof(char));

  fgets(s, dim, stdin);

  printf("The string is: %s\n", s); 

  New->token[i] = malloc(dim*sizeof(char));
  New->token[i] = strtok(s, " ");
  ++i;

  while((buffer = strtok(NULL, " ")) && buffer != NULL){

    printf("\nbuffer is: %s", buffer);

    for(r = 0; r < i; ++r) {

      if(strcmp(New->token[r], buffer) != 0 && r == i-1) {

        New->token[i] = malloc(strlen(buffer)*sizeof(char)+1);
        New->token[i] = buffer;
        ++i;

      }
      else if(New->token[r] == buffer) {
            break;
      }

    }



  }

printf("\n New string: ");
for(i = 0; New->token[i] != NULL; ++i) {
   printf(" %s", New->token[i]);
}


return 0;
}

На мой взгляд, это должно работать нормально, но мне действительно трудно найти то, что я здесь сделал неправильно.Если вам нужна дополнительная информация, просто спросите меня, я прошу прощения за возможное отсутствие ясности (и для моего английского).

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018

Завершите переписывание этого ответа, чтобы устранить некоторые принципиально неправильные вещи, которые я не видел в первый раз.См. Встроенные комментарии в коде внизу, чтобы объяснить некоторые изменения конструкции:

Я выполнил ваш код в точности как есть и увидел то, что вы описываете, и кроме примечания об использовании strcmp вдругой ответ, нашел несколько строк кода, которые можно откорректировать или удалить, чтобы заставить его делать то, что вы описали, следует:

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

typedef struct string {
  //char* token[25]; //this create a pointer to array of 25 char
  char token[25]; //this is all you need
} string;

. Как вы увидите позже, это значительно упростит распределение памяти.

некоторые основныепроблемы:

Включите символ новой строки \n в свой разделитель.Когда в конце ввода строки вводится <enter>, добавляется символ новой строки, в результате чего первый экземпляр this и второй экземпляр this\n будут неравными.

while((buffer = strtok(NULL, " \n")) && buffer != NULL){
                               ^^

Эта строка создает неинициализированную память.

string* New = malloc(dim*sizeof(string)); 

Примечание об использовании malloc () против calloc () : malloc() оставляет память, которую он создает, неинициализированной, в то время как calloc () создает блок памяти, все инициализированные для 0.

Память, созданная с помощью malloc()

enter image description here

Память, созданная с использованием calloc():

enter image description here

Это становится важным в нескольких местах вашего кода, но, в частности, я вижу проблему в последнем разделе:

for(i = 0; New->token[i] != NULL; ++i) {
   printf(" %s", New->token[i]);
}

Если память создана для Newне инициализируется, вы можете получить ошибку времени выполнения, когда индекс i увеличивается за пределы области памяти, в которую вы явно записали, и цикл пытается проверить New->token[i].Если New->token[i] содержит что-либо кроме 0, он попытается напечатать эту область памяти.

Вы также должны освободить каждый экземпляр памяти, созданный в вашем коде, с соответствующим вызовом free () .

Все это и многое другое рассматривается в следующей переписке вашего кода: (проверено это строка строка .)

typedef struct string {
  //char* token[25]; //this create a pointer to array of 25 char
  char token[25]; //this is all you need
} string;

int main() {
    char* s;
    char* buffer = NULL;
    int i = 0, r = 0;

    string* New = calloc(dim, sizeof(string));//Note: This creates an array of New.
                                              //Example: New[i]
                                              //Not: New->token[i]
    s = calloc(dim , sizeof(char));
    fgets(s, dim, stdin);
    printf("The string is: %s\n", s); 
    buffer = strtok(s, " \n");
    strcpy(New[i].token, buffer); //use strcpy instead of = for strings
    //restuctured the parsing loop to a more conventional construct
    // when using strtok:
    if(buffer)
    {
        ++i;
        while(buffer){
            printf("\nbuffer is: %s", buffer);
            for(r = 0; r < i; ++r) {
                if(strcmp(New[r].token, buffer) != 0 && r == i-1) {
                    strcpy(New[i].token, buffer);
                    ++i;
                }
                else if(strcmp(New[r].token, buffer)==0) {
                    break;
                }
            }
            buffer = strtok(NULL, " \n");
        }
    }
    printf("\n New string: ");
    for(i = 0; i<dim; i++) {
        if(New[i].token) printf(" %s", New[i].token);
    }
    free(New);
    free(s);
    return 0;
}
0 голосов
/ 18 декабря 2018

Структура кажется ненужной.
При этом используется массив указателей для хранения токенов.
Вход может быть проанализирован с помощью strspn и strcspn.
В массив добавляются уникальные токены.указатели.

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

#define DIM 70

int main() {
    char* token[DIM] = { NULL};

    char s[DIM];
    char* buffer = s;
    int unique = 0, check = 0;
    int match = 0;
    int loop = 0;
    size_t space = 0;
    size_t span = 0;

    fgets(s, DIM, stdin);

    printf("The string is: %s\n", s);

    while ( unique < DIM && *buffer){//*buffer not pointing to zero terminator
        space = strspn ( buffer, " \n\t");//leading whitespace
        buffer += space;//advance past whitespace
        span = strcspn ( buffer, " \n\t");//not whitespace
        if ( span) {
            printf("\ntoken is: %.*s", (int)span, buffer );//prints span number of characters
        }
        match = 0;
        for ( check = 0; check < unique; ++check) {
            if ( 0 == strncmp ( token[check], buffer, span)) {
                match = 1;//found match
                break;
            }
        }
        if ( ! match) {//no match
            token[unique] = malloc ( span + 1);//allocate for token
            strncpy ( token[unique], buffer, span);//copy span number of characters
            token[unique][span] = 0;//zero terminate
            ++unique;//add a unique token
        }
        buffer += span;//advance past non whitespace for next token
    }

    printf("\n New string: ");
    for( loop = 0; loop < unique; ++loop) {
        printf(" %s", token[loop]);//print the unique tokens
    }
    printf("\n");
    for( loop = 0; loop < unique; ++loop) {
        free ( token[loop]);//free memory
    }
    return 0;
}
0 голосов
/ 18 декабря 2018

Вы сравниваете указатели вместо сравнения строк.Замените

  }
  else if(New->token[r] == buffer) {
        break;

на

  }
  else if(strcmp(New->token[r], buffer) == 0) {
        break;

Вам также необходимо скопировать буфер:

memcpy(New->token[i],buffer,strlen(buffer)+1);

вместо

New->token[i] = buffer;

или заменитьобе строки (вместе с malloc) с

New->token[i] = strdup(buffer);

И лучше заменить strtok на strtok_r (strtok не возвращается).

...