Как хранить элементы массива строк в структуре в C? - PullRequest
0 голосов
/ 10 октября 2018

Итак, у меня есть строка: a = 2.b 1.d; milk cheese

Я разделяю ее, удаляя все специальные символы (знак равенства, точка, точка с запятой), и это делается с помощью функции strtok в C. IВозьмите каждый элемент и сохраните его в массив строк, например:

arr[0]="a"
arr[1]="2"
arr[2]="b"
arr[3]="1"
arr[4]="d"
arr[5]="milk"
arr[6]="cheese"

Теперь я хочу взять эти значения и поместить их в структуру.Вот моя структура:

struct stopPoints {
    int  weights[10];
    char connectingPoints[10];
    char *items;
    int startBool;
};

Я объявил структуру и назвал ее myPoint.Теперь я хочу сохранить каждый из разделенных элементов сверху в моей структуре.Например, я хочу сохранить «2» и «1» в myPoint.weights[0] и myPoint.weights[1].Я хочу сохранить «a» и «b» в myPoint.connectingPoints[0] и myPoint.connectingPoints[1].

Я подошел к этому, пытаясь различить буквы и цифры.Я перебираю массив "arr" и проверяю, есть ли у каждого индекса буква или цифра.Это делается с помощью значений ASCII (я знаю, что есть лучший способ сделать это в соответствии с ответами на мой предыдущий пост).Но когда я пытаюсь распечатать первый весовой элемент в моей структуре, я получаю случайное значение.Как я могу это исправить?Мой код ниже:

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

struct stopPoints {
    int  weights[10];
    char connectingPoints[10];
    char *items;
    int startBool;
};

int main ()
{
    struct stopPoints myPoint;
  char *arr[30];
  char str[] ="a = 2.b 1.d; milk cheese";
  char * pch;
  pch = strtok (str," ;=,.-");
  arr[0] = pch;
  int i=0;
  while (pch != NULL)
  {
    //printf ("%s\n",pch);
    pch = strtok (NULL, " ;=,.-");
    arr[i+1] = pch;
    printf("%s\n", arr[i]);
    i++;
  }
    int sizeofstring = sizeof(str)/sizeof(str[0]);

    int x,y=0;
     for (x=0; x<sizeofstring; x++){
          if (arr[y+1] >= 97 && arr[y+1] <= 122){
                myPoint.connectingPoints[x] = arr[y+1];
                y++;
          }
          else if (arr[y+1] >= 48 && arr[y+1] <= 57){
                myPoint.weights[x] = arr[y+1];
                y++;
          }
     }

    printf("%d\n", myPoint.weights[1]);

  return 0;
}

Ответы [ 3 ]

0 голосов
/ 10 октября 2018

Во-первых, очень хорошая работа над деталями, минимально полным и поддающимся проверке примером и форматированием вашего вопроса.Далее, очевидно, что вы совершенно растеряны ...

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

Для начала, не используйте магические числа в своем коде.Это для удобства чтения и обслуживания по мере роста вашего кода.10, 30, 97, 122, 48 and 57 все магические числа .

#define CPWT 10     /* if you need a constant, #define one (or more) */
#define NPTR 30     /*   (do not use "magic numbers" in your code)   */

Не используйте магические числа для символов.Хотя вы должны использовать макросы, предоставленные в ctype.h для islower() и isdigit(), если вы собираетесь использовать символы, тогда используйте символы , например, if (foo >= 'a' && foo <= 'z'), а не 97 и 122,Просто заключите в кавычки символ.

Далее вы перезаписываете каждый указатель в arr каждый раз, когда назначаете arr[i+1] = pch; Почему?

    char *arr[NPTR];                /* array of 30 UNINITIALIZED pointers */

Хотя strtok возвращает указатель,когда вы присваиваете arr[i+1] = pch;, вы назначаете одинаковый указатель каждому указателю в arr.Когда вы закончите, каждый элемент в arr будет содержать последнее значение, возвращаемое strtok (и поскольку вы присваиваете после , последний вызов strtok возвращает NULL - вы, скорее всего, SegFault)

Кроме того, перед тем, как что-либо «хранить» по адресу от arr[0] до arr[NPTR-1], вы должны выделить хранилище в зависимости от длины сохраняемой строки.(и да, даже если вы сохраняете односимвольный во многих элементах, вы сохраняете строки - и каждая строка требует завершающий ноль символ ).

