【问题标题】:How to assign subsequent columns in csv to array using strtok in C如何使用C中的strtok将csv中的后续列分配给数组
【发布时间】:2019-06-19 08:29:58
【问题描述】:

我正在尝试编写一个简单的程序,允许用户在指定他们想要提取的行和列之后从 csv 文件中提取数据。该程序接受起始行的输入(可以是任何数字,不一定是 1)和所需的最后一行。相同的原则适用于列。然后将输出分配给一个多维数组,该数组稍后将传递给函数进行计算。

根据我的理解,我在使用 strtok 提取不是第一个列时遇到问题,strtok 必须按顺序读取文件?

我一直在使用这个项目来自学 C,因此对这个原理进行了多次迭代来发展我的知识。我已经成功读取了csv文件,所以我了解了strtok在csv文件中的基本原理。

如果我使用第一列来读取,那么我可以像以前一样使用 strtok;

var[i][0] = atof(strtok(buf, ","));
var[i][j] = atof(strtok(NULL, ","));

但是从第 2 列开始读取,仍然必须使用上面的代码,因为 strtok 是顺序的,但不要将第 0 列分配给变量。我工作的唯一粗略方法是引入一个临时变量,将所有列放入其中,然后提取使用指定的列,但这是不可取的。

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

void CSV_Col_read(int r, int c, int start_row, int start_col, float var[r][c])
{

char buf[1024];
float temp[r][c];

  FILE *fp = fopen("PV_Data.csv", "r");

  if(!fp)
  {
    printf("Could Not Open File\n");
  }
  int i = 0;
  int index_row = 0;

  while(fgets(buf, sizeof buf, fp))
  {  
      index_row++;
      if (index_row >= start_row){
      if(i >= r - start_row + 1){
          break;
      }

      if (c == 1){
      var[i][0] = atof(strtok(buf, ","));
  }
      else if(start_col >= 2){
          temp[i][0] = atof(strtok(buf, ","));

      for (int j = 1; j <= c-1; j++)
      {       
      temp[i][j] = atof(strtok(NULL, ","));
      }

      for (int I = -1; I <= 2; I++){

      var[i][I+1] = temp[i][start_col + I];
      }
      }
      else{

              var[i][0] = atof(strtok(buf, ","));

      for (int j = 1; j <= c-1; j++)
      {       
      var[i][j] = atof(strtok(NULL, ","));
      }
  }
  i++;
}
  }
        fclose(fp);
}

void printData(int r, int c, int start_row, int start_col, float var[r][c])
{
if ( c == 1){
for (int i = 1; i <= (r - start_row); i++)
    {
    printf("%f\n", var[i][0]);
    }
}
else{
    for(int i = 0; i <= (r - start_row); i++)
    {
        printf("%f", var[i][0]);


        for(int j = 1; j <= (c - start_col); j++)
        {
            printf("\t");
            printf("%f", var[i][j]);   
    }
    printf("\n");
    }
}
}
int main()
{
    int start_row = 705;
    int start_col = 3;
    int r = 720;
    int c = 5;
    float var[r][c];

    (void) CSV_Col_read(r, c, start_row, start_col, var);

    printData(r, c, start_row, start_col, var);
}

上面的代码不是“为白痴证明插入正确的编程词”,但是据我所知,我只为一种情况指定了 elseif() 语句。这种临时变量的方法是可行的方法还是有更清洁的方法来解决这个问题?我不是要求代码来解决此问题,并且对将我引导至学习资源的答案感到非常满意。

谢谢

【问题讨论】:

  • 一个通常循环直到strtok返回一个NULL指针。但是请注意strtok 的缺点,例如它无法按照您在 CSV 文件中想要的方式处理连续的分隔符。还有许多其他问题和重要的特殊情况和极端情况使 CSV 文件比看起来更难解析。因此,我总是建议你找到一个可以为你做这件事的图书馆。
  • 我将研究循环方面。我已经看到 strtok 对于不仅仅是列中简单数字的数据可能会很尴尬。如果我这样做的目的不是自学 C,那么我认为你是对的,为什么其他人花了数周时间进行开发,但是,我发现任何需要解决的问题只能帮助我学习!

标签: c csv strtok


【解决方案1】:

对于我知道输入文件格式正确且没有“空”值的简单事情,那么我可能会做这样的事情:

float values[rows][columns] = { 0 };  // Initialize all to zero

char line[512];  // Hopefully large enough

unsigned r;  // Row index

// Loop to get all rows, taking care to not go out of bounds
for (r = 0; r < rows && fgets(line, sizeof line, fp) != NULL; ++r)
{
    unsigned c = 0;  // Column index

    // Get the first column value
    char *current = strtok(line, ",");

    // Loop to get all columns
    while (c < columns && current != NULL)
    {
        values[r][c] = atof(current);

        ++c;  // Increase to next column
        current = strtok(NULL, ",");  // Get next (if any) column value
    }
}

// Here the whole file will have been read and parsed, and the values
// from the file will be in the array `values`.

【讨论】:

  • 如果我遗漏了什么,请原谅我,因为我缺乏 C 知识,但我看不到输入或执行所需行和列的值的位置。我将在可以有 1000-100,000 行和超过 360 列的文件上实施这个原则。因此,我正在研究如何仅选择第 5 列和第 6 列以减少读取时间、执行计算并清除这些值,然后再进行下一部分计算。这可能吗?感谢您的回答
  • @RossHanna 您仍然必须阅读整行,这当然意味着阅读整个文件。当然,您可以阅读一行的一小部分,看看想要的列是否在该行中,如果没有阅读更多,但这很快就会变得非常复杂,而且一点也不简单。问题是,不能保证记录(行)中的所有元素都具有相同的大小,具有相同数量的字符。这当然也意味着没有两条线的大小相同。您不能只在第一行中找到位置并假设每一行都相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-02
  • 1970-01-01
  • 2019-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
相关资源
最近更新 更多