strtok() 用于 CSV 文件打印(空)以及所需的值
strtok() for CSV file printing (null) along with desired value
我正在尝试用 C 读取 CSV 文件并将该数据存储到 vector。
我的每一行 CSV 文件条目如下所示:12/12/1914, 52.4
,但是,我只对从此 CSV 中检索数字感兴趣,而不是日期。
为此,我一直在尝试使用 fgets()
逐行读取文件,然后通过使用 strtok()
将数字值分开。
当我打印出 strtok()
的结果时,我得到了我正在寻找的数字,但我也得到了 (null)
打印出来的结果:
(null)
25798.42
(null)
25706.68
(null)
25379.45
(null)
25444.34
(null)
25317.41
此外,当我尝试打印实际的矢量整体时,它们只是打印出垃圾(我认为这是因为 (null)
附加到它们但不是正数):
3907216808; 0;
3907216808; 0;
我读取数据的函数如下所示:
void get_CSV_data(vc_vector* prices)
{
FILE *fp = fopen(_FILE_PATH, "r");
char singleLine[20];
while(!feof(fp)){
fgets(singleLine, 20, fp);
char* token = strtok(singleLine, ",");
while (token != NULL) {
token = strtok(NULL, ",");
printf("%s\n", token);
vc_vector_push_back(prices, &token);
}
}
// Print each vector element
for (void* i = vc_vector_begin(prices);
i != vc_vector_end(prices);
i = vc_vector_next(prices, i)) {
printf("%u; ", *(int*)i);
}
}
我假设我使用 strtok()
不正确,有人可以指教吗?
另外,当我在这里时,快速提问,在某些时候需要 free(token);
吗?或者没有,因为 malloc()
从未被调用过?对 C 还是很陌生。
编辑:我的函数现在看起来像:
void get_CSV_data(vc_vector* prices)
{
FILE *fp = fopen(_FILE_PATH, "r");
char singleLine[20];
while(fgets(singleLine, 20, fp) != NULL){
char* token = strtok(singleLine, ",");
token = strtok(NULL, ",");
//printf("%s\n", token);
vc_vector_push_back(prices, strdup(token));
}
// Print each vector element
for (void* i = vc_vector_begin(prices);
i != vc_vector_end(prices);
i = vc_vector_next(prices, i)) {
printf("%s\n ", (char*)i);
}
}
我得到的结果如下:
25598.7425052.8325339.9925250.5525798.4225706.6825379.4525444.3425317.4125191.43 25052.8325339.9925250.5525798.4225706.6825379.4525444.3425317.4125191.43
25339.9925250.5525798.4225706.6825379.4525444.3425317.4125191.43
25250.5525798.4225706.6825379.4525444.3425317.4125191.43
25798.4225706.6825379.4525444.3425317.4125191.43
25706.6825379.4525444.3425317.4125191.43
25379.4525444.3425317.4125191.43
哪些是正确的。
在
char* token = strtok(singleLine, ",");
while (token != NULL) {
token = strtok(NULL, ",");
printf("%s\n", token);
vc_vector_push_back(prices, &token);
}
vc_vector_push_back
允许保存具有给定大小的数据,而不是可变大小,因此只有当您创建了指示要放入的字符数的向量时才能使用它
你的情况是vc_vector_push_back(prices, &token);
所以你最终至少会保存token中记忆的字符串的地址,这是错误的,你需要保存字符串中的字符:
char* token = strtok(singleLine, ",");
while (token != NULL) {
token = strtok(NULL, ",");
printf("%s\n", token);
vc_vector_push_back(prices, token);
}
复制 token 是没有用的(正如我最初想象的那样),因为 vc_vector_push_back
将根据您在创建向量时指定的大小进行复制
请注意,您还丢失了第一个令牌,您最终将推送 NULL,可能是您想要的
char* token = strtok(singleLine, ",");
while (token != NULL) {
printf("%s\n", token);
vc_vector_push_back(prices, token);
token = strtok(NULL, ",");
}
在
for (void* i = vc_vector_begin(prices);
i != vc_vector_end(prices);
i = vc_vector_next(prices, i)) {
printf("%u; ", *(int*)i);
}
你假设 prices 包含 int 但这是错误的,它包含 char*
,必须是
for (void* i = vc_vector_begin(prices);
i != vc_vector_end(prices);
i = vc_vector_next(prices, i)) {
printf("%s ", *(char**)i);
}
你还需要改变
while(!feof(fp)){
fgets(singleLine, 20, fp);
类似
while (fgets(singleLine, 20, fp) != NULL) {
我也鼓励您在使用前检查 fopen(...)
的值
When I print out the results of strtok() I get the numbers I am
looking for, but I also get (null) printed with them:
是的,因为你循环直到你这样做。考虑:
while (token != NULL) {
token = strtok(NULL, ",");
printf("%s\n", token);
vc_vector_push_back(prices, &token);
}
只要初始标记不为 NULL,在每次迭代中您都会读取并打印下一个标记。只有这样,已经打印出来之后,你是否循环返回以测试它是否为空。
因为你似乎想要每行的第二个标记,所以循环是没有意义的。只需调用 strtok()
两次:
char* token = strtok(singleLine, ",\n");
if (token) {
token = strtok(NULL, ",\n");
if (token) {
printf("%s\n", token);
vc_vector_push_back(prices, &token); // but see below
} // else handle malformed data
} // else handle malformed data
Also, while I am here, quick side question, is free(token)
; needed at
some point? Or no because malloc()
was never called?
不,因为正如你所说,没有分配内存。但请仔细考虑其中的含义。没有分配内存,因为 token
指向您正在标记化的 local 数组 singleLine
。这意味着:
- 当您将下一行读入同一个缓冲区时,您将替换指向的数据。
- 当函数 returns 时,该数组的生命周期结束,使任何指向它的指针都无效。
似乎 vc_vector
复制了元素,但在你的情况下,它只能复制指针本身,而不是指向的值,所以这对上述任何一个都没有帮助.相反,为了避免破坏您的数据并最终拥有一个充满悬挂指针的向量,您必须制作动态分配的令牌字符串副本,并将指向 those 的指针存储在您的向量中。
如果您有,那么非标准但常见的 strdup()
函数可以为您制作此类副本。否则 strlen()
、malloc()
和 strcpy()
的组合将完成相同的工作。请注意,即使您在使用 strdup()
时没有显式调用分配函数,但成功时,生成的重复字符串确实是动态分配的,需要在您不再需要时释放它。
Also, when I try and print the actual vector entires, they just print out garbage
好吧 那是 因为您在向量中存储了指向字符数组的指针,但随后试图将它们解释为就好像它们是指向 int
的指针一样。指针格式可能兼容,但它们指向的数据完全不兼容。并且类型 int
甚至不是合适的类型,因为您的数据不是完整的(除非您可以并且确实转换为定点表示)。也许,您不想复制字符串,而是希望使用并允许向量复制 double
s:
double d = strtod(token, NULL); // note: as written, performs no error checking
vc_vector_push_back(prices, &d);
这可能需要更改初始化向量的方式。然后你会把它们打印成双打,比如:
for (double *dp = vc_vector_begin(prices);
dp != vc_vector_end(prices);
dp = vc_vector_next(prices, dp)) {
printf("%.2f; ", *dp);
}
我正在尝试用 C 读取 CSV 文件并将该数据存储到 vector。
我的每一行 CSV 文件条目如下所示:12/12/1914, 52.4
,但是,我只对从此 CSV 中检索数字感兴趣,而不是日期。
为此,我一直在尝试使用 fgets()
逐行读取文件,然后通过使用 strtok()
将数字值分开。
当我打印出 strtok()
的结果时,我得到了我正在寻找的数字,但我也得到了 (null)
打印出来的结果:
(null)
25798.42
(null)
25706.68
(null)
25379.45
(null)
25444.34
(null)
25317.41
此外,当我尝试打印实际的矢量整体时,它们只是打印出垃圾(我认为这是因为 (null)
附加到它们但不是正数):
3907216808; 0;
3907216808; 0;
我读取数据的函数如下所示:
void get_CSV_data(vc_vector* prices)
{
FILE *fp = fopen(_FILE_PATH, "r");
char singleLine[20];
while(!feof(fp)){
fgets(singleLine, 20, fp);
char* token = strtok(singleLine, ",");
while (token != NULL) {
token = strtok(NULL, ",");
printf("%s\n", token);
vc_vector_push_back(prices, &token);
}
}
// Print each vector element
for (void* i = vc_vector_begin(prices);
i != vc_vector_end(prices);
i = vc_vector_next(prices, i)) {
printf("%u; ", *(int*)i);
}
}
我假设我使用 strtok()
不正确,有人可以指教吗?
另外,当我在这里时,快速提问,在某些时候需要 free(token);
吗?或者没有,因为 malloc()
从未被调用过?对 C 还是很陌生。
编辑:我的函数现在看起来像:
void get_CSV_data(vc_vector* prices)
{
FILE *fp = fopen(_FILE_PATH, "r");
char singleLine[20];
while(fgets(singleLine, 20, fp) != NULL){
char* token = strtok(singleLine, ",");
token = strtok(NULL, ",");
//printf("%s\n", token);
vc_vector_push_back(prices, strdup(token));
}
// Print each vector element
for (void* i = vc_vector_begin(prices);
i != vc_vector_end(prices);
i = vc_vector_next(prices, i)) {
printf("%s\n ", (char*)i);
}
}
我得到的结果如下:
25598.7425052.8325339.9925250.5525798.4225706.6825379.4525444.3425317.4125191.43 25052.8325339.9925250.5525798.4225706.6825379.4525444.3425317.4125191.43
25339.9925250.5525798.4225706.6825379.4525444.3425317.4125191.43
25250.5525798.4225706.6825379.4525444.3425317.4125191.43
25798.4225706.6825379.4525444.3425317.4125191.43
25706.6825379.4525444.3425317.4125191.43
25379.4525444.3425317.4125191.43
哪些是正确的。
在
char* token = strtok(singleLine, ","); while (token != NULL) { token = strtok(NULL, ","); printf("%s\n", token); vc_vector_push_back(prices, &token); }
vc_vector_push_back
允许保存具有给定大小的数据,而不是可变大小,因此只有当您创建了指示要放入的字符数的向量时才能使用它
你的情况是vc_vector_push_back(prices, &token);
所以你最终至少会保存token中记忆的字符串的地址,这是错误的,你需要保存字符串中的字符:
char* token = strtok(singleLine, ",");
while (token != NULL) {
token = strtok(NULL, ",");
printf("%s\n", token);
vc_vector_push_back(prices, token);
}
复制 token 是没有用的(正如我最初想象的那样),因为 vc_vector_push_back
将根据您在创建向量时指定的大小进行复制
请注意,您还丢失了第一个令牌,您最终将推送 NULL,可能是您想要的
char* token = strtok(singleLine, ",");
while (token != NULL) {
printf("%s\n", token);
vc_vector_push_back(prices, token);
token = strtok(NULL, ",");
}
在
for (void* i = vc_vector_begin(prices); i != vc_vector_end(prices); i = vc_vector_next(prices, i)) { printf("%u; ", *(int*)i); }
你假设 prices 包含 int 但这是错误的,它包含 char*
,必须是
for (void* i = vc_vector_begin(prices);
i != vc_vector_end(prices);
i = vc_vector_next(prices, i)) {
printf("%s ", *(char**)i);
}
你还需要改变
while(!feof(fp)){
fgets(singleLine, 20, fp);
类似
while (fgets(singleLine, 20, fp) != NULL) {
我也鼓励您在使用前检查 fopen(...)
的值
When I print out the results of strtok() I get the numbers I am looking for, but I also get (null) printed with them:
是的,因为你循环直到你这样做。考虑:
while (token != NULL) { token = strtok(NULL, ","); printf("%s\n", token); vc_vector_push_back(prices, &token); }
只要初始标记不为 NULL,在每次迭代中您都会读取并打印下一个标记。只有这样,已经打印出来之后,你是否循环返回以测试它是否为空。
因为你似乎想要每行的第二个标记,所以循环是没有意义的。只需调用 strtok()
两次:
char* token = strtok(singleLine, ",\n");
if (token) {
token = strtok(NULL, ",\n");
if (token) {
printf("%s\n", token);
vc_vector_push_back(prices, &token); // but see below
} // else handle malformed data
} // else handle malformed data
Also, while I am here, quick side question, is
free(token)
; needed at some point? Or no becausemalloc()
was never called?
不,因为正如你所说,没有分配内存。但请仔细考虑其中的含义。没有分配内存,因为 token
指向您正在标记化的 local 数组 singleLine
。这意味着:
- 当您将下一行读入同一个缓冲区时,您将替换指向的数据。
- 当函数 returns 时,该数组的生命周期结束,使任何指向它的指针都无效。
似乎 vc_vector
复制了元素,但在你的情况下,它只能复制指针本身,而不是指向的值,所以这对上述任何一个都没有帮助.相反,为了避免破坏您的数据并最终拥有一个充满悬挂指针的向量,您必须制作动态分配的令牌字符串副本,并将指向 those 的指针存储在您的向量中。
如果您有,那么非标准但常见的 strdup()
函数可以为您制作此类副本。否则 strlen()
、malloc()
和 strcpy()
的组合将完成相同的工作。请注意,即使您在使用 strdup()
时没有显式调用分配函数,但成功时,生成的重复字符串确实是动态分配的,需要在您不再需要时释放它。
Also, when I try and print the actual vector entires, they just print out garbage
好吧 那是 因为您在向量中存储了指向字符数组的指针,但随后试图将它们解释为就好像它们是指向 int
的指针一样。指针格式可能兼容,但它们指向的数据完全不兼容。并且类型 int
甚至不是合适的类型,因为您的数据不是完整的(除非您可以并且确实转换为定点表示)。也许,您不想复制字符串,而是希望使用并允许向量复制 double
s:
double d = strtod(token, NULL); // note: as written, performs no error checking
vc_vector_push_back(prices, &d);
这可能需要更改初始化向量的方式。然后你会把它们打印成双打,比如:
for (double *dp = vc_vector_begin(prices);
dp != vc_vector_end(prices);
dp = vc_vector_next(prices, dp)) {
printf("%.2f; ", *dp);
}