分词字符串使用strtok并保存它

huangapple go评论57阅读模式
英文:

tokenizing a string with strtok and saving it

问题

我正在尝试编写一个程序,该程序从每行输入中接收货币的名称和其价值。当运行程序并输入第一行时,名称和价值将正确输入到货币结构中,但当输入第二行时,上一个货币的名称被更改并变得不正确!

input like this:

2
jibcoin 2
bitcoin 5

output: 
just save name and value in union
英文:

I am trying to write a program that receives the name of the currency and its value from the input in each line. When the program is run and we input the first line, the name and value are correctly entered into the coins structure, but when we input the second line, the name of the previous currency is changed and becomes incorrect!

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

int main() {
    int number_coin, index; 
    union coin {
        char name[20]; 
        float value; 
        float amount;
    };
    union coin coins[1000];
    scanf("%d", &number_coin);
    getchar();
    for (index = 0; index < number_coin; index++) {
        char input[40];
        fgets(input, 40, stdin);
        input[strcspn(input, "\n")] = 0; // delete \n in end of input string
        char *token = strtok(input, " ");
        while (token != NULL) {
            strcpy(coins[index].name, token);
            token = strtok(NULL, " ");
            coins[index].value = atof(token);
            token = strtok(NULL, " ");
        }
    }
    return 0;
}

input like this:

2
jibcoin 2
bitcoin 5

output:
just save name and value in union

答案1

得分: 1

这不是联合(union)的工作方式。

当你有一个联合类型的变量时,它会为联合中最大的数据类型留出空间,并且可以是联合中的任何数据类型之一。你分配了名称,这是没问题的,但当你分配浮点数时,你实际上是在同一内存区域中用浮点数值类型覆盖了相同的变量。

我认为你正在寻找的行为是结构体(struct)。

请参见下面的演示你的问题:

$ cat union.c 
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
  union coin {
    char name[20];
    float value;
  };
  union coin coins[1000];
  strcpy(coins[0].name, "name 1");
  printf("coins[0].name = %s\n", coins[0].name);
  coins[0].value = 123.456;
  printf("coins[0].value = %f\n", coins[0].value);
  printf("coins[0].name changed to -> %s\n", coins[0].name);
  printf("resetting coins[0].name . . .\n");
  strncpy(coins[0].name, "name 1", 20);
  printf("coins[0].name = %s\n", coins[0].name);
  printf("coins[0].value changed to -> %f\n", coins[0].value);
  return 0;
}
$ gcc union.c 
$ ./a.out 
coins[0].name = name 1
coins[0].value = 123.456001
coins[0].name changed to -> y��B 1
resetting coins[0].name . . .
coins[0].name = name 1
coins[0].value changed to -> 70062382309412494639104.000000

联合的这个特性也可以用来确定系统的字节序(endianness)。

英文:

This isn't how unions work.

When you have a variable of a union type, it has space for the largest datatype of the union and could be any of the datatypes within the union. You assign the name, this is fine, but when you assign the float you are overwriting the same variable with a float value type in the same memory area.

I think the behavior you are looking for is a struct.

See below for a demonstration of your problem:

$ cat union.c 
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

int main(int argc,char **argv) {
  union coin {
    char name[20];
    float value;
  };
  union coin coins[1000];
  strcpy(coins[0].name,&quot;name 1&quot;);
  printf(&quot;coins[0].name = %s\n&quot;,coins[0].name);
  coins[0].value = 123.456;
  printf(&quot;coins[0].value = %f\n&quot;,coins[0].value);
  printf(&quot;coins[0].name changed to -&gt; %s\n&quot;,coins[0].name);
  printf(&quot;resetting coins[0].name . . .\n&quot;);
  strncpy(coins[0].name,&quot;name 1&quot;,20);
  printf(&quot;coins[0].name = %s\n&quot;,coins[0].name);
  printf(&quot;coins[0].value changed to -&gt; %f\n&quot;,coins[0].value);
  return 0;
}
$ gcc union.c 
$ ./a.out 
coins[0].name = name 1
coins[0].value = 123.456001
coins[0].name changed to -&gt; y��B 1
resetting coins[0].name . . .
coins[0].name = name 1
coins[0].value changed to -&gt; 70062382309412494639104.000000

This trait of unions can also be used to determine the endianness of a system.

答案2

得分: 0

以下是翻译好的内容:

对于每一行,您想要存储namevalue,可能稍后还会使用amount的结果,所以应该使用struct而不是只能一次存储一个字段的union

还要注意,如果第一行之后的任何一行不包含两个以空格分隔的标记,您的代码将产生未定义的行为。

以下是修改后的版本:

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

