文件分块和缓冲?

时间:2012-12-31 23:57:33

标签: c

最终,我只是想将二进制文件剪切成大小不超过X的片段。不用别的了。如果输入文件是21MB,我想要3个7MB我可以加入猫或什么不。我在下面的简单示例中工作,我使用7MB块大小的缓冲区。我是否必须使用它才能获得7MB的文件块?如果块大小是2GB,那显然不是我想放在内存中的东西。所以我需要创建一个缓冲区。

我确实在这里和其他网站上读了几篇关于这个的帖子,但是它们似乎都使用了malloc或数组创建的某种缓冲区,并且查找非缓冲方式让我领先于我对套接字和与TCP / IP相关的主题。

我注定了很多if / while语句吗?

P.S。我在哪里可以找到C中I / O流的书籍?我可以找到很多C ++,但不是C。

ifp = fopen(ifile, "rb"); // ifile is a 25MB sound file
ofp = fopen(ofile, "w");  // Omitted error checking.

setvbuf( ifp, NULL, _IOFBF, 1024); // Are these on
setvbuf( ofp, NULL, _IOFBF, 1024); // by default?

size_t CHUNK = 7000000;  // 7MB Chunk sizes
size_t result = 0;
size_t *buffer = malloc(CHUNK);

if (buffer == NULL) {fputs ("Could not allocate memory",stderr); exit (1);}
// Read 1 btye at a time?
result = fread(buffer, 1, CHUNK, ifp);
if (result != CHUNK) {fputs ("ERROR: Buffer/read mismatch.",stderr); exit (1);}

fwrite(buffer, CHUNK, 1, ofp);

free(buffer);

3 个答案:

答案 0 :(得分:1)

setvbuf( ifp, NULL, _IOFBF, I_BUFFER); // Are these on
setvbuf( ofp, NULL, _IOFBF, O_BUFFER); // by default?

这些文件缓冲区设置为“完全缓冲”,这意味着只有当缓冲区(由I_BUFFER和O_BUFFER定义)已满时才会写入数据。

我还建议您一次不需要大量阅读。 10-100KB足以将操作系统中的任何开销减少到几乎没有,并且执行此操作几次的循环将是如此小的比例无关紧要。如果你读取较小的部分,然后写出一小部分,你甚至可能会有一些重叠,如果你一次读取7MB,这将需要足够长的时间,以前的7MB的写入可能已完全完成已阅读7MB。

www.cplusplus.com涵盖了所有C标准库(尽管名称,它涵盖了C函数以及C ++函数)。

答案 1 :(得分:1)

这是我最初在1991年写的一个程序bsplit。它将文件分成任意大小的块;默认大小以千字节为单位(嗯,kibibytes - 1024字节)。

/*
@(#)File:           $RCSfile: bsplit.c,v $
@(#)Version:        $Revision: 1.11 $
@(#)Last changed:   $Date: 2008/08/09 05:54:55 $
@(#)Purpose:        Split file into blocks -- binary
@(#)Author:         J Leffler
*/

#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif /* __STDC_VERSION__ */

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "stderr.h"
#include "filter.h"

#define MAXFILENAMELEN  256
#define KILOBYTE 1024
#define MEGABYTE (KILOBYTE*KILOBYTE)
#define GIGABYTE (MEGABYTE*KILOBYTE)
#define NIL(x)  ((x)0)
#define MIN(a,b)    (((a) < (b)) ? (a) : (b))

char    *prefix = "bsplit.";
size_t   blocksize = 64;
size_t   nblocks = 0;
size_t   skipblocks = 0;
char     buffer[64*KILOBYTE];
long     counter = 0;

