【问题标题】:Parse getline into buffer by delimeter?通过分隔符将getline解析为缓冲区?
【发布时间】:2019-03-22 20:10:55
【问题描述】:

我有一个非常愚蠢的问题,我无法回答。

目标是接收用户给出的字符串,并用空格将其分割,然后将其放入数组中。

这是我目前的代码

#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) 标记;

不太确定我做错了什么。

谢谢!

【问题讨论】:

  • 也许你想要一个char *myargv[BUFFERSIZE];
  • 我怀疑你早在inputSize == 18446744073709551615 之前就会耗尽你的记忆。注意 getline 返回类型 ssize_t 而不是 size_t 用于此目的。
  • @Swordfish 刚刚给了我一个核心转储。
  • @DavidC.Rankin 这只是为了查看是否按下了 Ctrl+D
  • 当然,简单而正确的测试是让inputSize输入ssize_t,然后与-1进行比较。

标签: c getline


【解决方案1】:

来自strtok

char *strtok(c​​har *str, const char *delim);

另一方面,在您的分配中,返回类型是 char *

myargv[i] = (char) tokens;

你正在做一个 typecastchar *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 并打印给出垃圾。

我建议在2d-arraysarray of stringschar ** 上阅读一些内容

【讨论】:

  • 这很好,但是当我拿走(char) 时,我只会得到随机字符打印回来。当我添加 [MAX_TOKENS] 时它起作用了,虽然我不太确定为什么
  • 不仅仅是MAX_TOKENS,你也应该把%c改成%s
【解决方案2】:

虽然不是 100% 清楚你想要用你的代码完成什么,但你对多个指针的使用有点尴尬。

应该为您敲响警钟的第一件事是您需要明确地转换为(char)。如果你发现自己试图通过强制转换来绕过编译器警告或错误 -- STOP -- 你做错了。

如果您的目标是为execvp(或类似的)提供最多BUFFERSIZE 参数,那么您只需将myargv 声明为指向char 的指针数组,例如

    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"

如果您没有在代码中使用argcargv,那么main() 的正确声明是:

int main (void) {

(参见:C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570)。另请参见:See What should main() return in C and 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 并在适当的索引 (ndx) 处分配指向 myargv 的指针。您可以使用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 中字符串的位置。@987654360 时必须使用指针@ 仍在作用域内。否则,您需要为每个令牌分配内存,将新内存块的起始地址分配给myargv[ndx],并将令牌复制到新内存块。(mallocstrcpy ,或者strdup,如果你有的话))

最后,不要忘记,getline 分配的,所以不要忘记 free() 当你完成分配的内存,例如

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

总而言之,您可以使用类似于以下内容的方式对您的行进行标记:

#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 >>

检查一下,如果您还有其他问题,请告诉我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    • 2017-10-08
    • 2010-09-22
    • 2013-01-02
    • 1970-01-01
    • 2014-04-28
    相关资源
    最近更新 更多