一、printf函数输出问题。
1.printf函数的对象是谁?
printf()功能就是输出一个字符串到屏幕上,屏幕对象有两个,分别是标准输出、标准出错。
printf()其对象就是标准输出。
2.标准输出与标准出错有什么区别?
标准输出是有缓冲区的,其内容是先放在缓冲区,再输出。
标准出错是没有缓冲区的,其内容是直接输出。
3.研究一下printf()的输出问题。
例子1:
int main(int argc,char *argv[])
{
printf("helloworld");
printf("appletree");
while(1);
return 0;
}
执行结果:不会输出任何的内容,然后卡在死循环中。
分析: 因为printf函数是一个行缓冲区函数,所以必须要在标准输出缓冲区中遇到'\n'就可以输出东西。
例子2:
int main(int argc,char *argv[])
{
printf("helloworld");
printf("appletree\n");
printf("yueqian");
while(1);
return 0;
}
执行结果:输出helloworld和appletree,但是yueqian并没有输出。
分析:证明必须要遇到\n才可以将\n之前的全部东西输出。
例子3:
int main(int argc,char *argv[])
{
printf("helloworld");
printf("appletree");
return 0;
}
执行结果:输出helloworldappletree
分析:程序退出时,会刷新标准输出缓冲区。
例子4:
int main(int argc,char *argv[])
{
while(1)
{
printf("helloworld");
usleep(100000); //0.1s
}
return 0;
}
执行结果:等到缓冲区满了(1024B),就会将缓冲区中所有的数据全部输出。
分析:标准输出的缓冲区满了,即使没有'\n',程序也没有退出,也是会全部输出的。
例子5:
int main(int argc,char *argv[])
{
int x;
printf("helloworld");
printf("appletree");
scanf("%d",&x);
while(1);
return 0;
}
执行结果:输出helloworldappletree
分析:键盘输入的函数也会刷新printf的缓冲区。
例子6:主动刷新缓冲区 ---> fflush() --> man 3 fflush
功能: 刷新一个流。
fflush - flush a stream
#include <stdio.h>
int fflush(FILE *stream);
参数:
stream: 文件指针 如果要刷新标准输出缓冲区,这里填stdout。
返回值:
成功:0
失败:-1
int main(int argc,char *argv[])
{
printf("helloworld");
printf("appletree");
fflush(stdout); ---> 主动刷新标准输出缓冲区
while(1);
return 0;
}
例子7: 将printf的输出对象修改为标准出错,你想输出什么东西,就会马上输出。
修改输出对象: fprintf() --> man 3 fprintf
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
参数:
stream: 输出对象的文件指针。 stderr
format: 输出的内容。
返回值: 输出的字符个数
return the number of characters printed
printf("hello"); --> 默认的对象是标准输出
fprintf(stdout,"hello"); --> 将hello放在标准输出的缓冲区中,等价于上面那句话。
fprintf(stderr,"hello"); --> 将hello直接存放在标准出错中,而标准出错没有缓冲区的,所以会直接输出。
-------------------------------
int main(int argc,char *argv[])
{
//fprintf(stderr,"hello");
fprintf(stdout,"hello"); //等价于printf("hello")
while(1);
return 0;
}
-------------------------------
二、标准IO函数。
格式输入:scanf() fscanf() sscanf()
格式输出:printf() fprintf() sprintf() <按指定格式读写文件>
字符输入:getchar() getc() fgetc() <按字符读写文件> ---char
字符输出:putchar() putc() fputc()
字符串输入:gets() fgets() <按行读写文件>----string
字符串输出:puts() fputs()
1.sprintf() ---> (常用)保存一个字符串到一个缓冲区中,作用:拼接字符串。
#include <stdio.h>
int sprintf(char *str, const char *format, ...);
参数:
str: 保存字符串的缓冲区的地址。 (注意:必须要足够大接受数据)
format: 保存的内容。
返回值:
保存的内容的字符串长度。
例子1:
int a = 10;
printf("a = %d\n",a); //将a的值存放到%d的位置,然后将a=10这句话输出到标准输出缓冲区中。
例子2:
int a = 10;
fprintf(stderr,"a = %d\n",a); //将a的值存放到%d的位置,然后将a=10这句话输出到标准出错中。
例子3:
int a = 10;
char buf[100] = {0};
sprintf(buf,"a = %d\n",a); //将a的值存放在%d的位置,然后将a=10这句话保存到buf这个数组中。
说明:
sprintf 函数可以作为字符串拼接函数
练习1:
char buf1[] = "guoqing"
char buf2[] = "shuangwaiwai"
char buf3[1024];
使用sprintf函数将buf1和buf2拼接到buf3中
#include <stdio.h>
int main(int argc,char **argv)
{
/* char buf1[] = "guoqing";
char buf2[] = "shuangwaiwai"; */
char buf1[] = "国庆";
char buf2[] = "爽歪歪";
char buf3[1024] = {0};
sprintf(buf3,"%s%s",buf1,buf2);
printf("buf3:%s\n",buf3);
return 0;
}
2.getchar() ---> 从标准输入缓冲区中获取一个字符。 作用:清空标准输入的缓冲区。(重点)
#include <stdio.h>
int getchar(void);
参数:无
返回值:
成功:获取的字符
失败:-1
注意:
每次scanf()之前,都最好先清空缓冲区。
while(getchar()!='\n'); //清空缓冲区
scanf(); //一定可以保证缓冲区是空的。
3.getc() ---> 从某一个文件指针中获取一个字符
#include <stdio.h>
int getc(FILE *stream);
参数:
stream: 文件指针
返回值:
成功:获取的字符
失败:-1
说明:
1)getchar() --> 固定从标准输入缓冲区获取,不能从别的地方获取。
2)getc(stdin) 等价于 getchar() --> 指定从标准输入缓冲区中获取一个字符
FILE *fp = fopen("xxx","r");
getc(fp); --> 指定从xxx这个文件中获取一个字符。
------------------------------
#include <stdio.h>
int main()
{
FILE *fp = fopen("1.txt","r"); //hello
int c = getc(fp);
printf("c = %d\n",c);
printf("c = %c\n",c);
c = getc(fp);
printf("c = %d\n",c);
printf("c = %c\n",c);
fclose(fp);
return 0;
}
---------------------------------
总结:
getc和getchar两个功能有重叠部分 getc() > getchar()
getc重复使用的时候,光标也在移动
4.fgetc() 等价于 getc()
两个参数一样
#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
5.putchar() --> 把某一个字符存放在标准输出的缓冲区中
#include <stdio.h>
int putchar(int c);
参数:
c: 需要输出的字符
返回值:
成功:写入的字符
失败:-1
-------------------------
#include <stdio.h>
int main(int argc,char *argv[])
{
int c = putchar('x');
printf("c = %d\n",c);
printf("c = %c\n",c);
return 0;
}
--------------------------
6.putc() ---> 将字符存放在文件指针中。
#include <stdio.h>
int putc(int c, FILE *stream);
参数:
c: 需要输出的字符
stream: 文件指针
返回值:
成功:写入的字符
失败:-1
----------------------------
#include <stdio.h>
int main(int argc,char *argv[])
{
//putc('x',stdout);
FILE *fp = fopen("2.txt","w");
putc('x',fp);
putc('k',fp);
fclose(fp);
return 0;
}
-----------------------------
总结:
putc和putchar两个功能有重叠部分 putc() > putchar()
连续使用putc(),光标也会移动
7.fputc() 等价于 putc()
两个函数参数一样,返回值也一样
#include <stdio.h>
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
8.gets() --> 从标准输入缓冲区中获取一个字符串(不要用、危险)
#include <stdio.h>
char *gets(char *s);
参数:
s: 缓冲区
返回值:
成功:s的值
失败:NULL
------------------------------
#include <stdio.h>
int main(int argc,char *argv[])
{
char s[10] = {0};
gets(s); ---> 如果获取的字符超过10个,就会有问题。
printf("s = %s\n",s);
return 0;
}
说明:
gets()函数很危险,不要用。
/tmp/ccQKjzYm.o: In function `main':
gets.c:(.text+0x39): warning: the `gets' function is dangerous and should not be used.
-----------------------------
9.fgets() --> 获取一个字符串
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
参数:
s: 缓冲区的地址
size: 最多获取的字节数
stream: 文件指针
返回值:
成功:s的值
失败:NULL
注意如下:
int main(int argc,char *argv[])
{
FILE *fp = fopen("1.txt","r+");
char buf[10] = {0};
fgets(buf,6,fp);
printf("buf:%s\n",buf);
/*
buf:abcde
说明:如果你想从文件里面拿出5个字节,你至少要拿6个;因为最后
一个会自动补'\0'结束符。
*/
return 0;
}
练习2:
用代码来验证fgets的功能
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
/* char buf[10] = {0};
//等价于scanf,但是不同之处是fgets将‘\n’也会拿到buf中
fgets(buf,sizeof(buf),stdin);
printf("buf:%s strlen(buf)=%ld\n",buf,strlen(buf)); */
FILE *fp = fopen("1.txt","r+");
char buf[10] = {0};
fgets(buf,6,fp);
printf("buf:%s\n",buf);
/* buf:abcde
说明:如果你想从文件里面拿出5个字节,你至少要拿6个;因为最后
一个会自动补'\0'结束符。 */
return 0;
}
10.puts() ---> 输出一个字符串到标准输出中。
#include <stdio.h>
int puts(const char *s);
参数:
s: 输出的字符串
返回值:
成功:输出字符串的字符个数
失败:-1
-------------------------
#include <stdio.h>
int main()
{
char *s = "helloworld";
puts(s);
return 0;
}
-------------------------
11.fputs() --->输出一个字符串到指定的文件指针中
#include <stdio.h>
int fputs(const char *s, FILE *stream);
参数:
s: 缓冲区的地址
stream: 文件指针
返回值:
成功:非负数
失败:负数
12.scanf() --->从标准输入里面按照指定格式获取数据
#include <stdio.h>
int scanf(const char *format, ...);
案例
int num;
scanf("%d",&num);
13.fscanf() --->从文件指针里面按照指定格式获取数据(重点:拆分字符串)
#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);
案例
FILE *fp = fopen("1.txt","r+");
char buf[1024];
fscanf(fp,“%s",buf)
练习3:
用fscanf将1.txt里面的字符串拆分 “aaa,bbb,ccc”
char buf1[]
char buf2[]
char buf3[]
//可以切割以逗号隔开的字符串
fscanf(fp,"%[^','],%[^','],%s",buf1,buf2,buf3); //OK
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("1.txt","r+");
/* char buf[1024] = {0};
fscanf(fp,"%s",buf);
printf("buf:%s\n",buf);
*/
/* int num = 0;
fscanf(fp,"%d",&num);
printf("num:%d\n",num); */
char buf1[1024] = {0};
char buf2[1024] = {0};
char buf3[1024] = {0};
//fscanf(fp,"%s,%s,%s",buf1,buf2,buf3); //错误
//可以切割以逗号隔开的字符串
fscanf(fp,"%[^','],%[^','],%s",buf1,buf2,buf3); //OK
//fscanf(fp,"%3s,%3s,%3s",buf1,buf2,buf3);//有局限性
printf("buf1:%s buf2:%s buf3:%s\n",buf1,buf2,buf3);
return 0;
}
14.sscanf --->从字符串里面按照指定格式获取数据(重点:拆分字符串)
#include <stdio.h>
int sscanf(const char *str, const char *format, ...);
案例1
char *str = "bling"
char name[20]={0};
sscanf(str,"%s",name);
案例2
char *str1 = "bling,zhangsan,lisi";
char buf1[1024] = {0};
char buf2[1024] = {0};
char buf3[1024] = {0};
sscanf(str1,"%[^','],%[^','],%s",buf1,buf2,buf3);//拆分字符串
printf("buf1:%s buf2:%s buf3:%s\n",buf1,buf2,buf3);
三、目录IO
1.为什么要学习目录IO接口?
目录可以存放一些文件/目录,如果学习目录IO操作,那么我们就可以通过目录的名字来将目录下的全部名字读取出来,方便后续的操作。
例如:usr_data/
bling.txt aaa.txt bbb.txt --> 通过目录IO操作函数,就可以求出这些文件的名字。
2.访问文件与访问目录有什么区别?
访问文件 --> 得到文件里面的内容。
访问目录 --> 得到目录项的名字、属性、类型..
四、目录IO函数接口?
1.如何打开目录? --> opendir() --> man 3 opendir
#include <sys/types.h>
#include <dirent.h> //目录的专属头文件
DIR *opendir(const char *name);
参数:
name: 需要打开的那个目录的路径
返回值:
成功:目录流指针
失败:NULL
问题:打开一个目录是不是意味着已经切换进去了?
答案:没有切换进去。
2.如何切换到目录中? --> chdir() --> man 3 chdir
问题:一定要切换到目录下,才能读取目录的东西吗?
答案:不一定,但是切换进去之后,该目录下所有的文件都是当前目录下的文件,访问这些文件就很方便。
#include <unistd.h>
int chdir(const char *path);
参数:
path: 需要切换的那个目录的路径。
返回值:
成功:0
失败:-1
3.如何读取目录下的内容? --> readdir() --> man 3 readdir
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
参数:
dirp: 目录流指针
返回值:
成功:结构体指针
失败:NULL ---> 当读取目录完毕,就会返回NULL
说明:
每读取一个目录项,都会返回一个结构体指针,来代表这一个项的属性。
struct dirent{
ino_t d_ino; //索引号
off_t d_off; //偏移量
unsigned short d_reclen; //记录文件名长度
unsigned char d_type; //文件类型
char d_name[256]; //文件名(重点掌握)
};
文件类型:
DT_BLK This is a block device. ---> 块设备文件 //6
DT_CHR This is a character device. ---> 字符设备文件 //2
DT_DIR This is a directory. ---> 目录文件 //4
DT_FIFO This is a named pipe (FIFO). ---> 管道文件 //1
DT_LNK This is a symbolic link. ---> 链接文件 //10
DT_REG This is a regular file. ---> 普通文件 //8
DT_SOCK This is a UNIX domain socket.---> 套接字文件 //12
文件名:最多可以设置256个字符。
练习:
读一次和循环读打印它的文件名
4.关闭文件。 --> closedir() --> man 3 closedir
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
参数:
dirp: 目录流指针
返回值:
成功:0
失败:-1
5.重置目录指针。 ---> rewinddir() --> man 3 rewinddir
#include <sys/types.h>
#include <dirent.h>
void rewinddir(DIR *dirp);
参数:
dirp: 目录流指针
返回值:无
例子: 写代码,读取一个目录下的全部文件名出来。
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
//1. 打开目录。
DIR *dp = opendir("./bling_dir");
if(dp == NULL)
printf("opendir error!\n");
//2. 切换到目录下。
chdir("./bling_dir");
//3. 不断去读取该目录下的文件名。
struct dirent *ep = NULL;
while(1)
{
ep = readdir(dp);
if(ep == NULL) //说明已经读取到最后一项了
{
break;
}
if(ep->d_name[0] == '.') //如果文件名的第一个字符是.
{
continue;
}
printf("%s\n",ep->d_name);
}
//4. 关闭该目录。
closedir(dp);
return 0;
}