1. 字符读取与行读取
下面的演示程序实现从/etc/passwd文件中提取用户名,打印到屏幕上并保存在copyname.txt文件中 使用的函数是getc()、putc()、putchar()
#include <stdio.h>
int main()
{
FILE *fpr, *fpw;
int c = 0, f = 0;
/* 以下打开源文件 */
if((fpr = fopen("/etc/passwd", "r")) == NULL)
{
printf("open file /etc/passwd failes.\n");
return;
}
/* 以下打开目标文件*/
if((fpw = fopen("./copyname.txt", "w")) == NULL)
{
printf("open file ./copyname.txt failed.\n");
fclose(fpr);
return;
}
while((c = getc(fpr)) != EOF)
{
/* 字符已经读取到了c */
if(f == 0)
{
if(c != ':')
putchar(putc(c, fpw));
else
f = 1;
}
else if(c == '\n')
{
f = 0;
putchar(putc(c, fpw));
}
}
fclose(fpr);
fclose(fpw);
return 0;
}
下面这个演示程序是按行来获取数据的
#include <stdio.h>
int main()
{
FILE *fpr, *fpw;
char buf[1024], *p1, *p2;
/* 打开源文件 */
if((fpr = fopen("/etc/passwd", "r")) == NULL)
{
printf("open /etc/passwd file failed.\n");
return;
}
/* 打开目标文件 */
if((fpw = fopen("./copynameid.txt", "w")) == NULL)
{
printf("open ./copynameid.txt failed.\n");
fclose(fpr);
return;
}
memset(buf, 0, sizeof(buf));
while(fgets(buf, sizeof(buf), fpr) != NULL)
{
/* p1指向第一个":",p2指向第二个":" */
if((p1 = strstr(buf, ":")) == NULL) break;
if((p2 = strstr(p1+1, ":")) == NULL) break;
p1++; p2++;
/* p1指向第二个域密码字段,p2指向第三个域用户ID字段 */
/* 以下代码移动字符串内容,将ID字段的内容移动到用户名字段后 */
while(*p2 != ':')
{
*p1 = *p2;
p1++; p2++;
}
*p1 = 0;
/* 屏幕输出 */
puts(buf);
/* 文件输出 */
fputs(buf, fpw);
fputs("\n", fpw);
/* 清楚内存 */
memset(buf, 0, sizeof(buf));
}
fclose(fpr);
fclose(fpw);
return 0;
}
输出省略。这个程序很有意思的地方是在buf中利用指针直接判断并修改数据
2. 环境变量的查询与修改
每个程序中都维护一个指向环境变量的指针char **environ; 子进程会从父进程继承环境变量。子进程环境变量的修改不一定会影响父进程 无关的多个进程之间修改环境变量不会互相影响
打印环境变量
#include <stdio.h>
extern char **environ;
int main()
{
while(*environ)
{
printf("%s\n",*environ++);
}
return 0;
}
查询环境变量
多数时候,只是查看一个环境变量的值。可以使用 char * getenv(const char *name);函数
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *path=getenv("PATH");
printf("path=%s\n",path);
return 0;
}
这个和shell中用echo $PATH打印出来的效果是一样的
设置环境变量
putenv 定义函数 int putenv(const char * string); 表头文件 #include 函数说明 putenv()用来改变或增加环境变量的内容。 参数 string的格式为name=value,如果该环境变量原先存在,则变量内容会依参数string改变,否则此参数内容会成为新的环境变量。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *HELLO;
putenv("HELLO=hello");
HELLO=getenv("HELLO");
printf("HELLO=%s\n",HELLO);
return 0;
}
修改环境变量
修改PATH环境变量加上HOME目录,把修改后的环境变量打印出来。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *path=getenv("PATH");
char *home=getenv("HOME");
int n=strlen(path)+strlen(home);
char *str=malloc(n+2);
sprintf(str,"%s:%s",home,path);
printf("str=%s\n",str);
setenv("PATH",str,1);
path=getenv("PATH");
printf("new path=%s\n",path);
free(str);
return 0;
}
setenv与putenv区别
函数定义:int putenv(const char * string); putenv()用来改变或增加环境变量的内容。 参数string的格式为name=value,如果该环境变量原先存在,则变量内容会依参数string改变,否则此参数内容会成为新的环境变量。 返回值:执行成功则返回0,有错误发生则返回-1。
函数定义:int setenv(const char *name,const char * value,int overwrite); setenv()用来改变或增加环境变量的内容。参数name为环境变量名称字符串。 参数value则为变量内容,参数overwrite用来决定是否要改变已存在的环境变量。如果overwrite不为0,而该环境变量原已有内容,则原内容会被改为参数value所指的变量内容。如果overwrite为0,且该环境变量已有内容,则参数value会被忽略。 返回值:执行成功则返回0,有错误发生时返回-1。
3. 输入输出重定向(标准IO)
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char szBuf[100];
/* 将屏幕标准输出的内容重定向到文件 */
if((fp = freopen("./1", "w", stderr)) == NULL)
{
perror("freopen");
return EXIT_FAILURE;
}
/* stderr已经输出重定向,所有的错误输出都将写入文件./1中 */
fputs("I like linux.", stderr);
/* 关闭文件 */
fclose(fp);
/* 将标准输入由键盘输入更改为从文件./1中读入*/
if((fp = freopen("./1" ,"r", stdin)) == NULL)
{
perror("freopen");
return EXIT_FAILURE;
}
memset(szBuf, 0, sizeof(szBuf));
/* stdin已经输入重定向, 所有内容都将写入文件./1中 */
fgets(szBuf, sizeof(szBuf), stdin);
printf("szBuf = [%s]\n", szBuf);
/* 关闭文件 */
fclose(fp);
return 0;
}
上面的程序摘自《精通Unix下C语言编程与项目实践》 运行结果:
[sincerefly@localhost UC]$ ./a.out
szBuf = [I like linux.]
[sincerefly@localhost UC]$
也就是说,通过使用标准库中的freopen函数实现了输入输出的重定向 再来多看看两个例子,来自freopen函数的百度百科 举例1:
#include <stdio.h>
int main()
{
/* redirect standard output to a file */
if (freopen("D:\\OUTPUT.txt", "w", stdout)==NULL)
fprintf(stderr, "error redirecting stdout\n");
/* this output will go to a file */
printf("This will go into a file.");
/* close the standard output stream */
fclose(stdout);
return 0;
}
举例2:
#include <stdio.h>
int main()
{
int i;
if (freopen("./OUTPUT.txt", "w", stdout)==NULL)
fprintf(stderr, "error redirecting\stdout\n");
for(i=0;i<10;i++)
printf("%3d",i);
printf("\n");
fclose(stdout);
return 0;
}
从文件in.txt中读入数据,计算加和输出到out.txt中
举例3:
#include <stdio.h>
int main()
{
freopen("in.txt","r",stdin); /*如果in.txt不在连接后的exe的目录,需要指定路径如./in.txt*/
freopen("out.txt","w",stdout);/*同上*/
int a,b;
while(scanf("%d%d",&a,&b)!=EOF)
printf("%d\n",a+b);
fclose(stdin);
fclose(stdout);
return 0;
}
若要返回到显示默认 stdout) 的 stdout,使用下面的调用: freopen( “CON”, “w”, stdout ); //输出到控制台”CON” 检查 freopen() 以确保重定向实际发生的返回值。 下面是短程序演示了 stdout 时重定向
举例4:
/*Compile options needed: none*/
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
FILE *stream ; //将内容写到file.txt, "W"是写 ("r"是读)
if((stream = freopen("file.txt", "w", stdout)) == NULL)
exit(-1);
printf("this is stdout output\n");
stream = freopen("CON", "w", stdout);/*stdout 是向程序的末尾的控制台重定向*/
printf("And now back to the console once again\n");
}
4. 输入输出重定向(系统调用)
在Unix下,系统重定向是使用dup和dup2函数完成的
在学习使用这两个函数之前,必须要搞懂一个概念就是文件描述符
摘自:《文件描述符和文件指针的区别》
文件描述符就是open文件时产生的一个整数,直到一个索引作用,它用于UNIX系统中,用于标识文件。
文件指针是指向一个FILE的结构体,这个结构体里有一个元素就是文件描述符。它用于ANSI C标准的IO库调用中,用于标识文件。
既然FILE中包含文件描述符元素,可以用fopen()直接获取指针fp,然后使用fp获得fp中所包含文件描述符fd的信息。
文件描述符应该是唯一的,但文件指针(值)却不是唯一的,但指向的对象却应该是唯一的。
FILE 中除了包含了fd信息,还包含了IO缓冲,所以可以理解为FILE是对fd的墙头,是C标准形式,所以FILE 比fd更适合跨平台,应该多用fopen在,少用open。
C语言文件指针与文件描述符之间可以相互转换:
int fileno(FILE *stream);
FILE fdopen(int fd, const char mode);
来看一下下图的实现过程
简单明了,不解释过多。再来看一下程序: 结合上图理解一下过程。
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int fd, save_fd;
char msg[] = "This is a test\n";
fd = open("somefile", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
if(fd<0) {
perror("open");
exit(1);
}
save_fd = dup(STDOUT_FILENO);
dup2(fd, STDOUT_FILENO);
close(fd);
write(STDOUT_FILENO, msg, strlen(msg));
dup2(save_fd, STDOUT_FILENO);
write(STDOUT_FILENO, msg, strlen(msg));
close(save_fd);
return 0;
}
上面的程序按照流程图就很容易理解了,再多说两句就是STDOUT_FILENO也就是“1”的文件描述符 再有就是当改变输出方向后,不仅仅write函数,使用printf函数结果也都是一样的……haha
5. 通过fwrite()和write()比较标准库函数和系统调用的速度
fwrte是C标准库中提供的函数,是对write函数的扩展与封装,write则是Unix系统提供的函数。按照常理来讲,系统调用肯定比使用库快的多,但是事实正好相反 Why?原因就在于缓冲的问题,fwite会在内存中开辟缓冲区,来避免频繁的I/O,所以速度比系统调用要快(更多比较“open/read/write和fopen/fread/fwrite的区别”)
为了直观的比较一下fwrite和write的速度。我们来做一个简单的测试:
fwrite.c
#include <stdio.h>
int main(void)
{
FILE *stream;
int i;
char str[] = "qwertyuiop\n";
if ((stream = fopen("fwrite.txt", "w")) == NULL)
{
fprintf(stderr, "Cannot open output file.\n");
return 1;
}
for(i=0; i<100000; i++)
{
fwrite(str, sizeof(str), 1, stream);
}
fclose(stream); /*关闭文件*/
return 0;
}
运行时间:
real 0m0.009s
user 0m0.003s
sys 0m0.005s
write.c
#include <stdio.h>
#include <fcntl.h>
int main(void)
{
int fd;
int i;
char str[] = "qwertyuiop\n";
if ((fd = open("write.txt", O_RDWR|O_CREAT|O_TRUNC, 00664)) < 0)
{
fprintf(stderr, "Cannot open output file.\n");
return 1;
}
for(i=0; i<100000; i++)
{
write(fd, str, sizeof(str));
}
close(fd); /*关闭文件*/
return 0;
}
运行时间:
real 0m0.189s
user 0m0.008s
sys 0m0.181s
果然,从程序运行时间上看,使用标准库函数速度比直接系统调用快了很多 从程序的可移植性角度来讲,使用标准库函数也是一个好的习惯 而系统调用则是用在和系统关联度很高的地方
2016-03-27 补充
iSpeller 2014年2月26日13:27 对通过fwrite()和write()比较标准库函数和系统调用的速度
的评论:
这样应该不算比较公平的比较吧,系统调用需要切换到内核态自然比较慢,其实除了切换速度的比较,还应该比较下执行的效率,例如一次性(而不是少量多次)读入20MiB 的数据,看看谁更快些。