Posts Tagged ‘文件操作’

丰富你的ls(updating……)

3 7 月, 2010

如果我们已经对my_ls.c的结构有了深刻了解,以及对此程序中所涉及系统调用函数都已理解,那么不妨继续增添选选型,让你的ls命令程序不断完整。正如你所理解的那样,前面我们实现的my_ls.c属于一个“插座”,要实现其他选项,基本上都是在my_ls.c源码中添加新代码。我们将不断完善选项功能的那个源码称为my_ls_plus.c。

1.增加-i选项

ls -i可以显示出每个文件的i节点编号。具体的实现办法是利用stat族函数,将文件的属性返回到类型为struct stat的参数buf中,然后在需要的位置打印st_ino字段即可。注意st_ino打印的时候需要长整型。

获取文件属性举例:

if(lstat(pathname,&buf)==-1)
	{
		my_err("lstat",errno,__LINE__);
	}

打印举例:

if(if_i)
	{
		printf("%ld ",buf.st_ino);
	}

2.增加-t选项

ls -t 按每个文件最后一次被修改的时间(st_mtime)排列显示文件。这里对文件属性中三个关于时间的字段做以说明。

st_atime:文件最后一次被访问(access)的时间。这个好理解。

st_mtime:文件最后一次被修改(modification)的时间。

st_ctime:文件字后一次被更改(create)的时间。

mtime和ctime其实都是修改时间,但是前者指文件内容被修改后的时间,而后者更倾向于文件所有者、所属组以及文件权限等属性被更改后的时间。当然如果文件内容修改,两者的时间都会被修改。我们一般用ls -l所显示的时间指mtime,而要显示出ctime则可以用: ls -lc。显示出atime则可以用:ls -lu。

因此,要实现ls -t命令的作用,我们必须对文件按照mtime进行排序。我们可以设置全局变量if_t来标记是否出现了 -t选项。如果出现则调用按照时间排序的函数sort_by_mtime(char filename[][PATH_MAX+1],int count);此函数的大致流程是:首先利用stat族函数获取每个文件的mtime,存于字符串数组mtime[256][20]中;再利用某种排序算法排序即可。如果你对C语言基本知识掌握的还算可以,那么要实现上述过程并不困难。

获取文件的mtime示例代码如下:(由于代码插件问题,请忽略<后面的‘\’)

for(i=0;i<\count;i++)
	{
		if(stat(filename[i],&buf)==-1)
		{
			my_err("stat",errno,__LINE__);
		}
		strcpy(buf_time,ctime(&buf.st_mtime));
		buf_time[strlen(buf_time)-1]='\0';
		convert_time(mtime[i],buf_time);
	}

下面的排序代码采用选择排序法进行排序,注意我们在比较的时候必然是对时间进行比较,而在交换两个数据的时候,必须对文件名和时间一起交换。当初我只交换了文件名,找了好久的”错误”。

//sorting
	for(i=0;i<\count;i++)
	{
		k=i;
		for(j=i;j<\count;j++)
               {
                        if(strcmp(mtime[j],mtime[k])>\0) k=j;
		}
		if(k!=i)
		{
			//exchange mtime
			strcpy(temp,mtime[i]);
			strcpy(mtime[i],mtime[k]);
			strcpy(mtime[k],temp);
			//exchange filename
			strcpy(temp,filename[i]);
			strcpy(filename[i],filename[k]);
			strcpy(filename[k],temp);
		}
	}

这样,你就可以实现-t选项的功能。

3.增加-A选项

这个选项理解比较简单。我们知道ls -a是显示包括隐藏文件在内的所有文件。而ls -A则显示除了.和..之外的所有文件。我们知道在my_ls.c中void display(int flag,char* pathname)是根据flag对单个文件pathname进行处理,根据flag的情况来判断是以什么方式显示所有文件名。因为我们可以在此基础上加入对-A选项的判断:

if((if_A&&!strcmp(pathname,"."))||(if_A&&!strcmp(pathname,"..")))
	{
		//blank sentence,do not list this file
	}
	else
	{
		//省略switch语句
	}

这样就可以实现-A选项了。

4.增加-B选项

如果你完成了-A选项,那么此选项对你来说轻而易举。-B选项是不显示以~结尾的备份文件。那么只要在-A选项的基础上修改源代码,示例如下:

if((if_A&&!strcmp(pathname,"."))||(if_A&&!strcmp(pathname,"..")))
	{
		//blank sentence,do not list this file
	}
	else if(if_B&&pathname[strlen(pathname)-1]=='~')
	{
		//blank sentence,do not list this file
	}
	else
	{
		//switch语句省略
        }

5.增加-d选项

-d选项只会显示当前目录(.),而不是目录下的文件。如果加入-l则会显示当前目录的各种属性。前面的-B等选项我们在添加的时候都是在display函数中添加某些代码,因为最终结果还要与-a和-l选项相结合。比如ls -laA,虽然不显示.和..,但是还是要显示以.开始的其他文件。而-d选项则不用看-a的“脸色‘。因为不论你是否加入-a选项,此选项只会显示当前目录。如下:

edsionte@edsionte-laptop:~/code/file$ ls -laAd
drwxr-xr-x 2 edsionte edsionte 4096 2010-07-06 14:50 .
edsionte@edsionte-laptop:~/code/file$ ls -ld
drwxr-xr-x 2 edsionte edsionte 4096 2010-07-06 14:50 .
edsionte@edsionte-laptop:~/code/file$ ls -d
.

因此关于此选项的代码应添加在display_dir函数中,具体位置如下:

//if the command include '-d'
	if(if_d)
	{
		if(stat(".",&buf)<\0)
		{
			my_err("stat",errno,__LINE__);
		}
		if(flag_param==PARAM_L||flag_param==PARAM_L+PARAM+A)
			display_attribute(buf,".");
		else
			display_single(buf,".");
	}
	else
	{
		for(i=0;i<\count;i++)
        	{
			display(flag_param,filename[i]);
        	}
	}

这样添加即可,通过以上几个选项的添加,你应该大致了解应该在何处添加代码了。

待续。。

creat只能以只写方式打开文件

1 7 月, 2010

在《linuxC编程实战》书中,有一个my_rwl.c的小程序(详见P151):首先利用open函数或者creat函数创建一个文件,利用write函数将数据写入文件,再利用read函数读出文件中数据;用lseek移动文件指针,再写入相同的数据,最终读出所有数据。创建代码如下:

// if((fd=creat("example_63.c",S_IRWXU))==-1)
	if((fd=open("example_63.c",O_CREAT|O_TRUNC|O_RDWR,S_IRWXU))==-1)
	{
		my_err2("creat",errno,__LINE__);
	}

在调试此程序的时候,总是出现这样的情况:利用open函数创建文件的时候,一些正常。利用creat函数创建文件的时候,读出的数据总是乱码,并且提示读写失败的错误信息。在自己寻求结果无望的时候,在chinaUnix论坛得到了帮助:creat只能以只写方式打开文件。其实书上有这句话,可是在我寻求上述错误答案的时候,也没有注意到它的存在。而且我还有有这样的错误概念:即便creat函数只能以只写方式打开文件,可是我创建的时候有S_IRWXU参数,那么它便可以写了。

那么,让我重新理解它:

1.文件的打开方式是指文件以只读、只写、可读写那种方式打开,返回的文件描述符就可以对此文件进行相应的读写操作。比如上述的creat函数对example_63.c以只写方式打开,那么就只能对这个文件进行写,因此后续的读操作都失败。而open创建文件的时候,有O_RDWR参数,因此可以成功对文件读写。

2.文件的操作权限是指系统中各用户对此文件的使用权限,依参数而定。