Вы не можете назначать строки в C (кроме назначения строковых литералов или во время инициализации массива).В противном случае вы должны скопировать строк в C.

Так как вы начинаете с неинициализированных, нераспределенных указателей в arr, вы должны выделить а затем copy для хранения информации по каждому адресу указателя.У вас есть два способа сделать это: (1) либо malloc (length + 1) символов, проверить выделение, а затем strcpy (или более эффективно memcpy, так как вы уже отсканировали, чтобы найти nul-символ с strlen()) или (2) если у вас есть strdup(), он будет выделять и копировать, как вы это делали в (1), за один вызов функции.( примечание: , поскольку strdup выделяет, вы все равно должны проверить выделение выполнено успешно)

Пример (1)

    if ((pch = strtok (str," ;=,.-")) != NULL) {    /* validate each call */
        size_t len = strlen (pch);                  /* get length */
        if ((arr[0] = malloc (len + 1)) == NULL) {  /* allocate/validate */
            perror ("arr[0]-malloc");
            exit (EXIT_FAILURE);
        }
        memcpy (arr[0], pch, len+1);    /* copy! string to arr[0] */
        i++;
    }

Пример 2(используя strdup для выделения / копирования)

    /* only loop if good return from strtok */
    while ((pch  = strtok (NULL, " ;=,.-")) != NULL)
    {
        /* allocate/copy all at once with strdup (if you have it) */
        if ((arr[i] = strdup (pch)) == NULL) {
            perror ("arr[n]-strdup");
            exit (EXIT_FAILURE);
        }
        i++;
    }

Поскольку вы только что заполнили i элементов arr, вам не нужно:

    int sizeofstring = sizeof(str)/sizeof(str[0]);

Вы знаетеу вас есть i строки в arr - используйте i, а не sizeofstring и проходите по каждой строке, которую вы сохранили в arr, а не каждый символ в str.Это побеждает всю цель наличия токенизированных str.Кроме того, вы хотите учитывать только один символ или одну цифру строки в arr (не "milk" и "cheese") при установке connectingPoints и weights, поэтомупроверьте, является ли второй символ nul-символом , в противном случае пропустите элемент arr.

Вы не можете использовать y для connectingPoints и weights,у вас есть a, b, d connectingPoints (3 из них) и только 2 weights.Вы попытаетесь получить доступ за пределами допустимых данных в weights, если вы выполняли итерацию с j = 0; j < y; ...

Опять же, вы не можете назначить указатель как символ, поэтому самый простой способ назначить 1-й символ в строкекак символ, это просто разыменование указатель, например *arr[x] (что эквивалентно символу arr[x][0]).Имея это в виду, вы можете сделать:

    /* i contains the number of strings in arr - use it */
    for (x = 0; x < i; x++) {
        if (arr[x][1] == 0) {   /* only consider single char strings in arr */
            if (cpts < CPWT && islower(*arr[x])) { /* check bound/lowercase */
                myPoint.connectingPoints[cpts] = *arr[x];  /* assign char */
                cpts++;
            }
            else if (weights < CPWT && isdigit(*arr[x])) { /* same w/digits */
                myPoint.weights[weights] = *arr[x];
                weights++;
            }
        }
    }

( примечание: раздельное использование счетчиков cpts и weights вместо одного y)

Собрав все части воедино, вы можете сделать неловкий подход с помощью чего-то вроде следующего:

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

#define CPWT 10     /* if you need a constant, #define one (or more) */
#define NPTR 30     /*   (do not use "magic numbers" in your code)   */

struct stopPoints {
    int  weights[CPWT];
    char connectingPoints[CPWT];
    char *items;
    int startBool;
};

