Разобрать getline в буфер делитером? - PullRequest
0 голосов
/ 18 октября 2018

У меня действительно тупой вопрос, от которого я не могу пройти.

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

Это мой текущий код

#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUFFERSIZE 256
#define PROMPT "myShell >> "
#define PROMPTSIZE sizeof(PROMPT)

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


    //execvp() to locate executable

    char *buffer;
    size_t bufferSize = BUFFERSIZE;
    size_t inputSize;

    char *tokens;
    char myargv[BUFFERSIZE];

    buffer = (char *) malloc(bufferSize * sizeof(char));
    tokens = (char *) malloc(bufferSize * sizeof(char));


    while (1) {
        printf(PROMPT);
        inputSize = (size_t) getline(&buffer, &bufferSize, stdin);
        if (inputSize == 18446744073709551615) {
            break;
        }

        int i = 0;
        tokens = strtok(buffer, " ");
        while (tokens != NULL) {
            myargv[i] = (char) tokens;
            printf("%c\n", myargv[i]);
            tokens = strtok(NULL, " ");
            i = i + 1;
        }


    }

}

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

предупреждение: приведено из указателяцелое число разного размера [-Wpointer-to-int-cast] myargv [i] = (char) токены;

Не совсем точно уверен, что я делаю неправильно.

Спасибо!

Ответы [ 2 ]

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

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

Первое, что должно звучать для вас как ALARM BELLS, - это ваше явное приведение к (char).Если вы когда-либо пытаетесь использовать преобразование, чтобы обойти предупреждения или ошибки компилятора - ОСТАНОВИТЕСЬ - вы делаете что-то не так.

Если ваша цель - предоставить до BUFFERSIZE аргументов для execvp (илии т.д.), тогда вам просто нужно объявить myargv как массив указателей на символ , например

    char *myargv[BUFFERSIZE] = {NULL};  /* array of pointers - init NULL */

Каждый из указателей, возвращаемых strtok, может использоваться какмассив аргументов для execvp, и если вы инициализируете массив для всех NULL указателей и заполните не более BUFFERSIZE - 1, вы всегда будете предоставлять массив аргументов для execvp и предоставите требуемый страж * NULL послепоследний аргумент.

Вы можете объявить свои разделители для strtok любым удобным для вас способом, но поскольку вы правильно определяете константы с помощью #define, нет никаких причин не добавлять константу для вашего strtokтакже разделители, например,

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

#define BUFFERSIZE 256
#define PROMPT "myShell >> "
#define DELIM " \n"

Если вы не используете argc или argv в своем коде, тогда правильное объявление для main():

