要求:写三个代码文件,main.c,mycp.c,mycp.h。要求在命令行输入两个文件名即可实现把第一个文件复制到第二个新文件中,其中mycp函数的要求是mycp(char *src, char *dest),*src是源文件路径,*dest是目标文件路径。
知识点:
1.Linux系统编程之错误处理:perror , strerror 和 errno
首先是errno,在系统编程中错误通常通过函数返回值来表示,并通过这个特殊变量errno来描述。errno这个全局变量在errno.h头文件中声明如下:extern int errno;
查看错误代码errno是调试程序的一个重要方法。当linuxC api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因。
2.错误处理函数
(1) 函数 void perror(const char *s);
函数说明:perror()用来将上一个函数发生错误的原因输出到标准错误(stderr)。参数s所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno的值来决定要输出的字符串。
例子:
#include
main()
{
FILE *fp;
fp = fopen("/tmp/jiege","r+");
if(fp == NULL)
perror("open failed!");//类似于fputs("open failed!",stderr)
}
执行:
$ ./perror
open failed! : No such file or diretory
报错提示字符串里的内容加冒号:错误信息,这里报错是因为找不到文件或目录。
(2) 函数char * strerror(int errnum);
函数说明:strerror()用来依参数errnum的错误代码来查询其错误原因的描述字符串,然后将该字符串指针返回。
返回值:返回描述错误原因的字符串指针。
/* 显示错误代码0 至9 的错误原因描述*/
#include
#include
main()
{
int i;
for(i=0;i<10;i++)
printf("%d : %s\n",i,strerror(i));
}
执行:
0 : Success
1 : Operation not permitted
2 : No such file or directory
3 : No such process
4 : Interrupted system call
5 : Input/output error
6 : Device not configured
7 : Argument list too long
8 : Exec format error
9 : Bad file descriptor
3.函数void * memset (void *s ,int c, size_t n):将一段内存空间填入某值
函数说明:memset()会将参数s所指的内存区域前n个字节以参数c填入,然后返回指向s的指针。在编写程序时,若需要将某一数组作初始化,memset()会相当方便。
返回值:返回指向s的指针。
附加说明:参数c虽声明为int, 但必须是unsigned char ,所以范围在0到255之间。
#include
#include
main()
{
char s[30];
memset (s,'A',sizeof(s));
s[30]='\0';
printf("%s\n",s);
}
执行:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
4.write()函数
表头文件:unistd.h
函数原型:ssize_t write (int fd,const void * buf,size_t count);
函数说明:write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。当然,文件读写位置也会随之移动。
返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。
5.read()函数
表头文件:unistd.h
函数原型:ssize_t read(int fd,void * buf ,size_t count);
函数说明:read()会把参数fd所指的文件传送count个字节到buf指针所指的内存中。若参数count为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动。
返回值:当有错误发生时则返回-1,错误代码存入errno中,而文件读写位置则无法预期。
6.sizeof()是一个计算指定数据类型长度的运算符,如sizeof(int)计算一个整型数据所占用的存储空间。
7.open()、creat()函数:创建和打开文件
表头文件:包含sys/types.h,sys/stat.h,fcntl.h
定义函数:
int open( const char * pathname, int flags);
int open( const char * pathname,int flags, mode_t mode);
int creat(const char * pathname,mode_t mode);
函数说明:参数pathname 指向欲打开的文件路径字符串。flags和mode都是一组掩码的合成值,flags表示打开或创建的方式,mode表示文件的访问权限。对于open()函数来说,第三个参数mode仅当创建新文件时(即使用了O_CREAT时)才使用,即只有在建立新文件时才生效,用于指定文件的访问权限位。如果打开一个已有的文件,但是又使用第三个参数指定了权限,那第三个参数将不会生效,等于没有。flags的可选项比如有:
O_RDONLY ——以只读方式打开文件
O_WRONLY ——以只写方式打开文件
O_CREAT ——若欲打开的文件不存在则自动建立该文件。
O_TRUNC ——若文件存在并且以可写的方式打开时,此旗标会令文件长度清为0,而原来存于该文件的资料也会消失。
create()函数等价于open(pathname,O_CREAT | O_TRUNC | O_WRONLY,mode)。
返回值:open()和creat()函数出错时返回-1,成功则返回一个整数,这个整数就是和这个打开的文件相对应的文件描述符,通过这个返回的文件描述符我们可以对这个文件进行读写等各种操作。
参数mode 则有下列数种组合,只有在建立新文件时才会生效,此外真正建文件时的权限会受到umask值所影响,因此该文件权限应该为(mode-umaks)。
如:0755->即用户具有读/写/执行权限,组用户和其它用户具有读写权限;
关于权限怎么回事可以看我上一篇博客:
http://blog.csdn.net/lxjstudyit/article/details/63681247
8.int close(int fd):关闭文件
表头文件:unistd.h
函数说明:当使用完文件后若已不再需要则可使用close()关闭该文件,close()会让数据写回磁盘,并释放该文件所占用的资源。参数fd为先前由open()或creat()所返回的文件描述词。
返回值:若文件顺利关闭则返回0,发生错误时返回-1。
有了上述知识点储备后,接下来开干:
打开vim,首先编写mycp.h
#include
#include
#include//包含exit
#include
#include
#include
#include//用perror输出错误
#include
int mycp(char *src,char *dest);//函数声明
mycp.c
#include "mycp.h"
int mycp(char *src,char *dest)
{
char buff[512] = {0};
int fd0 = open(src,O_RDONLY);
int fd1 = creat(dest,0755);//创建文件(起初b.txt并不存在)
if(fd0 < 0 || fd1 < 0)
{
perror(strerror(errno));
exit(-1);
}
int len = 0;
while((len = read(fd0, buff, sizeof(buff))) > 0){
if(write(fd1, buff, len) != len){
perror(strerror(errno));
exit(-1);
}
memset(buff, 0, sizeof(buff));
}
close(fd0);
close(fd1);
}
main.c
#include "mycp.h"
int main(int argc,char *argv[])
{
if(argc!=3)//保证程序的健壮性
{
printf("please try to input two files!\n");
exit(1);
}
mycp(argv[1], argv[2]);
return 0;
}
最后一个main.c,按通常C语言应该是main(),当然我们可以设为main()再改动,但是这里要实现一个命令行输入的功能,所以要用带参数的main()函数,下面来看一个例子:
下面的程序演示argc和argv的使用:
#include
int main(int argc, char * argv[])
{
int i;
for (i=0; i < argc; i++)
printf("Argument %d is %s.\n", i, argv[i]);
return 0;
}
假如上述代码编译为test.exe,那么运行
./test.exe aa bb cc
将得到
Argument 0 is test.exe.
Argument 1 is aa.
Argument 2 is bb.
Argument 3 is cc.
如果运行
./test.exe shaoguan
将得到
Argument 0 is test.exe.
Argument 1 is shaoguan.
所以,就能得到结论:argc是一个整型,指的是往主函数传入字符串的个数,传入进来的argv[]中的所有单个字符构成一个argv[]数组。
搞定这些后,就可以开始编译运行了,首先通过ls命令看我的文件,a.txt里面也有内容。
接下来通过gcc编译,通过-o将a.out作为输出文件,然后运行a.out将a.txt复制到b.txt文件里面,接着用ls命令查看目录,创建了一个新文件b.txt,再用cat查看b.txt的内容,与a.txt一样,说明复制成功!
那么main.c的健壮性是有啥用呢,主要是提示输入错误
比如
你输入的参数没有三个的情况提示你应该输入两个文件名。
这样程序就完成了,可以实现任一文件的内容复制到新文件的中。
当然,Linux下,以上实现的复制功能都没有一行cp命令来得快。。。。。。