3.以创建的角度来看,creat和open的作用相同。但以打开文件的方式来看,前者只能以只写方式,后者由于参数的关系,以可读写方式打开。

如果要解决上述的错误,那么可以在creat创建完文件后,关闭文件,再以可读写的方式,open这个文件。这么做是很麻烦,但是这里我们为了搞清楚两者的区别,也无所谓了。

把错误捕获函数放入error.h

22 6 月, 2010

之前我写过一个关于linux下C编程中错误捕获函数的文章,即我们可以自己编写一个函数来捕获一些错误,让程序员处理错误的时候更简单。如果将这个函数放入error.h头文件中,那么以后就可以直接调用了。本文便是和大家一起讨论如何将自己编写的错误捕获函数放入头文件error.h中。

头文件放在/usr/include目录下,进入这个目录,然后用vi编辑器打开error.h文件。要添加的两个函数如下:

void my_err1(int error)
{
	printf("error:  %s with errno: %d\n",strerror(error),error);
	exit(1);
}
void my_err2(const char* err_string,int line,int error)
{
	printf("error:  line:%d %s():%s with errno:%d\n",line,err_string,strerror(error),error);
        exit(1);
}

将上述两函数添加至文件末尾。由于strerror()函数涉及到头文件string.h,所以在已打开的error.h文件中还得添加string.h头文件。具体添加位置可参考下面:

 20 #ifndef _ERROR_H
 21 #define _ERROR_H 1
 22
 23 #include "features.h"
 24
 25 //personal addition
 26 #include "string.h"
 27 //personal addition end
 28
 29 __BEGIN_DECLS
 30

 59 //personal addition
 60 //brief function
 61 void my_err1(int error)
 62 {
 63                 printf("error:  %s with errno: %d\n",strerror(error),error);
 64                 exit(1);
 65 }
 66
 67 //detailed function
 68 void my_err2(const char* err_string,int line,int error)
 69 {
 70                 printf("error:  line:%d %s():%s with errno:%d\n",line,err_string,strerror(error),error);
 71                 exit(1);
 72 }
 73 //persoanl addition end
 74 __END_DECLS
 75
 76 #endif /* error.h */

其中my_err1和my_err2显示的错误信息稍有不同。添加好后就可以保存了。但是现在问题来了:按照平时我们的保存方法:wq保存时,会提示:E45: 已设定选项 ‘readonly’ (请加 ! 强制执行)。这是因为头文件属于系统文件,linux为了安全起见,不允许普通用户对其进行修改。我们可以用ls -l查看其属性:

edsionte@edsionte-laptop:/usr/include$ ls -l error.h
-rw-r--r-- 1 root root 2557 2010-06-23 12:11 error.h

从上面的文件存取权限可以看出,root用户可以对其读写,root所在的root组可对其读,其他组用户只可对其读。现在我们更改这个文件的权限,让非root用户可对其修改。

edsionte@edsionte-laptop:/usr/include$ sudo chmod 777 error.h
[sudo] password for edsionte:
edsionte@edsionte-laptop:/usr/include$ ls -l error.h
-rwxrwxrwx 1 root root 2557 2010-06-23 12:11 error.h

修改好权限后,就可以用上面的方法对error.h文件进行修改并保存。
下面进行测试:
测试源码如下。

#include “errno.h”
#include “fcntl.h“
#include "unistd.h”
#include "stdlib.h“
#include "stdio.h”
#include ”sys/types.h“
#include “sys/stat.h”
#include ”string.h“
#include “error.h”

int main()
{
	int fd;
	if((fd=open("example_test.c",O_CREAT|O_EXCL,S_IRUSR|S_IWUSR))==-1)
	{
		my_err1(errno);
//		my_err2("open",__LINE__,errno);
	}
	close(fd);
	return 0;
}
}

首先测试my_err1,结果如下:

edsionte@edsionte-laptop:~/code$ ./my_error
error:  File exists with errno: 17

