【问题标题】:Reading a file in at once rather than line by line (in c)一次读取文件而不是逐行读取(在 c 中)
【发布时间】:2018-02-06 19:56:14
【问题描述】:

我试图一次将一个文件读入一个字符串,但是我只能用这段代码逐行读取它:

int main()
{
    char input[1000000];
    FILE *fPointer;

    fPointer = fopen("/Users/boys/users.json", "r");

    if( fPointer != NULL)
    {
        while(!feof(fPointer))
        {
            fgets(input, 1000000, fPointer);
            //printf("%s", input);

        }

    }
    printf("%s", input);

    return 0;
}

我正在处理一个 json 文件,过去只使用 csv 和更简单的文件和 c,因为我对它还是很陌生,而且不是那么好,所以如果有人有一个简单的解决方案来读取文件一次而不是一行一行,这将是一个很大的帮助,谢谢!

【问题讨论】:

  • 如您所见,我尝试在循环之外打印文件内容,这表明我没有一次阅读它
  • 打开文件,寻尾,获取文件位置(等于大小),回退文件位置,分配内存,读取文件到分配的内存。完成。
  • fread() 函数帮助你。用点和逗号分隔文本,很难理解
  • 至于你目前的代码,我建议你花点时间阅读Why is “while ( !feof (file) )” always wrong?
  • while(!feof(fPointer)) 这不会像你想象的那样工作。

标签: c arrays string file


【解决方案1】:
  1. 打开文件 (FILE *fPointer = fopen("/Users/boys/users.json", "r");)。
  2. 寻找到最后 (fseek(fPointer , 0, SEEK_END);)。
  3. 获取文件位置(等于大小)。
  4. 倒回文件位置。
  5. 分配所需的内存。
  6. 将文件读入分配的内存。
  7. 使用字符串。
  8. 完成后取消分配内存。

PS:通过fread() 读取文件不会形成字符串,因为它可能缺少空字符(但您始终可以自己插入)。

【讨论】:

  • 次要:通过fread() 读取文件不会形成字符串,因为它可能缺少空字符
【解决方案2】:

fgets() 函数读取直到到达 EOF / NewLine / 指定大小。所以使用它的功能不正确,因为如果文件包含多行,您最多可以读取一行。

这是一种可能的方式

首先你必须找出文件中内容的大小(以字节为单位)

fseek(fPointer, 0L, SEEK_END);
long numOfBytes = ftell(fPointer);

比为这个数据量动态分配缓冲区足够大。 (因为你可以使用VLA

char *ptr = calloc(numOfBytes+1, 1);
if (ptr == NULL) // Failed to allocate memory
   { perror("calloc"); exit(EXIT_FAILURE); };

将指示器的位置重置到文件的开头(或重新打开文件)

fseek(fPointer, 0L, SEEK_SET); // Or use rewind

现在您可以使用fread 读取文件的全部内容。

fread(ptr, sizeof(char), numOfBytes, fPointer);

然后清理

free(ptr);
fclose(fPointer);

【讨论】:

  • int numOfBytes = ftell(fPointer);? ftell() 返回 long。学究式地,fseek() 到以二进制模式打开的流的末尾是未定义的行为。
  • 我认为文件的内容不会大于 2^32 这就是 bug 的产生方式。
  • 在大多数机器上,long 的大小与 int 的大小相同。 No.
  • 好吧好吧。我已经修好了。
  • 我推荐calloc(numOfBytes+1, 1) 而不是malloc(numOfBytes),并且应该检查结果是否失败。
【解决方案3】:

在符合 POSIX 的系统上:

int fd = open( filename, O_RDONLY );

struct stat sb;
fstat( fd, sb );

// by adding one, data can possibly be
// treated as a string, assuming no NULs
// exist in the file's contents
char *data = calloc( sb.st_size + 1, 1 );

size_t totalRead = 0;

while ( totalRead < sb.st_size )
{
    ssize_t bytesRead = read( fd, &( data[ totalRead ] ), sb.st_size - totalRead );
    if ( bytesRead <= 0 )
    {
        break;
    }

    totalRead += bytesRead;
}

我使用calloc() 创建一个零填充缓冲区,而不是将最后一个字节显式设置为NUL。这使得实际处理更容易查看和理解。

出于同样的原因,我也省略了错误检查。

【讨论】:

  • 嗯,downvoter 显然不喜欢简单的、符合标准的代码。
  • NMDV,我看到的唯一无法解释的弱点是 unsigned size_tsigned off_t 之间的随意转换及其潜在的不同范围影响.
  • @chux 责备 POSIX。 POSIX 使用off_t 作为struct stat 中的文件大小,使用size_t 作为read() 的参数,然后返回ssize_t,并没有真正简单、简洁的方法来解决这一事实,所有参考相同类型的值 - 字节数。
  • 这可能是一种不好的礼仪,但我赞成这样做以抵消无法解释的反对票。但是,总的来说,使用 st_size 和寻求文件结尾之间是否存在显着差异?我希望内部实现非常相似。
【解决方案4】:

来自man fgets

fgets() 从流中最多读入一个小于 size 的字符,并将它们存储到 s 指向的缓冲区中。在 EOF 或换行符后停止读取。如果读取了换行符,则将其存储到缓冲区中。一个终止的空字节('\0')存储在缓冲区中的最后一个字符之后。

你可以使用fread来实现你想要的。

【讨论】:

    猜你喜欢
    • 2013-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2010-12-24
    • 2010-11-19
    • 1970-01-01
    相关资源
    最近更新 更多