machine_1
machine_1

Reputation: 4454

Copy text and binary files with one function?

I have two functions. One copies text files which uses the "r" and "w" modes for reading and writing, and one that copies binary files which uses the "rb" and "wb" modes.

But if I were to use these functions, I have to determine first whether the file that I want to duplicate is a text or binary file. I want a solution that allows me to copy a file successfully without knowing previously the nature of the file. A single function that deals with this matter. Any ideas?

P.S : I noticed that ctrl + c copy is faster than my function, so tips to improve the speed of the function are also welcome.

#include <stdio.h>

void copyfile_txt(const char *inputfile, const char *outputfile);
void copyfile_bin(const char *inputfile, const char *outputfile);

int main(void)
{
    copyfile_txt("file_input.txt", "file_output.txt");
    copyfile_bin("ubuntu-14.04.3-desktop-amd64.iso", "Ubuntu.iso");
    return 0;
}

long GetFileSize(FILE *fp)
{
    fseek(fp, 0L, SEEK_END);
    long sz = ftell(fp);
    fseek(fp, 0L, SEEK_SET);
    return sz;
}
void copyfile_txt(const char *inputfile, const char *outputfile)
{
    static FILE *fp_I;
    static FILE *fp_O;

    fp_I = fopen(inputfile, "r");
    if (!fp_I) {
        perror(inputfile);
        return;
    }

    fp_O = fopen(outputfile, "w");
    if (!fp_O) {
        fclose(fp_I);
        perror(outputfile);
        return;
    }

    static int c;
    while ((c = getc(fp_I)) != EOF) {
        putc(c, fp_O);
    }
    fclose(fp_I);
    fclose(fp_O);
}
void copyfile_bin(const char *inputfile, const char *outputfile)
{
    static FILE *fp_I;
    static FILE *fp_O;

    fp_I = fopen(inputfile, "rb");
    if (!fp_I) {
        perror(inputfile);
        return;
    }

    fp_O = fopen(outputfile, "wb");
    if (!fp_O) {
        fclose(fp_I);
        perror(outputfile);
        return;
    }

    static int c;
    static long n;
    static long sz;

    n = 0;
    sz = GetFileSize(fp_I);

    while (n++ < sz) {
        c = getc(fp_I);
        putc(c, fp_O);
    }

    fclose(fp_I);
    fclose(fp_O);
}

Upvotes: 5

Views: 794

Answers (1)

chqrlie
chqrlie

Reputation: 144770

You do not need to make a special case of text files, copy all files as binary. This is what most file copying utilities do.

Note however that your binary file copying function has problems:

  • There is no need to compute the file size, copying until you get EOF is correct and allows you to copy files larger than LONG_MAX.

  • There is no need for static variables: using static variables makes your function fail if used from multiple threads.

  • It would be useful to return a completion code.

Here is a simpler version:

int copyfile_bin(const char *inputfile, const char *outputfile) {
    FILE *fp_I;
    FILE *fp_O;

    fp_I = fopen(inputfile, "rb");
    if (!fp_I) {
        perror(inputfile);
        return -1;
    }

    fp_O = fopen(outputfile, "wb");
    if (!fp_O) {
        fclose(fp_I);
        perror(outputfile);
        return -1;
    }

    int c;
    while ((c = getc(fp_I)) != EOF) {
        putc(c, fp_O);
    }

    fclose(fp_I);
    if (fclose(fp_O) != 0) {
        perror(outputfile);
        return -1;
    }
    return 0;
}

To improve speed, you could increase the buffer sizes with setvbuf() or use fread() and fwrite() with a large allocated buffer.

For a faster inner loop, use this:

    size_t nr, size = 1024 * 1024;
    void *buffer = malloc(size);
    if (buffer != NULL) {
        while ((nr = fread(buffer, 1, size, fp_I)) > 0) {
            if (fwrite(buffer, 1, nr, fp_O) != nr) {
                fprintf(stderr, "write error: ");
                perror(outputfile);
                free(buffer);
                fclose(fp_I);
                fclose(fp_O);
                return -1;
            }
        }
        if (ferror(fp_I)) {
            fprintf(stderr, "read error: ");
            perror(inputfile);
            free(buffer);
            fclose(fp_I);
            fclose(fp_O);
            return -1;
        }
        free(buffer);
    } else {
        /* use the regular getc/putc loop */
        int c;
        while ((c = getc(fp_I)) != EOF) {
            putc(c, fp_O);
        }
    }

Upvotes: 6

Related Questions