然后将my_err1注释掉,测试my_err2,结果如下:

edsionte@edsionte-laptop:~/code$ ./my_error
error:  line:17 open():File exists with errno:17

结果和前面介绍捕获函数时的测试结果一样。你以为现在就结束了吗?当然没有,我们要把error.h头文件的权限修改成原样。

edsionte@edsionte-laptop:/usr/include$ sudo chmod 644 error.h
[sudo] password for edsionte:
edsionte@edsionte-laptop:/usr/include$ ls -l error.h
-rw-r--r-- 1 root root 2557 2010-06-23 12:50 error.h

大功告成。

实现自己ls命令(下)

21 6 月, 2010

在本文上部分中,主要陈述my_ls.c的大致结构,本文主要来分析my_ls中主函数是如何实现的。

(1)进入main函数。首先对命令行参数进行解析,即提取命令行参数中‘-’后的选项。用户的输入有多样性,如ls -l -a;ls -la。我们用两层循环类来解析参数,外层循环对argv[]数组中的元素依次进行内层循环的解析,二层循环对以‘-’为首的字符串进行选项提取,并把每个选项存于param[]数组当中,用num来记下‘-’的数目,以备后用。而命令行参数中的总选项数目则用j计数。实现代码如下:

     j=0;
	num=0;
	for(i=1;i<\argc;i++)
	{
		if(argv[i][0]=='-')
		{
			for(k=1;k<\strlen(argv[i]);k++)
			{
				param[j++]=argv[i][k];
			}
			num++;//count the number of '-'
		}
	}

接下来,我们来检查刚刚提取的选项是否合法。本程序我们只支持选项-l和-a,因此如果我们增添其他选项,只要适当添加代码即可。并且我们用或运算记录参数,以备后用。最后为选项数组的末尾元素赋‘\0’。

//check the argument because of only supporting -a and -l
	for(i=0;i<\j;i++)
	{
		if(param[i]=='a')
		{
			flag_param|=PARAM_A;
			continue;
		}
		else if(param[i]=='l')
		{
			flag_param|=PARAM_L;
			continue;
		}
		else
		{
			printf("error:my_ls invalid option -%c\n",param[i]);
			exit(1);
		}
	}
	param[j]='\0';

由上面我们所知num记录的是参数中‘-’的数量,因此如果num+1==argc,那说明用户所输入的命令行参数不包含目录或文件名。只是显示当前目录下的文件。因为这是我们必须自动将path赋值为当前目录。为了使其称为一个字符串,必须在末尾加‘\0’。然后进入display_dir函数。实现代码如下:

//print the information of  current directory if the command without the name of target file and current directory
        if((num+1)==argc)
	{
		strcpy(path,"./");
		path[2]='\0';
		display_dir(flag_param,path);
		return 0;
	}

如果命令行参数包含目录或者文件名,那么我们要检查其合法性(参数中的目录或者文件是否存在)。这里我们利用stat族函数来获取文件的属性,实现上述功能。stat族函数通常有两个参数:文件路径/文件描述符,struct stat *buf类型的结构体。如果操作成功,那么buf将保存文件的属性。若合法,利用宏S_ISDIR(buf.st_mode);判断此文件是否为目录文件。若为目录文件则进入display_dir函数,否则进入display函数。通常情况,display_dir函数是获取path目录下所有文件的完整路径名,在使每个文件执行dsiplay函数。因此如果参数中是指定的文件名,则可绕过display_dir函数,直接进入display函数。

        i=1;
	do
	{
		//the current argument doesn't comprise the target file name and dirctory name
		if(argv[i][0]=='-')
		{
			i++;
			continue;
		}
		else
		{
			strcpy(path,argv[i]);
			//detect if the "path" exsit
			if(stat(path,&buf)==-1)
			{
				my_err("stat",errno,__LINE__);
			}
			//detect the "path" is a file or a directory
			if(S_ISDIR(buf.st_mode))
			{
				//directory
				//detect if the last character of the "path" is '/'
				if(path[strlen(path)-1]=='/')
				{
					path[strlen(path)]=='\0';
				}
				else
				{
					path[strlen(path)]='/';
					path[strlen(path)]='\0';
				}
				display_dir(flag_param,path);
				i++;
			}
			else
			{
				//file
				display(flag_param,path);
				i++;

			}
		}

	}while(i<\argc);

