IO拷贝文件大全

1. 文件拷贝简介

在shell命令中有cp命令,能实现文件拷贝

cp srcfile destfile

若文件destfile不存在,则将srcfile复制一份取名为destfile;
若文件destfile存在,则将srcfile复制一份取名为destfile,覆盖原来的destfile;
文件存在则清空,文件不存在则创建。

2. 使用fgetc/fputc拷贝文件

  1. 使用open以只读的方式打开srcfile,然后以只写的方式打开destfile。
FILE *fp = open("srcfilr","r"); //以只读的方式打开文件,光标定位在文件开头 
//若打开文件成功则返回文件的文件指针,若打开失败则返回NULL,并置为错误码。
if(fp == NULL){
    printf("errno = %d\n", errno);//errno就是错误码,系统定义的可直接使用
    printf("%s\n", strerror(errno));//错误码就是个数字,如果不知道其含义可以使用strerror(errno)打印错误码对应的信息
    perror("fopen file");//可以使用perror直接打印出错误信息,"fopen file"字符串是附加信息,若不加换行,则错误信息会在字符串后接 ':'加错误信息;加换行后 ':'和错误信息会在下一行显示。
    return -1;//返回-1
}
FILE *fq = open("destfile", "w");//以只读的方式打开文件destfile,若文件存在则清空文件,若文件不存在则创建文件。
if(fp == NULL){
    //可使用perror打印文件错误信息,然后返回-1
    perror("fopen file");
    return -1;
}

  1. 循环从srcfile文件中读取文件,然后写入到destfile中,直至读到文件结束符EOF。
char ch;
while((ch = fgetc(fp))!=EOF){
    fputc(ch, fq);
}
  1. 整体代码
//可将头文件封装到一个.h文件中,然后再将一些宏也封装到此文件中,最后将此文件放入/usr/include/文件夹中,这样就可以使用<>了,也不用在编译的时候加头文件路径了
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
    FILE *fp, *fq;
    char ch;
    //文件名通过命令行传参
    if((fp = fopen(argv[1], "r"))==NULL){
        printf("%s",argv[1]);
        //下面两句经常使用,也可封装到头文件中
        perror("");
        return -1;
    }
    if((fq = fopen(argv[2],"w"))==NULL){
        printf("%s",argv[2]);
        perror("");
        return -1;
    }
    while((ch = fgetc(fp))!=EOF){
        fputc(ch, fq);
    }
    //关闭文件
    fclose(fp);
    fclose(fq);
    return 0;
}

3. 使用fgets/fputs拷贝文件

步骤和fgetc/fputc相同

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void cp_file(FILE* fp, FILE* fq)
{
    char buf[10];
    //fgets在读到换行符是会结束,并将换行符保存在buf中,fgets最多能读取size - 1,size是fgets的第二个参数。
    //fgets在读到最后一行时,会先把EOF之前的读完,然后第二次再读取EOF,这时会返回NULL
    while (fgets(buf, sizeof(buf), fp)) {
        //这地点应该加一个判断,判断是否成功写入文件,一般都会写成功,所以在这里默认写入成功
        fputs(buf, fq);
    }
}

int main(int argc, const char* argv[])
{
    FILE *fp, *fq;

    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("open file error");
        return -1;
    }
    if ((fq = fopen(argv[2], "w")) == NULL) {
        perror("open file error");
        return -1;
    }
    cp_file(fp, fq);
    fclose(fp);
    fclose(fq);
    return 0;
}

4. 使用fread/fwrite拷贝文件

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void copy_file(FILE* sfp, FILE* dfp)
{
    char buf[128] = { 0 };
    int ret;
    //当读取到文件结尾的时候feof(sfp)返回真
    //如果读取的时候失败了ferror(sfp)也是真
    //只有两者同时为假的时候才能循环读写数据
    while (!(feof(sfp) || ferror(sfp))) {
        ret = fread(buf, 1, sizeof(buf), sfp);
        fwrite(buf, 1, ret, dfp);
    }
}

int main(int argc, const char* argv[])
{
    FILE *fp, *fq;
    if ((fp = fopen(argv[1], "r")) == NULL) {
        printf("%s", argv[1]);
        perror("");
        return -1;
    }
    if ((fq = fopen(argv[2], "w")) == NULL) {
        printf("%s", argv[2]);
        perror("");
        return -1;
    }
    copy_file(fp, fq);
    fclose(fp);
    fclose(fq);
    return 0;
}

5. 封装头文件

将所需要的头文件封装到一个.h文件中,将复用行较高的语句定义成宏,并将此文件移动到/usr/include/路径下。
若以后需要可以直接向此头文件中添加内容,在使用的时候我们只需引入这一个头文件即可。

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

#endif

6. 使用read/write拷贝文件

#include <head.h>

