문제

이것은 간단한 질문처럼 보이지만 여기서는 비슷한 것을 찾지 못했습니다.

C에는 파일 복사 함수가 없으므로 스스로 복사하는 파일을 구현해야하지만 사소한 물건에 대해서도 휠을 재창조하는 것을 좋아하지 않으므로 클라우드를 묻고 싶습니다.

  1. fopen ()/fread ()/fwrite ()를 사용하여 파일 복사에 어떤 코드를 권장 하시겠습니까?
    • Open ()/read ()/write ()를 사용하여 파일 복사에 어떤 코드를 권장 하시겠습니까?

이 코드는 휴대 가능 (Windows/Mac/Linux/BSD/QNX/Younameit), 안정, 시간 테스트, 빠른, 메모리 효율 등이 있어야합니다. 특정 시스템의 내부에 들어가기 위해 더 많은 성능을 짜기 위해 (파일 시스템 클러스터 크기를 얻는 것과 같은) 환영합니다. .

이것은 사소한 질문처럼 보이지만 예를 들어 CP 명령의 소스 코드는 C 코드의 10 행이 아닙니다.

도움이 되었습니까?

해결책

실제 I/O가 진행되는 한, 한 스트림에서 다른 스트림으로 데이터를 복사하기 위해 다양한 GOISE에서 백만 번 쓴 코드는 다음과 같습니다. 성공시 0을 반환하거나 Errno가 오류에 설정된 경우 -1이 오류가 발생합니다 (이 경우 수의 바이트가 복사되었을 수 있음).

일반 파일을 복사 할 때는 일반 파일이 항상 I/O를 차단하기 때문에 Eagain 물건을 건너 뛸 수 있습니다. 그러나 필연적 으로이 코드를 작성하면 누군가가 다른 유형의 파일 설명자에 사용하므로 공짜를 고려하십시오.

GNU의 파일 별 최적화가 있습니다 cp 내가 여기서 귀찮게하지 않은 것, 쓰기하는 대신 0 바이트의 긴 블록의 경우 끝을 찾아 출력 파일을 확장합니다.

void block(int fd, int event) {
    pollfd topoll;
    topoll.fd = fd;
    topoll.events = event;
    poll(&topoll, 1, -1);
    // no need to check errors - if the stream is bust then the
    // next read/write will tell us
}

int copy_data_buffer(int fdin, int fdout, void *buf, size_t bufsize) {
    for(;;) {
       void *pos;
       // read data to buffer
       ssize_t bytestowrite = read(fdin, buf, bufsize);
       if (bytestowrite == 0) break; // end of input
       if (bytestowrite == -1) {
           if (errno == EINTR) continue; // signal handled
           if (errno == EAGAIN) {
               block(fdin, POLLIN);
               continue;
           }
           return -1; // error
       }

       // write data from buffer
       pos = buf;
       while (bytestowrite > 0) {
           ssize_t bytes_written = write(fdout, pos, bytestowrite);
           if (bytes_written == -1) {
               if (errno == EINTR) continue; // signal handled
               if (errno == EAGAIN) {
                   block(fdout, POLLOUT);
                   continue;
               }
               return -1; // error
           }
           bytestowrite -= bytes_written;
           pos += bytes_written;
       }
    }
    return 0; // success
}

// Default value. I think it will get close to maximum speed on most
// systems, short of using mmap etc. But porters / integrators
// might want to set it smaller, if the system is very memory
// constrained and they don't want this routine to starve
// concurrent ops of memory. And they might want to set it larger
// if I'm completely wrong and larger buffers improve performance.
// It's worth trying several MB at least once, although with huge
// allocations you have to watch for the linux 
// "crash on access instead of returning 0" behaviour for failed malloc.
#ifndef FILECOPY_BUFFER_SIZE
    #define FILECOPY_BUFFER_SIZE (64*1024)
#endif

int copy_data(int fdin, int fdout) {
    // optional exercise for reader: take the file size as a parameter,
    // and don't use a buffer any bigger than that. This prevents 
    // memory-hogging if FILECOPY_BUFFER_SIZE is very large and the file
    // is small.
    for (size_t bufsize = FILECOPY_BUFFER_SIZE; bufsize >= 256; bufsize /= 2) {
        void *buffer = malloc(bufsize);
        if (buffer != NULL) {
            int result = copy_data_buffer(fdin, fdout, buffer, bufsize);
            free(buffer);
            return result;
        }
    }
    // could use a stack buffer here instead of failing, if desired.
    // 128 bytes ought to fit on any stack worth having, but again
    // this could be made configurable.
    return -1; // errno is ENOMEM
}

입력 파일을 엽니 다 :

int fdin = open(infile, O_RDONLY|O_BINARY, 0);
if (fdin == -1) return -1;

출력 파일을 여는 것은 tricksy입니다. 근거로, 당신은 다음을 원합니다 :

int fdout = open(outfile, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0x1ff);
if (fdout == -1) {
    close(fdin);
    return -1;
}