static int  bsplit(FILE *ifp, const char *fn)
{
    size_t   n;         /* Bytes read this time */
    size_t   bsize;     /* Size written for current block */
    size_t   tsize;     /* Size written for current file */
    size_t   rsize;     /* Amount to read */
    FILE    *op;        /* Output file stream */
    char     file[MAXFILENAMELEN];  /* Output file name */

    tsize = 0;
    bsize = 0;
    op = NIL(FILE *);
    rsize = MIN(sizeof(buffer), blocksize);
    while ((n = fread(buffer, sizeof(char), rsize, ifp)) > 0)
    {
        tsize += n;
        if (tsize > skipblocks)
        {
            if (bsize == 0)
            {
                sprintf(file, "%s%03ld", prefix, counter++);
                if ((op = fopen(file, "w")) == NIL(FILE *))
                {
                    err_sysrem2("failed to open file", file);
                    return(-1);
                }
                printf("%s\n", file);
            }
            bsize += n;
            if (fwrite(buffer, sizeof(char), n, op) != n)
            {
                err_sysrem2("failed to write to file", file);
                return(-1);
            }
            if (bsize >= blocksize)
            {
                fclose(op);
                bsize = 0;
            }
            if (nblocks > 0 && tsize >= nblocks)
                break;
        }
    }
    return 0;
}

int main(int argc, char **argv)
{
    int opt;
    size_t multiplier = KILOBYTE;
    char *p;
    char  c;
    int   rc;

    opterr = 0;
    err_setarg0(argv[0]);

    while ((opt = getopt(argc, argv, "s:n:p:b:V")) != -1)
    {
        switch (opt)
        {
        case 'p':
            prefix = optarg;
            if (strlen(prefix) > MAXFILENAMELEN - sizeof("000"))
                err_error("file name prefix (%s) is too long (max %d)", prefix,
                          (int)(MAXFILENAMELEN-sizeof("000")));
            break;
        case 's':
            skipblocks = atoi(optarg);
            break;
        case 'n':
            nblocks = atoi(optarg);
            break;
        case 'b':
            blocksize = atoi(optarg);
            p = optarg + strspn(optarg, "0123456789");
            if (*p != '\0')
            {
                c = tolower((unsigned char)*p);
                if (c == 'c')
                    multiplier = 1;
                else if (c == 'b')
                    multiplier = KILOBYTE/2;
                else if (c == 'k')
                    multiplier = KILOBYTE;
                else if (c == 'm')
                    multiplier = MEGABYTE;
                else if (c == 'g')
                    multiplier = GIGABYTE;
                else
                    err_error("unknown size multiplier suffix %s\n", p);
                if (p[1] != '\0')
                    err_error("unknown size multiplier suffix %s\n", p);
            }
            break;
        case 'V':
            err_version("BSPLIT", &"@(#)$Revision: 1.11 $ ($Date: 2008/08/09 05:54:55 $)"[4]);
            break;
        default:
            err_usage("[-b blocksize][-p prefix][-s skipblocks][-n blocks][file [...]]");
            break;
        }
    }

    /* Convert sizes to bytes */
    blocksize  *= multiplier;
    skipblocks *= blocksize;
    if (nblocks > 0)
        nblocks = skipblocks + nblocks * blocksize;

    rc = filter_stdout(argc, argv, optind, bsplit);
    return(rc);
}

标题stderr.h声明了一系列错误报告例程;我在大多数程序中都使用它。标题filter.h声明函数filter_stdout(),它遍历参数列表,打开文件以便读取和调用函数 - 在本例中为bsplit() - 依次处理每个文件。它自动处理'无参数意味着读取标准输入'等。 (联系我以获取代码 - 请参阅我的个人资料。)

请注意,乘数c表示'字符',b表示512字节的块,kmg表示KiB, MiB和GiB分别。

答案 2 :(得分:0)

你根本不需要担心缓冲。标准C库开发人员已经为您完成了这项工作。即使您使用fgetc()逐字逐句阅读并使用fputc()进行书写,也不会失去太多的表现。这些通常是访问缓冲的stdio结构的宏。

我相信我不必告诉你如何编写一个按字符读写的循环,并在所需的字节数后切换到一个新文件。

再次明确说明:优化的第一条规则:不要。第二个:在你做之前的措施。