【问题标题】:copying contents of file to another file n bytes at a time in c在c中一次将文件的内容复制到另一个文件n个字节
【发布时间】:2023-04-09 14:22:02
【问题描述】:

试图通过在 c 中一次复制 n 个字节来将文件的内容复制到另一个文件。我相信下面的代码可以一次复制一个字节,但我不确定如何使它适用于 n 个字节,尝试制作一个大小为 n 的字符数组并将读/写函数更改为 read(sourceFile , &c, n) 和 @ 987654322@,但缓冲区似乎不是这样工作的。

#include <fcntl.h>    
#include <unistd.h> 
#include <stdint.h>
#include <time.h>

void File_Copy(int sourceFile, int destFile, int n){
    char c;

    while(read(sourceFile , &c, 1) != 0){
        write(destFile , &c, 1);
    }


}

int main(){
    int fd, fd_destination;      
    fd = open("source_file.txt", O_RDONLY); //opening files to be read/created and written to
    fd_destination = open("destination_file.txt", O_RDWR | O_CREAT); 

    clock_t begin = clock(); //starting clock to time the copying function

    File_Copy(fd, fd_destination, 100); //copy function

    clock_t end = clock();
    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; //timing display

return 0;
}

【问题讨论】:

  • read() 返回读取的字节数。知道何时要对这些字节做某事很重要。
  • while(read(sourceFile , &amp;c, 1) != 0) -> while(read(sourceFile , &amp;c, 1) == 1)
  • “但缓冲区似乎不是那样工作”是什么意思。意思是?您的尝试以何种方式失败?
  • 您应该查看read 的联机帮助页,在这种情况下,函数返回的值可能小于预期的数字。

标签: c file unix


【解决方案1】:

如何让它在 n 个字节上工作

只需读取 N 个字节并复制您成功读取的字节数。

#define N  4096 
void File_Copy(int sourceFile, int destFile, int n){
    char c[N];
    const size_t csize = sizeof(c)/sizeof(*c);
    while (1) {
        const ssize_t readed = read(sourceFile, c, csize);
        if (readed <= 0) {
            // nothing more to read
            break;
        }
        // copy to destination that many bytes we read
        const ssize_t written = write(destFile, c, readed);
        if (written != readed) {
            // we didn't transfer everything and destFile should be blocking
            // handle error
            abort();
        }
    }
}

【讨论】:

  • 请注意 c[N] 存储在堆栈中。在我的系统上,堆栈大小为 8MB。您可能想使用 malloc...
  • N = 4096 是 4 KB。所以它将使用 0.0005% 的堆栈空间。
【解决方案2】:

您想一次复制大小为n 的缓冲区:

void File_Copy(int sourceFile, int destFile, int n){
    char c[n];

    ssize_t st;
    while((st = read(sourceFile , c, n)) > 0){
        write(destFile , c, st);
    }
}

注意,n 字节不一定总是被一次复制,它可能会更少。您还必须检查 write() 的返回值并处理写入较少字节的情况,因为它符合您的需要。

一个例子是循环:

while (st > 0) {
    int w = write(destFile, c, st);
    if (w < 0) {
        perror("write");
        return;
    }
    st -= w;
}

另一个问题:当你在这里创建目标文件时

fd_destination = open("destination_file.txt", O_RDWR | O_CREAT);

您没有指定第三个 mode 参数。这会导致随机模式,这可能会导致此open() 下次失败。所以最好添加一个有效模式,例如这样:

fd_destination = open("destination_file.txt", O_RDWR | O_CREAT, 0644);

这可能会扭曲您的测试结果。

【讨论】:

  • write 可能写得少于st,它需要一个循环。
  • @MaximEgorushkin 我已经提到必须处理这个问题。循环是一种可能性。
  • 好吧,我可以理解这里发生的大部分事情,但我不明白为什么缓冲区中的字节数似乎根本不影响性能。无论我做多高/低,它似乎都不会改变执行复制所需的时间,这有什么原因吗?我会假设一次更改要复制的位数会对运行时产生一些可衡量的影响。
  • @Wizlawn 是的,应该。你可能用太小的文件来测试它,你的总运行时间是多少?
  • @Ctx 我已经得到了大约 10-11 秒。时间似乎大多是随机的,与我给出的 n 值没有相关性,并且我测试了从 1 到 10000 的值。
【解决方案3】:

这是我使用 lseek 的版本(不需要循环): 它依赖于读写总是处理完整的缓冲区,而不是它的一部分(我不知道这是否得到保证)。

void File_Copy(int sourceFile, int destFile)
{
    off_t s = lseek(sourceFile, 0, SEEK_END);
    lseek(sourceFile, 0, SEEK_SET);

    char* c = malloc(s);

    if (read(sourceFile, c, s) == s)
        write(destFile, c, s);

    free(c);
}

以下代码不依赖此假设,也可用于不支持lseek的文件描述符。

void File_Copy(int sourceFile, int destFile, int n)
{
    char* c = malloc(n);
    while (1)
    {
        ssize_t readStatus = read(sourceFile, c, n);
        if (readStatus == -1)
        {
            printf("error, read returned -1, errno: %d\n", errno);
            return;
        }
        if (readStatus == 0)
            break; // EOF

        ssize_t bytesWritten = 0;
        while (bytesWritten != readStatus)
        {
            ssize_t writeStatus = write(destFile, c + bytesWritten, readStatus - bytesWritten);
            if (writeStatus == -1)
            {
                printf("error, write returned -1, errno is %d\n", errno);
                return;
            }
            bytesWritten += writeStatus;
            if (bytesWritten > readStatus) // should not be possible
            {
                printf("how did 'bytesWritten > readStatus' happen?");
                return;
            }
        }
    }
    free(c);
}

在我的系统(PCIe SSD)上,使用 1MB 到 4MB 之间的缓冲区可以获得最佳性能(您也可以使用 dd 来查找此大小)。更大的缓冲区没有意义。而且你需要大文件(尝试 50GB)才能看到效果。

【讨论】:

  • 如果sourcefile 不可搜索?例如,这不能从 stdin 或套接字复制。
  • 我不知道您想要一个可以处理任何类型文件描述符的解决方案。
猜你喜欢
  • 2022-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 2021-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多