C分割输入字符串 - 分割错误

时间:2017-09-10 07:22:56

标签: c segmentation-fault

我的代码可以这样工作:

input : a[]="create  /dir/bar"

并保存在此字符串中:

b[]=create
c[]=/dir/bar

还有一种情况,我保存另一个字符串:(例如)

a[]=write /foo/bar  "test"

b[]= write
c[]=/foo/bar
d[]=test   (without the "")

我的代码是:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SPACE ' '

void divide(char a[], char b[], char c[], char d[]);

int main(int argc, char const *argv[]) {
    char a[50+1];
    char b[50+1];
    char c[50+1];
    char d[50+1];
    int i;

    scanf("%s\n", a);

    divide(a, b, c, d);

    for(i=0; b[i]!='\0'; i++)
        printf("%s %s %s \n", b, c, d);

    return 0;
}



void divide(char a[], char b[], char c[], char d[]){
    int i, j;

    for(i=0; a[i]!=SPACE; i++)
        b[i]=a[i];

    b[i]='\0';

    for(; a[i]==SPACE; i++)
        ;

    for(j=0; a[i]!='\0'; i++, j++)
        c[j]=a[i];

    c[j]='\0';
    for(; a[i]==SPACE; i++)
        ;

    if(a[i]=='"'){
        i++;
        for(j=0; a[i]!='"'; i++)
            d[j]=a[i];

        d[j]='\0';

        return;
    }
}

但是在程序获得输入后它不适用于分段错误。问题出在哪里?

我不能使用malloc,因为它花费了太多时间来工作(我必须获得数千条线路)并且不尊重限制。 (我在我大学的一个项目工作)

2 个答案:

答案 0 :(得分:0)

如果你运行这个简单的程序

#include<stdio.h>

int main(int argc, char const *argv[]) {
    char a[50+1];

    scanf("%s\n", a);
    printf("|%s|\n", a);

    return 0;
}

并输入“create foo”,您将获得输出

  

|创建|

正如你所看到的,你只得到了第一个词,即“创造”,而不是预期的“创造foo”

scanf("%s\n", a);

只会给出第一个字。因此,divide函数将失败。而不是scanf你可以做

fgets(a, 51, stdin);

确保将整个输入读入数组a

通常,您的程序缺少大量的范围检查和输入验证。你应该添加它。

我看到的另一个问题是,如果输入是

create  /dir/bar

你永远不会初始化字符串d,但你仍然在main中打印它。这是未定义的行为。

尝试:

char d[50+1];
d[0] = '\0';    // Add this line

答案 1 :(得分:0)

你可能会让它变得比它需要的更困难。是的,您可以通过重复调用sscanf或使用scanf重复读取来标记字符串,但C库提供了一个工具来标记文本行中的单词。足够聪明地命名为strtok

你只需要声明一个常量字符串,其中包含你希望打破单词的分隔符(例如delims = " \t";以打破空格或制表符上的单词,然后调用strtok (str, delims)返回第一个标记(单词) ),然后循环重复调用strtok (NULL, delims)来解析剩余的单词(或直到你达到最多3个单词)。

注意第一次调用strtok使用str作为第一个参数,而所有后续调用都使用NULL

这是处理字符串中未知数量的令牌的一种更灵活的方法。

而不是使用a[]b[]c[]等。考虑只使用一个buf[]来读取输入行,然后是一个数组用于保存参数的字符串(允许您在strtok循环期间使用索引变量来将正确的字符串分配并复制到关联的索引中。)

在这种情况下,请勿使用void作为回报。为什么不使用有意义的返回(比如文本行中的参数数量)。这样,您就知道在您的除法函数中读取(或标记化)了多少。给它一个可以提供有用信息的回报,例如

size_t divide (char *buf, char (*params)[MAXC+1]);

现在将返回size_t类型,其中包含每次分割调用产生的参数数量。

完全放置(并使用fgets读取整行输入),您可以执行以下操作:

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

enum { MAXP = 3, MAXC = 50 };   /* max parameters & chars */

size_t divide (char *buf, char (*params)[MAXC+1]);

int main (void) {

    char buf[MAXC * 4 + 1] = "";
    char params[MAXP][MAXC + 1];    /* array to hold 3 parameters */
    size_t i, len, nparams = 0;

    /* use fgets for line-oriented user input */
    printf ("\nenter commands: ");
    if (!fgets (buf, sizeof buf, stdin)) {
        fprintf (stderr, "error: insufficient input.\n");
        return 1;
    }

    len = strlen (buf);         /* get length */
    if (buf[len - 1] == '\n')   /* validate last char is '\n' */
        buf[--len] = 0;         /* overwrite with nul-terminating char */
    else {                      /* short read -- handle error */
        fprintf (stderr, "error: incomplete input read.\n");
        return 1;
    }

    nparams = divide (buf, params);

    for (i = 0; i < nparams; i++)
        printf ("parameter[%zu] : %s\n", i, params[i]);

    return 0;
}

/* divide using strtok */
size_t divide (char *buf, char (*params)[MAXC+1])
{
    char *delims = " \t",   /* delimiters for strtok */
        *p = buf;           /* pointer to buf */
    size_t n = 0;           /* var to return number of params */

    p = strtok (buf, delims);           /* tokenize fist paramter */

    while (p) { /* now loop until all words exhausted or limit reached */
        strncpy (params[n++], p, MAXC); /* copy token to params array */
        if (n == MAXP)                  /* check if limit reached */
            break;
        p = strtok (NULL, delims);      /* get next token */
    }

    return n;   /* return the number of parameters found */
}

示例使用/输出

$ /bin/splitparams

enter commands: create  /dir/bar
parameter[0] : create
parameter[1] : /dir/bar

$ ./bin/splitparams

enter commands: write /foo/bar  "test"
parameter[0] : write
parameter[1] : /foo/bar
parameter[2] : "test"

或提供一堆额外的单词(以验证仅处理3个)

$ ./bin/splitparams

enter commands: write /foo/bar  "test" and some more stuff
parameter[0] : write
parameter[1] : /foo/bar
parameter[2] : "test"