【问题标题】:Jumping to next line with fscanf()使用 fscanf() 跳转到下一行
【发布时间】:2015-03-03 22:26:15
【问题描述】:

我有两个文件.csv,我需要读取整个文件,但它必须按字段归档。我的意思是,csv 文件是数据以逗号分隔的文件,所以我不能使用 fgets
我需要读取所有数据,但我不知道如何跳到下一行。

这是我到目前为止所做的:

int main()
{
   FILE *arq_file;
   arq_file = fopen("file.csv", "r");

   if(arq_file == NULL){
      printf("Not possible to read the file.");
      exit(0);
   }

   while( !feof(arq_file) ){
   fscanf(arq_file, "%i %lf", &myStruct[i+1].Field1, &myStruct[i+1].Field2);  
   }

   fclose(arq_file);
   return 0;
}  

它将进入无限循环,因为它永远不会获得下一行。
我怎样才能到达我刚刚读到的那一行下面的那一行?

更新:文件 01 示例

1,Alan,123,
2,Alan Harper,321
3,Jose Rendeks,32132
4,Maria da graça,822282
5,Charlie Harper,9999999999  

文件 02 示例

1,320,123
2,444,321
3,250,123,321
3,3,250,373,451
2,126,621
1,120,320
2,453,1230
3,12345,0432,1830

【问题讨论】:

  • while (!feof(file)) is always wrong。您可以发布示例输入/输出吗?顺便说一句,您应该复制并粘贴代码,因为在发布的代码中没有声明i,因此它不构成MCVE
  • 每行有多少个数据元素?元素是用逗号分隔(正如 CSV 名称所暗示的那样)还是其他分隔符?您是否必须处理本身可能包含逗号的双引号字段?您能否显示 5 行数据?
  • 顺便说一句,您至少需要"%i , %lf" 作为格式字符串;如果双精度值后面还跟一个逗号,则在%lf 之后需要另一个空格和逗号。无论如何,数字输入都会跳过空格,其中包括换行符。 (逗号后面的空格是可选的;前面的空格并不是真正的可选,但如果您确信数字之后和逗号之前永远不会有空格,它就成为可选的。)您可能会更好地获得 CSV-阅读图书馆。 The Practice of Programming 有工作代码。
  • @JonathanLeffler 我刚刚更新了文件示例。始终仅以逗号分隔。没有双引号字段。
  • @PlayHardGoPro 这看起来像是用fgets() + 解析行来完成的任务。

标签: c file csv scanf


【解决方案1】:

我认为一个例子比给你提示更好,这是fgets() + strtok() 的组合,还有其他功能可以工作,例如strchr(),虽然这样更容易,因为我只是想要为您指明正确的方向,我就是这样做的

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

int
main(void)
{
    FILE  *file;
    char   buffer[256];
    char  *pointer;
    size_t line;

    file = fopen("data.dat", "r");
    if (file == NULL)
     {
        perror("fopen()");
        return -1;
     }

    line = 0;
    while ((pointer = fgets(buffer, sizeof(buffer), file)) != NULL)
     {
        size_t field;
        char  *token;

        field = 0;
        while ((token = strtok(pointer, ",")) != NULL)
         {
            printf("line %zu, field %zu -> %s\n", line, field, token);

            field  += 1;
            pointer = NULL;
         }
        line += 1;
     }
    return 0;
}

代码的工作原理我觉得已经很清楚了,希望大家能看懂。