그러나 혼란스러운 요인이 있습니다.

  • 파일이 동일 할 때 특수 사례가 필요하며,이를 완성하는 방법을 기억할 수 없습니다.
  • 출력 파일 이름이 디렉토리 인 경우 파일을 디렉토리에 복사 할 수 있습니다.
  • 출력 파일이 이미 존재하는 경우 (O_EXCL로 열려이이를 결정하고 오류에 대한 EEXIST를 확인하십시오). cp -i 하다.
  • 출력 파일의 권한이 입력 파일의 권한을 반영하기를 원할 수 있습니다.
  • 다른 플랫폼 별 메타 데이터를 복사하기를 원할 수도 있습니다.
  • 오류에서 출력 파일을 풀고 싶거나 원하지 않을 수도 있습니다.

분명히이 모든 질문에 대한 답은 " cp".이 경우 원래 질문에 대한 답은"나 또는 다른 사람이 말한 모든 것을 무시하고 출처를 사용하는 것입니다. cp".

BTW, 파일 시스템의 클러스터 크기를 얻는 것은 쓸모가 없습니다. 디스크 블록의 크기를 통과 한 후 오랫동안 버퍼 크기로 속도가 거의 항상 증가 할 것입니다.

다른 팁

테스트 하네스를 사용하여 한 파일에서 다른 파일로 복사해야 할 때 사용하는 기능입니다.

/*
@(#)File:           $RCSfile: fcopy.c,v $
@(#)Version:        $Revision: 1.11 $
@(#)Last changed:   $Date: 2008/02/11 07:28:06 $
@(#)Purpose:        Copy the rest of file1 to file2
@(#)Author:         J Leffler
@(#)Modified:       1991,1997,2000,2003,2005,2008
*/

/*TABSTOP=4*/

#include "jlss.h"
#include "stderr.h"

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_fcopy_c[] = "@(#)$Id: fcopy.c,v 1.11 2008/02/11 07:28:06 jleffler Exp $";
#endif /* lint */

void fcopy(FILE *f1, FILE *f2)
{
    char            buffer[BUFSIZ];
    size_t          n;

    while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0)
    {
        if (fwrite(buffer, sizeof(char), n, f2) != n)
            err_syserr("write failed\n");
    }
}

#ifdef TEST

int main(int argc, char **argv)
{
    FILE *fp1;
    FILE *fp2;

    err_setarg0(argv[0]);
    if (argc != 3)
        err_usage("from to");
    if ((fp1 = fopen(argv[1], "rb")) == 0)
        err_syserr("cannot open file %s for reading\n", argv[1]);
    if ((fp2 = fopen(argv[2], "wb")) == 0)
        err_syserr("cannot open file %s for writing\n", argv[2]);
    fcopy(fp1, fp2);
    return(0);
}

#endif /* TEST */

분명히이 버전은 파일 설명자가 아닌 표준 I/O의 파일 포인터를 사용하지만 합리적으로 효율적이며 휴대용이 가능합니다.


글쎄, 오류 함수를 제외하고는 - 그것은 나에게 독특합니다. 오류를 깨끗하게 처리하는 한 괜찮을 것입니다. 그만큼 "jlss.h" 헤더가 선언합니다 fcopy(); 그만큼 "stderr.h" 헤더가 선언합니다 err_syserr() 다른 많은 유사한 오류보고 기능 중. 기능의 간단한 버전은 다음과 같습니다. 실제는 프로그램 이름을 추가하고 다른 작업을 수행합니다.

#include "stderr.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

void err_syserr(const char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
    exit(1);
}

위의 코드는 선택한 경우 최신 BSD 라이센스 또는 GPL V3을 갖는 것으로 취급 될 수 있습니다.

각 읽기의 크기는 512 (섹터 크기) 4096의 배수 여야합니다.

다음은 매우 쉽고 명확한 예입니다. 파일을 복사하십시오. 특정 기능 호출이없는 ANSI-C로 작성 되었으므로이 제품은 휴대 성일 것이라고 생각합니다.

파일을 복사함으로써 의미하는 바에 따라, 그것은 확실히 사소한 것과는 거리가 멀다. 콘텐츠 만 복사한다는 의미라면 거의 할 일이 없습니다. 그러나 일반적으로 파일의 메타 데이터를 복사해야하며 반드시 플랫폼 의존적입니다. 휴대용으로 원하는 C 라이브러리를 모릅니다. 휴대성에 관심이있는 경우 파일 이름을 자체적으로 처리하는 것만으로는 사소한 문제가 아닙니다.

C ++에는 파일 라이브러리가 있습니다. 후원

내 자신의 파일 사본을 구현할 때 찾은 한 가지는 분명해 보이지만 I/O는 그렇지 않습니다. 느린. 당신은 얼마나 많은 사람들이 당신의 사본의 속도를 거의 시간을 가질 수 있습니다. 따라서 분명히 가능한 한 적은 수의해야합니다.

내가 찾은 가장 좋은 결과는 내가 gnourmous 버퍼를 얻었을 때의 전체 소스 파일을 하나의 I/O로 읽은 다음 전체 버퍼를 하나의 I/O로 다시 썼습니다. 내가 10 개의 배치로 그것을해야한다면, 그것은 느려졌다. Naieve 코더가 먼저 시도 할 수있는 것처럼 각 바이트를 읽고 쓰려고 시도하는 것은 고통 스러웠습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top