int main (void)
{
    struct stopPoints myPoint = { .weights = {0} };
    char *arr[NPTR];                /* array of 30 UNINITIALIZED pointers */
    char str[] ="a = 2.b 1.d; milk cheese";
    char *pch;
    int i = 0, x, cpts = 0, weights = 0;

    if ((pch = strtok (str," ;=,.-")) != NULL) {    /* validate each call */
        size_t len = strlen (pch);                  /* get length */
        if ((arr[0] = malloc (len + 1)) == NULL) {  /* allocate/validate */
            perror ("arr[0]-malloc");
            exit (EXIT_FAILURE);
        }
        memcpy (arr[0], pch, len+1);    /* copy! string to arr[0] */
        i++;
    }

    /* only loop if good return from strtok */
    while ((pch  = strtok (NULL, " ;=,.-")) != NULL)
    {
        /* allocate/copy all at once with strdup (if you have it) */
        if ((arr[i] = strdup (pch)) == NULL) {
            perror ("arr[n]-strdup");
            exit (EXIT_FAILURE);
        }
        i++;
    }

    /* i contains the number of strings in arr - use it */
    for (x = 0; x < i; x++) {
        if (arr[x][1] == 0) {   /* only consider single char strings in arr */
            if (cpts < CPWT && islower(*arr[x])) { /* check bound/lowercase */
                myPoint.connectingPoints[cpts] = *arr[x];  /* assign char */
                cpts++;
            }
            else if (weights < CPWT && isdigit(*arr[x])) { /* same w/digits */
                myPoint.weights[weights] = *arr[x];
                weights++;
            }
        }
    }

    puts ("arr contents:");
    for (x = 0; x < i; x++)
        printf (" arr[%2d]: %s\n", x, arr[x]);

    puts ("\nconnectingPoints:");
    for (x = 0; x < cpts; x++)
        printf (" myPoint.connectionPoints[%2d]: %c\n", 
                x, myPoint.connectingPoints[x]);

    puts ("\nweights:");
    for (x = 0; x < weights; x++)
        printf (" myPoint.weights[%2d]: %c\n", 
                x, myPoint.weights[x]);

    return 0;
}

( примечание: использование islower() и isdigit() изctype.h)

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

$ ./bin/strtokarrptrs
arr contents:
 arr[ 0]: a
 arr[ 1]: 2
 arr[ 2]: b
 arr[ 3]: 1
 arr[ 4]: d
 arr[ 5]: milk
 arr[ 6]: cheese

connectingPoints:
 myPoint.connectionPoints[ 0]: a
 myPoint.connectionPoints[ 1]: b
 myPoint.connectionPoints[ 2]: d

weights:
 myPoint.weights[ 0]: 2
 myPoint.weights[ 1]: 1

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

0 голосов
/ 10 октября 2018

Вы не инициализируете свою структуру в любой точке, и вы чередуетесь, присваивая значения myPoint.connectionPoints [x] и myPoint.weights [x], но затем пропускаете место в другом массиве, поэтому вы получаете случайные значения

      if (arr[y+1] >= 97 && arr[y+1] <= 122){
            myPoint.connectingPoints[x] = arr[y+1];  // myPoint.weights[x] left unassigned 
            y++;
      }
      else if (arr[y+1] >= 48 && arr[y+1] <= 57){
            myPoint.weights[x] = arr[y+1]; // myPoint.connectingPoints[x] left unassigned
            y++;
      }

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

Например

    int index = 0;
    for (char* pch = strtok(str, " ;=,.-"); pch != NULL; pch = strtok(NULL, " ;=,.-")
    {
      switch (index++)
      {
      case 0:
      case 1:
      case 2:
      ...
      default:
        break;
      }
    }

РЕДАКТИРОВАТЬ: на самом деле я не уверен, что вам вообще нужен переключатель, просто присвойте ему счетчик для каждого массива и приращение.

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

Когда вы проверяете значение символа, используйте для этого одну из стандартных функций времени выполнения c isdigit () isalpha ()/ islower () вместо прямой проверки значения ASCII.

0 голосов
/ 10 октября 2018

Я не совсем понимаю, что вы пытаетесь сделать, но arr - это массив указателей, myPoint.connectingPoints - это массив char, а myPoint.weights - это массив int.Так что myPoint.connectingPoints[x] = arr[y+1] и myPoint.weights[x] = arr[y+1] сделают указатель на char или int преобразование, что, как правило, плохая идея.

Может быть, вы хотели сделать

myPoint.connectingPoints[x] = arr[y+1][0];

и

myPoint.weights[x] = arr[y+1][0];

То же самое касается сравнений с arr[y+1].97, 122, 48 и 57 не являются жестко закодированными адресами памяти, не так ли?

Это полностью игнорирует тот факт, что у вас переполнение буфера в myPoint.connectingPoints и myPoint.weights.Подсказка: sizeofstring больше 10.

Пожалуйста, всегда компилируйте с -Wall.Флаг существует по причине

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