First, a primer on how strtok()
works. The function will give you back a pointer to somewhere in the original string, said string having been modified to make it look like you only have a single token (a).
例如,第一个strtok
of "A,B,C"
会把它变成"A\0B,C"
并给您返回的地址A
特点。那时使用它会给你"A"
.
同样,第二个调用会将其变成"A\0B\0C"
并给您返回的地址B
特点。
事实上,它为您提供了指向原始字符串的指针,这一点在这里至关重要,因为原始字符串位于buff
.
而且,你其实是覆盖 buff
每次从文件中读取一行时。所以,对于所有这五行,Game[i].ProductID
只是第一个字符的地址buff
。处理完第五行后,该行:
while (fgets(buff, 1024, fp) != NULL && i < 5)
首先会读到第六行before退出循环。
这就是为什么您看到的最后几行实际上是not与前五个中的任何一个相同。您正在打印所有 C 字符串ProductID
,在(相同的)地址buff
,所以你只看到第六个,然后你看到full行,因为您在读入该行后没有标记该行。
你需要做的是做一个copy覆盖该行之前的标记。这可以通过类似的方法来完成(这有点复杂,但可以正确处理以下情况strtok
返回 NULL):
if ((Game[i].ProductID = strtok(buff, ",")) != NULL)
Game[i].ProductID = strdup(Game[i].ProductID);
记住你应该free
这些内存分配在某个时刻。
In the 难以置信万一您的环境没有strdup
(它是 POSIX 而不是 ISO),请参阅here https://stackoverflow.com/questions/252782/strdup-what-does-it-do-in-c/252802#252802.
而且,顺便说一句,大多数 CSV 实现都允许嵌入逗号,例如将它们括在引号中或转义它们(后者很少见,但我have见过他们):
name,"diablo, pax",awesome
name,diablo\, pax,awesome
预计这两个领域都将是三个领域,name
, diablo, pax
and awesome
.
简化处理strtok
不会允许这样的复杂性,但是,假设您的字段不包含嵌入的逗号,那么可能没问题。如果你的输入is更复杂的是,您最好使用第三方 CSV 库(当然需要有合适的许可证)。
(a) For the language lawyers among us, this is covered in the ISO C standard, C11 7.24.5.8 The strtok function, /3 and /4
(my bold):
3/ 序列中的第一个调用搜索由s1
对于第一个字符not包含在当前分隔符指向的字符串中s2
。如果没有找到这样的字符,则 所指向的字符串中没有标记s1
和strtok
函数返回一个空指针。如果找到这样的角色,这是第一个令牌的开始.
4/ The strtok
然后函数从那里搜索一个字符is包含在当前分隔符字符串中。如果没有找到这样的字符,则当前标记延伸到指向的字符串的末尾s1
,后续搜索令牌将返回空指针。如果找到这样的角色,它被空字符覆盖,从而终止当前标记。strtok 函数保存指向下一个字符的指针,下一次搜索标记将从该字符开始。