int main (void) {

(см .: C11 Standard §5.1.2.2.1 Prзапуск программы p1 (черновик n1570) .См. Также: См. Что должно возвращать main () в C и C ++? )

Если вы только читаете строку и токенизируете строку для использования с execvp, тогда объявление и инициализация ваших переменных в пределах цикла гарантирует, что они будут правильно инициализироваться на каждой итерации, например,

    while (1) {
        size_t ndx = 0,             /* line index */
            n = 0;                  /* line alloc size (0, getline decides) */
        ssize_t nchr = 0;           /* return (chars read by getline) */
        char *line = NULL,          /* buffer to read each line */
            *myargv[BUFFERSIZE] = {NULL};  /* array of pointers - init NULL */

Объявляя ваши inputSize, мои nchr вышекак ssize_t (правильный тип возврата для POSIX getline), вы можете упростить свой тест для EOF, например,

        fputs (PROMPT, stdout);
        if ((nchr = getline (&line, &n, stdin)) == -1) {
            putchar ('\n');         /* tidy up with newline */
            break;
        }

Все, что остается, - это токенизация line и назначение указателей на myargv по правильному индексу (ndx).Вы можете использовать цикл while, но for предоставляет удобный способ токенизации с strtok, например

        for (char *p = strtok (line, DELIM); p; p = strtok (NULL, DELIM)) {
            myargv[ndx] = p;    /* points within line, duplicate as req'd */
            printf ("token: %s\n", myargv[ndx++]);
            if (ndx == BUFFERSIZE - 1)  /* preserve sentinel NULL */
                break;
        }
        /* call to execvp, etc. here */

( Примечание: , просто назначив указатель натокен на myargv[ndx], myargv[ndx] указывает на расположение строки в line. Вы должны использовать указатели, пока line остается в области. В противном случае вам нужно выделить память для каждого токена, назначить начальныйадрес для нового блока памяти - myargv[ndx] и скопируйте токен в новый блок памяти (или malloc и strcpy, или strdup, если он у вас есть))

Наконец,не забывайте, getline выделяет, поэтому не забывайте free() память, выделенную, когда вы закончите, например,

        free (line);    /* don't forget to free memory allocated by getline */
    }

Если сложить все вместе, вы можете справиться с токенизацией вашей строки с чем-то похожимto:

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

#define BUFFERSIZE 256
#define PROMPT "myShell >> "
#define DELIM " \n"

int main (void) {

    while (1) {
        size_t ndx = 0,             /* line index */
            n = 0;                  /* line alloc size (0, getline decides) */
        ssize_t nchr = 0;           /* return (chars read by getline) */
        char *line = NULL,          /* buffer to read each line */
            *myargv[BUFFERSIZE] = {NULL};  /* array of pointers - init NULL */

        fputs (PROMPT, stdout);
        if ((nchr = getline (&line, &n, stdin)) == -1) {
            putchar ('\n');         /* tidy up with newline */
            break;
        }
        for (char *p = strtok (line, DELIM); p; p = strtok (NULL, DELIM)) {
            myargv[ndx] = p;    /* points within line, duplicate as req'd */
            printf ("token: %s\n", myargv[ndx++]);
            if (ndx == BUFFERSIZE - 1)  /* preserve sentinel NULL */
                break;
        }
        /* call to execvp, etc. here */

        free (line);    /* don't forget to free memory allocated by getline */
    }

    return 0;
}

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

$ ./bin/getlinestrtok
myShell >> my dog has fleas
token: my
token: dog
token: has
token: fleas
myShell >> my cat has none
token: my
token: cat
token: has
token: none
myShell >> happy cat
token: happy
token: cat
myShell >>

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

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

из strtok

char * strtok (char * str, const char * delim);

тип возврата char *с другой стороны, в вашем назначении

myargv[i] = (char) tokens;

вы делаете typecast из char * в char Я уверен, что это не то, что вы хотите сделать

может бытьчто-то в этой строке

изменить myargv для хранения массива токенов

char myargv[MAX_TOKENS][BUFFERSIZE];

и в цикле while вместо присваивания myargv[i] = (char) tokens использовать strcpy

        strcpy(myargv[i], tokens);
        printf("%s\n", myargv[i]);

позвольте мне попытаться объяснить, почему ваша оригинальная программа работает неправильно

char myargv[BUFFERSIZE];

здесь myargv назначается память BUFFERSIZE vitz 256, как в

+---+---+---+---+---+---+---+---+....---+---+
|   |   |   |   |   |   |   |   |   |   |   | 
+---+---+---+---+---+---+---+---+---+---+---+
  0   1   2   ..                          255

каждый блок имеет размер sizeof(char) или 1 byte

в while цикле здесь

myargv[i] = (char) tokens;

вы получили char *, который по сути равен 4 byte, если выдействительно зайдите по этому адресу и посмотрите, что там byte by byte вы должны были увидеть первый токен.однако теперь вы пытаетесь поместить это 4 byte address в 1 byte indexed location, что приводит к усечению и присваиванию.

затем прибывает printf

printf("%c\n", myargv[i]);

теперь на основе того, что произошло впредыдущий шаг, myargv[i], теперь содержит урезанную версию адреса, которая является просто числовым "%c\n" спецификатором формата, пытается преобразовать его в соответствующий ascii и вывести на печать мусор.

I 'Предлагаю прочитать кое-что по 2d-arrays, array of strings, char **

...