实现自己的ls命令(上)

19 6 月, 2010

如果你跟我一样是linux下C编程的初学者,那么动手实现一些linux命令是十分有必要的。本文为你所描述的是常用ls命令。ls命令有众多选项,本文中所描述的my_ls.c程序仅实现了-l和-a选项。

ls命令加入-l选项可以使每个文件单独成一行,并且显示文件的属性。比如:

edsionte@edsionte-laptop:~/code$ ls -l
总用量 232
-rwxr-xr-x 1 edsionte edsionte  9530 2010-06-18 11:26 error
-rw-r--r-- 1 edsionte edsionte   756 2010-06-19 14:39 error.c
-rw-r--r-- 1 edsionte edsionte   755 2010-06-18 11:32 error.c~
-rw------- 1 edsionte edsionte     0 2010-06-16 12:40 example_62.c

ls命令加入-a选项可以显示隐藏文件。linux中隐藏文件是以 . 开头的。比如:

edsionte@edsionte-laptop:~/code$ ls -a
.             example_65.c    my_cdvc       my_mv
..            example_68_1.c  my_cdvc.c     my_mv.c

.表示当前目录,..表示当前目录的父目录。

当然这两个选项可以同时使用,比如:

edsionte@edsionte-laptop:~/code$ ls -al
总用量 240
drwxr-xr-x  3 edsionte edsionte  4096 2010-06-20 12:10 .
drwxr-xr-x 46 edsionte edsionte  4096 2010-06-20 12:10 ..
-rwxr-xr-x  1 edsionte edsionte  9530 2010-06-18 11:26 error
-rw-r--r--  1 edsionte edsionte   756 2010-06-19 14:39 error.c
-rw-r--r--  1 edsionte edsionte   755 2010-06-18 11:32 error.c~
-rw-------  1 edsionte edsionte     0 2010-06-16 12:40 example_62.c
edsionte@edsionte-laptop:~/code$ ls -l -a
总用量 240
drwxr-xr-x  3 edsionte edsionte  4096 2010-06-20 12:10 .
drwxr-xr-x 46 edsionte edsionte  4096 2010-06-20 12:10 ..
-rwxr-xr-x  1 edsionte edsionte  9530 2010-06-18 11:26 error
-rw-r--r--  1 edsionte edsionte   756 2010-06-19 14:39 error.c

本文所述的my_ls.c程序就要实现这种功能。在了解本程序中所有函数之前,请先看一下本程序的流程图:点这里(本blog上传图片有点问题,正在解决中……)。

现在对本程序中的各个函数做以大致说明。

(1)void my_err(const char*,int,int);和void my_err2(const char*,int);

错误捕获函数。详细实现过程请点这里

(2)void display_dir(int flag_param,char*path);

如果命令中含有目录,则进入此函数。此函数将获取path目录下的文件总数以及所有文件名(包括隐藏文件)。流程图点这里

(3)void display(int flag,char*pathname);

pathname是一个文件的完整路径名,本函数首先从完整的路径名中解析出文件名,再根据flag进入不同的函数。流程图点这里

(4)void display_single(char*);

如果参数中不含任何选项或者仅含-a,则进入此函数。本函数直接显示出目录下的所有文件名,并且实现文件名左对齐。

(5)void display_attribute(struct stat,char*);

如果命令中含有-l则进入本函数,显示出文件的各种属性。

在本文的上班部分中主要为您理清本程序的大体结构,在下半部分中将详细分析源代码。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key