int main() {
    int number_coin, index;
    struct coin {
        char name[40];
        float value;
        float amount;
    };
    struct coin coins[1000];

    scanf("%d", &number_coin);
    getchar();
    for (index = 0; index < number_coin; index++) {
        char input[40];
        fgets(input, sizeof input, stdin);
        char *token = strtok(input, " \t\n");
        if (token != NULL)
            strcpy(coins[index].name, token);
        token = strtok(NULL, " \t\n");
        if (token) {
            coins[index].value = atof(token);
            continue;
            token = strtok(NULL, " \t\n");
            if (token == NULL)
                continue;
        }
        printf("invalid line\n");
        index--;
    }
    return 0;
}

以下是使用sscanf()并具有更好错误报告的替代方法:

#include <stdio.h>

int main() {
    char input[40];
    char newline;
    int number_coin, index;
    struct coin {
        char name[20];
        float value;
        float amount;
    };
    struct coin coins[1000];

    if (!fgets(input, sizeof input, stdin)) {
        fprintf(stderr, "empty input file\n");
        return 1;
    }
    if (sscanf(input, "%d%1[\n]", &number_coin, &newline) != 2
        || number_coins < 0 || number_coins > 1000) {
        fprintf(stderr, "invalid number of coins: %s", input);
        return 1;
    }
    for (index = 0; index < number_coin; index++) {
        if (!fgets(input, sizeof input, stdin)) {
            fprintf(stderr, "missing input lines\n");
            return 1;
        }
        if (sscanf(input, "%19s%f%1[\n]", coins[index].name,
                   &coins[index].value, &newline) != 3) {
            fprintf(stderr, "invalid format: %s\n", input);
            return 1;
        }
        // 初始化 amount...
        coins[index].amount = 0;
    }
    return 0;
}
英文:

For each line, you want to store the name and the value, and probably the resulting amount later, so you should be using a struct instead of a union that can only store one of the fields at a time.

Note also that your code has undefined behavior if any of the lines after the first one does not contain two space separated tokens.

Here is a modified version:

#include &lt;math.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

int main() {
    int number_coin, index; 
    struct coin {
        char name[40]; 
        float value; 
        float amount;
    };
    struct coin coins[1000];

    scanf(&quot;%d&quot;, &amp;number_coin);
    getchar();
    for (index = 0; index &lt; number_coin; index++) {
        char input[40];
        fgets(input, sizeof input, stdin);
        char *token = strtok(input, &quot; \t\n&quot;);
        if (token != NULL)
            strcpy(coins[index].name, token);
            token = strtok(NULL, &quot; \t\n&quot;);
            if (token) {
                coins[index].value = atof(token);
                continue;
                token = strtok(NULL, &quot; \t\n&quot;);
                if (token == NULL)
                    continue;
            }
        }
        printf(&quot;invalid line\n&quot;);
        index--;
    }
    return 0;
}

Here is an alternative using sscanf() with better error reporting:

#include &lt;stdio.h&gt;

int main() {
    char input[40];
    char newline;
    int number_coin, index; 
    struct coin {
        char name[20]; 
        float value; 
        float amount;
    };
    struct coin coins[1000];

    if (!fgets(input, sizeof input, stdin)) {
        fprintf(stderr, &quot;empty input file\n&quot;);
        return 1;
    }
    if (sscanf(input, &quot;%d%1[\n]&quot;, &amp;number_coin, &amp;newline) != 2
    ||  number_coins &lt; 0 || number_coins &gt; 1000) {
        fprintf(stderr, &quot;invalid number of coins: %s&quot;, input);
        return 1;
    }
    for (index = 0; index &lt; number_coin; index++) {
        if (!fgets(input, sizeof input, stdin)) {
            fprintf(stderr, &quot;missing input lines\n&quot;);
            return 1;
        }
        if (sscanf(input, &quot;%19s%f%1[\n]&quot;, coins[index].name,
                   coins[index].value, &amp;newline) != 3) {
            fprintf(stderr, &quot;invalid format: %s\n&quot;, input);
            return 1;
        }
        // initialize amount...
        coins[index].amount = 0;
    }
    return 0;
}

答案3

得分: -1

以下是翻译好的部分:
"it get modified becouse you are using [*token=strtok()], so in this way you will modify the value/string previously stored in the memory address of "token"."
"Also I think could be better to cast coin as a struct instead of union, so in this way you create (as you already did with union), an array of 1000 structures iterable with a for loop that includes the already existing for loop."
"Another suggestion is to store the return value of [gets] in the [input] variable without using fgets."
"hope to be useful!"

英文:

it get modified becouse you are using [*token=strtok()], so in this way you will modify the value/string previously stored in the memory address of "token". Also I think could be better to cast coin as a struct instead of union, so in this way you create (as you already did with union), an array of 1000 structures iterable with a for loop that includes the already existing for loop. Another suggestion is to store the return value of [gets] in the [input] variable without using fgets. hope to be useful!

huangapple
  • 本文由 发表于 2023年5月13日 20:03:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76242630.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定