【讨论】:

  • 你能教我,如果我删除 pointer = NULL ,它开始以无限循环“11111”打印的原因吗?我真的无法理解一件事。我如何从“指针”获取​​真实数据。我认为,在使用指针时。指针 = 内存地址,*指针 = 该内存地址中存储的内容。我错了吗?
  • @PlayHardGoPro 因为strtok() 应该在第一次使用相同字符串的调用之后收到NULL。第二个问题,我不明白你的意思?在哪里获取数据? printf() 函数执行 *pointer 并访问指针指向的数据。
  • @PlayHardGoPro 内部 strtok() 使用静态变量来存储标记化的状态,因此您需要告诉它您正在解析相同的字符串,即通过 NULL 作为第一个参数,请注意,如果您传递另一个字符串,那么它将不起作用,有一个可重入 POSIX 版本的 strtok() strtok_r() 将状态存储在用户提供的 poitner 中,但仍然,strtok() 仅在简单情况下是可以的像这样。
  • 知道了,谢谢!我已经有了不带逗号的数据,当我打印令牌时,我得到“1jose123”。只是不知道如何拆分它以将其值分配给我的结构。这是完整的代码。我在将令牌的值传递给我的结构时遇到了一些问题。如果你能检查一下。 pastebin.com/TQeB4G1G
【解决方案2】:

如果必须使用相同的代码处理两个数据文件,那么您将不得不将字段读入字符串,然后将字符串转换为数字。

从你的描述中不清楚你是否需要在行尾做一些特殊的事情——但是因为只有一个数据行以逗号结尾,你必须允许字段用一个分隔逗号或换行符。

坦率地说,您可能会使用 getchar() 或等价物。很简单。

char buffer[4096];
char *bufend = buffer + sizeof(buffer) - 1;
char *curfld = buffer;
int c;

while ((c = getc(arq_file)) != EOF)
{
    if (curfld == bufend)
        …process overlong field…
    else if (c == ',' || c == '\n')
    {
        *curfld = '\0';
        process(buffer);
        curfld = buffer;
    }
    else
        *curfld++ = c;
}
if (c == EOF && curfld != buffer)
{
    *curfld = '\0';
    process(buffer);
}

但是,如果您想使用更高级别的函数,那么您确实想使用fgets() 来读取行(除非您需要担心异常的行尾,例如 DOS vs Unix vs old-style Mac (CR -仅)行尾)。或者使用 POSIX getline() 读取任意长的行。然后使用strtok_r() 或等效方法拆分行。

char *buffer = 0;
size_t buflen = 0;

while (getline(&buffer, &buflen, arq_file) != -1)
{
     char *posn = buffer;
     char *epos;
     char *token;
     while ((token = strtok_r(posn, ",\n", &epos)) != 0)
     {
         process(token);
         posn = 0;
     }
     /* Do anything special for end of line */
}
free(buffer);

如果你认为你必须使用scanf(),那么你需要使用类似的东西:

char buffer[4096];
char c;

while (fscanf(arq_file, "%4095[^,\n]%c", buffer, &c) == 2)
    process(buffer);

%4095[^,\n] 扫描集最多可将 4095 个既不是逗号也不是换行符的字符读取到 buffer,然后读取下一个字符(因此,必须是逗号或换行符 - 或者可能是 EOF,但这会导致问题)进入c。如果文件中的最后一个字符既不是逗号也不是换行符,那么您将跳过最后一个字段。

【讨论】:

  • 关于最后一个代码和EOFfscanf(arq_file, "%4095[^,\n]%c", buffer, &amp;c) &gt;= 1怎么样?然而,此代码的更大问题是 %4095[^,\n] 扫描集读取 1 到 4095 个字符,但不是 0。其他方法为 +1。
  • @chux:是的,您可以使用fscanf(…) &gt;= 1,但无论如何我可能会选择其他方法之一。我喜欢使用 4096 作为缓冲区大小,部分原因是它的“冲击值”(比如 80 或 256)。但是没有让fscanf() 读取零长度字段的好方法;走其他路的另一个原因。 strtok_r() 也不能正确处理空字段(多个相邻分隔符被忽略)。最终,CSV 库才是正确的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-05
  • 1970-01-01
  • 2021-11-16
  • 2011-12-04
  • 1970-01-01
  • 2022-06-14
相关资源
最近更新 更多