在stdin上使用strtok时遇到问题

时间:2016-11-11 22:51:46

标签: c

我从std读取异常困难的时间,拆分空格,然后将这些标记放入数组中。 (忽略80字节的分配,这只是暂时的测试)

char* inputLine = (char*)malloc(80);
char* commands[80];
char* input;
int i;
fgets(inputLine, 80, stdin);
input = strtok(inputLine, " \n");
for (i=0; input != NULL; i++) {
    memcpy(commands[i], input, sizeof(input));
    input = strtok(NULL, " \n");
}

输入

command1 command2 command3

输出应为

commands[0] = "command1" 
commands[1] = "command2" 
commands[2] = "command3"

然而,我得到的输出是

commands[0] = "command1"
commands[1] = ""
commands[2] = "command3"

当单步执行调试器时,我可以看到命令[0]和命令[1]被正确填充。但是,当最后一个memcpy被执行时,它会分配命令[2]并删除命令[1]。

我对C的经历非常有限,我感谢有人指出我的愚蠢错误!

3 个答案:

答案 0 :(得分:0)

您似乎没有分配内存来存储命令。您只分配了一个指针数组,因此您的行为实际上是未定义的。修复是分配内存并初始化它。

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

int main(){
    char* inputLine = (char*)malloc(80);
    char* commands[80];
    char* input;
    int i;
    int numCommands = 0;
    fgets(inputLine, 80, stdin);
    input = strtok(inputLine, " \n");
    for (i=0; input != NULL; i++) {
        numCommands++;
        commands[i] = malloc(strlen(input) + 1);
        strncpy(commands[i], input, strlen(input));
        input = strtok(NULL, " \n");
    }

    for (i = 0; i < numCommands; i++) {
        puts(commands[i]);
    }
}

答案 1 :(得分:0)

所以这是另一个解决stdin并将其放入由空格分割的标记库的解决方案:

int main(void)
{
        char *line = NULL, *buf = NULL;
        size_t len = 0;
        size_t bufpos = 0, buflen = 0;
        ssize_t bytes = 0;
        char *p;
        char **command = NULL;
        size_t count = 0;
        int k;

        while((bytes = getline(&line, &len, stdin)) != -1)  {
                fprintf(stderr, "buf:%p, bufpos:%lu, buflen: %lu\n", buf, bufpos, buflen);
                buflen += bytes;
                buf = realloc(buf, buflen);
                strncpy(buf+bufpos, line, bytes);
                *(buf+bufpos+bytes-1) = ' '; /* change \n at the end to a space */
                bufpos = buflen;
        }

        free(line);

        if(buflen == 0) {
                exit(EXIT_FAILURE);
        }

        if(buf[buflen-1] == ' ')
                buf[buflen-1] = 0;

        command = malloc(sizeof(char *)); /* at least one command is there */

        /* mark all the commands */
        command[0] = buf;
        count = 1;
        for(p = buf; *p != 0; p++) {
                int delimit = 0;

                /* eatup consecutive whitespaces */
                while(isspace(*p)) {
                        delimit = 1;
                        *p = 0;
                        p++;
                        if(!*p || !isspace(*p))
                                break;
                }

                if(delimit) {
                        command = realloc(command, (1+count)*sizeof(char *));
                        command[count] = p;

                        count++;
                }
        }

        for (k = 0; k < count; k++) {
                fprintf(stdout, "%d : %s\n", k, command[k]);
        }

        free(command);
        free(buf);

        return 0;
}

有几点需要注意:

  1. 这使用getline,这是一种非常安全的动态读取字符串的方式。

  2. command数组是一个指针数组,指向buf数组的各个点,其中包含所有读入的内容。这意味着您不会在整个地方重新分配字符串。您现在可以将buf传递给strtok,或者执行我在分配和分配命令的for循环中所做的操作。

  3. 这可以更好地控制如何划分命令

  4. 您可以将其变为重新输入功能。

答案 2 :(得分:0)

您不需要分配如此多的内存。只是做

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

int main()
{
    char line[1024];

    while(fgets(line, sizeof line, stdin)) {
        char *command[80];
        int n_command = 0, i;
        char *p;
        for (p = strtok(line, " \n"); p && (n_command < 80); p = strtok(NULL, " \n"))
            command[n_command++] = p;
        for (i = 0; i < n_command; i++)
            printf("[%s]", command[i]);
        puts(""); /* final end of line to stdout */
    }
    return EXIT_SUCCESS;
} /* main */

因为您可以使用静态大小(足够大的大行)和最大数量的command条目。请注意,当达到最多80个条目时,将丢弃命令条目,如果它们更长,则每行包含1024个字符。此示例仅说明在strtok(3)循环中使用for

$ pru_$$ | sed 's/^/    /'
Lorem ipsum dolor sit amet sed. Arcu ac quam. Posuere malesuada in. Morbi et feugiat libero mauris pellentesque. Vulputate a vel et cupidatat mi mi.

Praesent eget vestibulum mauris imperdiet et dolor aliquam dui. Ultrices donec nascetur. Ante neque hymenaeos. Quis sed placerat at ac et. Purus elementum vestibulum nunc.

Et id eu tellus at libero enim odio et. Nec enim maecenas ac pellentesque magna placerat culpa ut. Dolor at malesuada justo est ut tellus sapien nunc. Diam saepe fermentum. Vivamus dolor adipiscing vitae wisi vivamus. Diam consequat lobortis. Integer habitasse feugiat. Non in minima. Lacus odio odio urna nunc.
[Lorem] [ipsum] [dolor] [sit] [amet] [sed.] [Arcu] [ac] [quam.] [Posuere] [malesuada] [in.] [Morbi] [et] [feugiat] [libero] [mauris] [pellentesque.] [Vulputate] [a] [vel] [et] [cupidatat] [mi] [mi.]

[Praesent] [eget] [vestibulum] [mauris] [imperdiet] [et] [dolor] [aliquam] [dui.] [Ultrices] [donec] [nascetur.] [Ante] [neque] [hymenaeos.] [Quis] [sed] [placerat] [at] [ac] [et.] [Purus] [elementum] [vestibulum] [nunc.]

[Et] [id] [eu] [tellus] [at] [libero] [enim] [odio] [et.] [Nec] [enim] [maecenas] [ac] [pellentesque] [magna] [placerat] [culpa] [ut.] [Dolor] [at] [malesuada] [justo] [est] [ut] [tellus] [sapien] [nunc.] [Diam] [saepe] [fermentum.] [Vivamus] [dolor] [adipiscing] [vitae] [wisi] [vivamus.] [Diam] [consequat] [lobortis.] [Integer] [habitasse] [feugiat.] [Non] [in] [minima.] [Lacus] [odio] [odio] [urna] [nunc.]