0

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

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

我注定要使用大量的 if/while 语句吗?

PS 在哪里可以找到有关 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);
4

3 回答 3

1

这是我最初在 1991 年编写的一个程序bsplit。它将文件分割成任意大小的块;默认大小以千字节为单位指定(好吧,千字节- 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 字节块,而k, m, 和g分别表示 KiB、MiB 和 GiB。


支持代码现在可以在我的 GitHub 上的SOQ(堆栈溢出问题)存储库中以 files 、 、 和filter.csrc filter.h/ filterio.clibsoq子目录的形式获得。还有一个标题将替换该节 - 并且将使用而不是请求 POSIX.1 2008 (2018) 支持。stderr.cstderr.hposixver.h__STDC_VERSION__#define _XOPEN_SOURCE 700600

于 2013-01-01T00:27:42.963 回答
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++ 函数)。

于 2013-01-01T00:15:05.377 回答
0

您根本不需要担心缓冲。标准 C 库开发人员已经为您完成了这项工作。即使您使用 逐字符读取fgetc()并使用 写入,也不会损失太多性能fputc()。这些通常是访问缓冲 stdio 结构的宏。

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

再次明确说明:优化的第一条规则:不要。第二:先测量再做。

于 2013-01-01T00:26:54.440 回答