int main(int argc, const char* argv[])
{
    int fd1, fd2, ret;
    char buf[128] = { 0 };
    //以只读的方式打开文件,若成功则返回文件描述符,失败返回-1
    if ((fd1 = open(argv[1], O_RDONLY)) == -1)
        //头文件中定义的宏
        PRINT_ERR("open file1 error");
    //以只读的方式打开文件,若文件存在则清空文件,不存在则创建文件,文件权限为(0666 &(~umask))
    if ((fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
        PRINT_ERR("open file2 error");
    //read成功返回读取到的字节的个数,0表示读取到了文件的结尾,失败返回-1置位错误码,所以只要read的返回值大于0都写文件
    while ((ret = read(fd1, buf, sizeof(buf))) > 0) {
        //将buf中的内容写入文件,写入长度为ret
        write(fd2, buf, ret);
    }

    return 0;
}

7. 多进程拷贝文件

父进程拷贝文件的前半部分,子进程拷贝文件的后半部分

  1. 首先求出想要拷贝文件的长度
//将光标定位到文件的结尾,lseek返回是光标距离文件开头的字节数,也就是文件的大小
int len = lseek(fd, 0, SEEK_END);
  1. 查看目标文件是否有,若有则清空,若没有则创建
if((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1){
        PRINT_ERR("open destfile error");
}
  1. 创建子进程
pid_t pid = fork();
//创建子进程,成功父进程返回子进程的pid,子进程返回0;失败返回-1
  1. 文件拷贝
    和其他文件拷贝没有什么区别,就是要注意光标的位置,一定要在子进程和父进程中分别打开文件,否则在创建子进程的时候子进程会拷贝父进程中的所有资源,包括文件的光标位置,也也就是父进程和子进程公用一个光标,任意进程修改光标都会对另一进程造成影戏。
  2. 全部代码
#include <head.h>

//检查目标文件是否创建
int InitFile(const char *filename){
    int fd;
    if((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1){
        PRINT_ERR("open destfile error");
    }
    close(fd);
    return 0;
}
//求文件的长度
int filelen(const char *filename){
    int fd;
    if((fd = open(filename, O_RDONLY)) == -1)
        PRINT_ERR("open srcfile error");
    int len = lseek(fd, 0, SEEK_END);
    return len;
}
//拷贝文件
int copy_file(const char *srcfile, const char *destfile, int n, int len){
    int sfd, dfd;
    char buf[128];
    int count = 0;
    //只能在父进程和子进程中分别打开文件
    //以只读方式打开源文件
    if((sfd = open(srcfile,O_RDONLY)) == -1)
        PRINT_ERR("open srcfile error");
    //以读写的方式开打文件,以只写的方式也可以
    if((dfd = open(destfile, O_RDWR)) == -1)
        PRINT_ERR("open destfile error");
    //分别定位文件的光标位置,若是子进程先执行,此时文件长度为0,lseek是可以将光标定位到距文件开头n个字节位置
    lseek(sfd, n, SEEK_SET);
    lseek(dfd, n, SEEK_SET);
    //使用read/write读写文件
    while(count = read(sfd, buf, sizeof(buf))){
        //len的作用主要是防止父进程在拷贝文件的时候将子进程拷贝的前几个字符覆盖
        //每次都将还需读取的长度减去一睹字节
        len -= count;
        //当最后一次读取长度大于所需长度,那么我们在写的时候只需要写入我们需要的长度
        if(len < 0)
            count += len;
        write(dfd, buf, count);
    }
    return 0;
}
int main(int argc, const char* argv[])
{
    int fd, wfd;

    int len = filelen(argv[1]);
    if(len == -1){
        fprintf(stderr, "srcfilelen  == -1");
        return -1;
    }

    InitFile(argv[2]);
    pid_t pid = fork();

    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        copy_file(argv[1], argv[2], len / 2, len - len / 2);
    } else {
       copy_file(argv[1],argv[2], 0, len / 2);
    }
    return 0;
}

8. 多线程拷贝文件

多线程拷贝文件的速度要比多进程拷贝文件的速度快

#include <head.h>

pthread_t tid1, tid2;
int file_len;

void CopyFile(int n, int len){
    int sfd, dfd;
    char buf[128];
    int count = 0;
    if ((sfd = open("hello.txt", O_RDONLY)) == -1) {
        printf("open srcfile error");
    }

    if ((dfd = open("world.txt", O_RDWR)) == -1) {
        printf("open destfile error");
    }

    lseek(sfd, n, SEEK_SET);
    lseek(dfd, n, SEEK_SET);

    while (count = read(sfd, buf, sizeof(buf))) {
        len -= count;

        if (len < 0)
            count += len;
        write(dfd, buf, count);
    }
}
//查看目标文件是否有
int InitFile(const char* filename)
{
    int fd;
    if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
        PRINT_ERR("open destfile error");
    }
    close(fd);
    return 0;
}
//求源文件长度
int filelen(const char* filename)
{
    int fd;
    if ((fd = open(filename, O_RDONLY)) == -1)
        PRINT_ERR("open srcfile error");
    int len = lseek(fd, 0, SEEK_END);
    return len;
}
//根据不同的arg调用不同的处理函数
void* Func(void* arg)
{
    int n = 0, len = 0;
    int* flag = (int*)arg;

    if (*flag == 0) {
        n = 0;
        len = file_len / 2;
    } else {
        n = file_len / 2;
        len = file_len - n;
    }
    //printf("*flag = %d, n = %d, len = %d\n", *flag, n, len);
    CopyFile(n, len);
}

int main(int argc, const char* argv[])
{
    //为了方便,将文件写成固定的了,当让也可采用命令行传参
    file_len = filelen("hello.txt");
    if (file_len == -1) {
        fprintf(stderr, "srcfilelen  == -1");
        return -1;
    }

    InitFile("world.txt");

    //通过不同的标记拷贝文件不同的位置
    int flag = 0;
    if (errno = pthread_create(&tid1, NULL, Func, (void*)&flag))
        PRINT_ERR("create thread1 error");
    int flag1 = 1;
    if (errno = pthread_create(&tid2, NULL, Func, (void*)&flag1))
        PRINT_ERR("create thread2 error");
    //一定要阻塞等待线程结束,或者使用死循环,一定不要让进程结束,当进程结束时,所有的线程都会被销毁
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值