1 gets与fgets每行一次的IO
函数原型
char *gets(char *buf)
char *fgets(char *buf,int n,FILE *fp)
gets标准输入读,fgets从指定流中读。
gets是一个不推荐使用的函数,理由是可能造成缓冲区的溢出。
fgets从指定流中一直读到换行符为止(包括换行符),读入的数据放入buf中。缓冲区的数据以NULL字符结尾!也就是说会多写一个字符(NULL)。如果待读的数据比n要大,则只读取n-1个字节的数据,最后以NULL结尾,下次调用会继续读该行。
说明,因为读到的数据以NULL结尾,那么就可以使用strlen(buf)获得所读取的数据的大小。strlen接受一个char*类型的指针,计算出从这个地址开始直到NULL(即\0)的长度,不包括NULL。
2 puts与fputs
函数原型
int puts(const char *str)
int fputs(const chat *str,FILE *fp)
fputs将一个以NULL(即\0)字符终止的字符串写入指定的流,尾端的NULL不输出。
puts将一个以NULL结尾的字符串写到标准输出,终止符不写出。但是,与fputs不同的是,随后puts又将一个换行符写到标准输出。
puts()和 printf的用法一样,puts()函数的作用与语句“printf("%s\n",s);的作用相同。注意:puts在输出字 符串后会自动输出一个回车符。
**所以尽量配对的使用fgets和fputs!!!**尽量不用gets和puts
虽然这是每行IO,但是他的输出和行却一点关系没有,puts和fputs可以看作仅仅是一个字符串输出工具。
3 snprintf
int snprintf(char *str, size_t size, const char *format, ...);
将可变个参数(…)按照format格式化成字符串,然后将其复制到str中
(1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符(’\0’);
(2) 如果格式化后的字符串长度 => size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(’\0’)
函数返回值:若成功则返回欲写入的字符串长度,若出错则返回负值。
4 read读文件,结束符是什么,当read遇到‘\0’会怎么样 ?
注意:read和write是不区分’\0’的!他们读写只会按照给定的字节读写,没有结束标志符。停止读写的原因只能是缓冲区超出,或者缓冲区为零!总之不受’\0’的影响。
其实,可以把文件的内容看做是字符串!读写文件就是读写里面的字符(一个字节大小).
这里结合socket,给出一个实例,验证linux中输入输出函数的特性:
这个例子是回射程序,客户端在终端上中输出一行数据,然后服务器接受这个数据,再将其返回,客户端收到后再显示在终端上。
//echo_client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#define BUFSIZE 512
void str_cli(FILE * fp,int sockfd);
int main (int argc,char **argv)
{
int sd;
int n;
struct sockaddr_in servaddr;
char buf[BUFSIZ];
if (argc!=2)
{
printf("argc is not 2\n");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(2222);
if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<0)
{
perror("inet_pton error\n");
exit(1);
}
sd=socket(AF_INET,SOCK_STREAM,0);
if(connect(sd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
perror("connect is error\n");
exit(1);
}
str_cli(stdin,sd);
exit(0);
}
void str_cli(FILE * fp,int sockfd)
{
char sendline[BUFSIZE];
char receline[BUFSIZE];
char a;
int n,n2;
sendline[8]='8';
sendline[9]='9';
while(1)
{
fgets(sendline,BUFSIZE,fp);//输入123后回车,实际写入5个字节:"123\n\0",因为fgets不会丢弃换行符!
printf("strlen of senline is %ld",strlen(sendline));//结果是4,strlen遇到NUll就结束
printf("the neirong of sendline is %s",sendline);//此时可知sendline中的内容是:“123\n\0xxxxxxxx...” x指的是系统的随机数。,但这里输出是123\n,因为%s遇到NULL就结束
n=write(sockfd,sendline,strlen(sendline)+1);//写进去5个字节,也就是“123\n\0”,这里不能写strlen(senline),因为没有将结束符写进去,这样会导致后面服务器发来的消息也没有结束符,所以后面的fputs就会找不到在哪结束!!!
printf("the n write is %d",n);//n=5
n2=read(sockfd,receline,BUFSIZE);
printf("the num read n2 is %d",n2);
fputs(receline1,stdout);
}
return ;
}
//echo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>
#define BUFSIZE 1024
void str_echo(int confd);
int main()
{
int sd;
pid_t pid;
int confd;
socklen_t len;
time_t tick;
char buf[BUFSIZE];
char buf_addr[BUFSIZE];
struct sockaddr_in cliaddr,servaddr;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(2222);
//servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
inet_pton(AF_INET,"0.0.0.0",&servaddr.sin_addr.s_addr);//带或不带s_addr都可以
sd=socket(AF_INET,SOCK_STREAM,0);
if(sd<0)
{
perror("socket is error\n");
exit(1);
}
if(bind(sd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
{
perror("bind error\n");
close(sd);//实际上exit会自动关闭所有的文件描述符,所以不写这一步也可以
exit(1);
}
if(listen(sd,5)<0)
{
perror("listen is error\n");
close(sd);
exit(1);
}
while(1)
{
len=sizeof(cliaddr);
confd=accept(sd,(struct sockaddr *)&cliaddr,&len);
//打印对端客户端信息,IP地址和端口
printf("connection from :%s,port is %d\n", inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,buf_addr,sizeof(buf_addr)),ntohs(cliaddr.sin_port));
pid=fork();
if(pid==0)
{
close(sd);
str_echo(confd);
exit(0);
}
close(confd);
}
}
void str_echo(int confd)
{
int n;
char buf[BUFSIZE];
while(1)
{
n=read(confd,buf,BUFSIZE);//会读到5个字节,因为客户端只写入了5个字节,read读完缓冲区就返回。
printf("n is %d\n",n);//n=5
print("read byte is %s",buf);//此时buf中的内容会是“123\n\0xxxxxxxx...”,但是这一行的输出会是123\n,因为%s遇到NULL就停下
if(n<0 && errno==EINTR)
{
continue;
}
else if(n>0)
{
//write(confd,buf,BUFSIZE);//这个地方一定不能写入BUFSIZE个数据!!!,否则客户端的read会把这1024(BUFSIZE的大小)全部读出!!!
write(confd,buf,n);//这才是正确的,读到几个字节,返回几个字节
}
else
{
perror("read error:\n");
exit(1);
}
}
return ;
}
注意:
-
这里牵涉待的一个点就是,读的时候可以读BUFSIZE个字节的数据,但是写的时候最好加上结束符,这样的话,就是写入固定大小的数据,read不用读完BUFSIZE个字节就能读完缓冲区返回!
-
所以,以后规定,读可以读BUFSZIE,但是写的内容要严格规范,不能简单的写进BUFSZIE个字节,否则,read会没完没了的读!