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

时间:2019-03-06 22:46:57

标签: c strtok

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

哪个是正确的

3 个答案:

答案 0 :(得分: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);,因此最终将至少保存在 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(...)的值

答案 1 :(得分:0)

  

当我打印出strtok()的结果时,我得到的数字是   寻找,但我也得到(空)印有它们:

是的,因为您一直循环执行。考虑:

        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指向要标记的 local 数组singleLine。这意味着:

  1. 当您将下一行读入同一缓冲区时,将替换指向的数据。
  2. 函数返回时,该数组的生存期结束,从而使该数组中的所有指针均无效。

似乎vc_vector会复制元素,但是在您的情况下,它只能复制指针本身,而不是指向指针的值,因此上述任何一个都完全无济于事。相反,为了避免破坏数据并最终使向量充满悬挂的指针,您必须动态分配令牌字符串的副本,并将指向那些的指针存储在向量中。

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

  

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

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

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

答案 2 :(得分:-1)

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

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, ",");