用于CSV文件打印的strtok()(null)以及所需的值

问题描述 投票:-1回答:3

我正在尝试用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

哪个是正确的。

c strtok
3个回答
2
投票

   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);所以你最终将至少保存在令牌中记忆的字符串的地址,这是错误的,你需要保存字符串内的字符:

    char* token = strtok(singleLine, ",");
    while (token != NULL) {
        token = strtok(NULL, ",");
        printf("%s\n", token);
        vc_vector_push_back(prices, 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);
 }

你认为价格包含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(...)的值


0
投票

当我打印出strtok()的结果时,我得到了我正在寻找的数字,但我也得到(null)打印出来的数字:

是的,因为你循环直到你这样做。考虑:

        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

此外,虽然我在这里,快速的问题,是free(token);在某些时候需要?或者不,因为malloc()从未被召唤过?

不,因为,正如你所说,没有分配内存。但仔细考虑其含义。没有分配内存,因为token指向你正在标记的本地数组singleLine。这意味着:

  1. 当您将下一行读入同一缓冲区时,您将替换指向的数据。
  2. 当函数返回时,该数组的生命周期结束,使得它的任何指针(in)无效。

似乎vc_vector复制元素,但在你的情况下,它只能复制指针本身,而不是指向的值,所以这对上述任何一个都没有帮助。相反,为了避免破坏您的数据并最终拥有一个充满悬空指针的向量,您必须制作动态分配的令牌字符串副本,并将指针存储到向量中的指针。

如果你有,那么非标准但常见的strdup()功能可以为你制作这样的副本。否则,strlen()malloc()strcpy()的组合将做同样的工作。请注意,即使在使用strdup()时没有显式调用分配函数,但在成功时,生成的重复字符串确实是动态分配的,需要在不再需要时释放它。

此外,当我尝试打印实际的矢量条目时,它们只是打印出垃圾

好吧那是因为你在向量中存储了指向字符数组的指针,但后来试图将它们解释为int的指针。指针格式可能是兼容的,但它们指向的数据完全不兼容。并且类型int甚至不是合适的类型,因为您的数据不是整数(除非您可以并且确实转换为定点表示)。也许,你想要使用,并允许向量复制,而不是复制字符串,doubles:

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);
}

-1
投票

token != NULL条件下检查while后,你执行另一个token = strtok(NULL, ",");。这给出了 - 最后一个令牌到达 - 获得NULL值的100%“机会”,您随后打印出来:

while (token != NULL) {
        token = strtok(NULL, ",");
        printf("%s\n", token);

strtok放在循环的最后一行:

while (token != NULL) {
        printf("%s\n", token);
        vc_vector_push_back(prices, &token);
        token = strtok(NULL, ",");

此外,您存储指向局部变量的指针,当超出范围时,不能再访问它。您需要复制值:

while (token != NULL) {
        printf("%s\n", token);
        token = strdup(token);
        vc_vector_push_back(prices, &token);
        token = strtok(NULL, ",");
© www.soinside.com 2019 - 2024. All rights reserved.