linux_c学习笔记(一)标准IO
1、说明与参考
IO操作分为标准IO和系统调用IO,IO操作是一切实现的基础,学C语言的时候写的很多小demo都是运行就结束了,中间产生的数据都随之消息了,这在真正的开发中是不存在的。
- stdio 标准IO
- sysio 系统调用IO
为什么要有这两种IO?
在不同的操作系统上(win和linux),想要实现通一套标准,就需要使用标准IO,因此标准IO的通用性更高。
IO操作中标准IO相关的常用函数如下所示:
fopen();
fclose();
fgetc();
fputc();
fgets();
fputs();
fread();
fwrite();
printf();
scanf();
fseek();
ftell();
rewind();
fflush();
其中一个数据结构贯穿始终 FILE
2、fopen()和fclose()
使用man手册查看fopen函数,第一个参数是文件名,第二个是mode

继续往下看可以知道mode的选择

因此可以知道相关参数的含义如下 (因此对于r和r+,必须要求文件存在)
| mode | 说明 |
|---|---|
| r | 只读模式打开文件,指针在文件的起始位置 |
| r+ | 读写模式打开文件,指针在文件的起始位置 |
| w | 只写模式(有则清空,无则新增),指针在文件起始位置 |
| w+ | 读写形式(有则清空,无则新增),指针在文件起始位置 |
| a | 追加只写形式,文件不存在创建,指针在文件末尾,最后一个有效字节的下一个位置 |
| a+ | 追加读写模式,文件不存在创建,指针位置根据后续操作是读还是写决定,读就在起始位置,写就在末尾 |
下面看一段程序,这里主要是注意这个errno的使用
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
/*fopen函数的基本使用*/
int main()
{
FILE *fp;
fp = fopen("tmp","r");
if(fp == NULL)
{
fprintf(stderr, "fopen():%s\n",strerror(errno));
exit(1);
}
puts("OK!");
fclose(fp);
exit(0);
}
运行结果如下,因为这里没有对应的文件,所以为报错,使用errno可以查看错误原因 
这里可以用perror进一步优化报错的效,这样更简洁一点

3、fgetc()和fputc()
直接看代码,这两个函数比较好理解,实现了每次一个字符的IO操作,一个读一个写,使用这两个函数实现一个复制文件的实现
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv)
{
FILE *fps, *fpd;
int ch;
int count = 0;
if(argc < 3)
{
fprintf(stderr,"usage:%s <src_file> <dest_file>\n",argv[0]);
exit(1);
}
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("fopen()");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fps);//这行不写,如果失败就会造成前面的没有关闭,造成内存泄漏
perror("fopen()");
exit(1);
}
// while (fgetc(fps) != EOF)//这个是一个个的读取的
// {
// count++;
// }
// printf("count is %d\n",count);
while(1)
{
ch = fgetc(fps);
if(ch == EOF)//读到了文件尾
break;
fputc(ch,fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}
这里使用了这个方法做了一下输入的检查:

另外这里需要注意的是,读出来的是一个int型的数据(ch为int型的数据),这里不要怀疑,要跟手册为准
4、fgets()和fputs()
简单来说就是把原来字符操作转为字符串的操作了,含义是每次读一行,在上面的代码中进行如下修改,这里定义了一个buf[BUFFERSIZE]的char型的数组,使用fets进行读取,这里读到末尾是返回一个NULL了。

5、fread()和fwrite()
这两个函数的用法在man手册中描述如下

这里的nmemb理解为对象,size为字节,就是每次读多少个对象,一次多少个字节,用法示例如下:
- ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
- size -- 这是要读取的每个元素的大小,以字节为单位。
- nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
- stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
fread(buf, 1, 10, fp) 读取一个元素的大小是一个字节,读10个元素
fread(buf, 10, 1, fp) 读取一个元素的大小是十个字节,读1个元素
再次修改上面的代码,将while中的代码修改为如下形式

上面的部分可以理解为,读BUFFER_SIZE个,之后再写BUFFER_SIZE个,但是这样最后的效果是不对的,因为无法保证一定可以读到BUFFER_SIZE大小的内容,因此对这个部分的代码做如下修改:

详细的说明已经在图片中展示出来了
6、printf()和scanf()
这里重点关注printf相关的一些函数族printf() fprintf() sprintf()和snprintf()

先介绍一个函数atoi,提取字符串里面的数字

上面这段代码输出的结果是123,因为a是字母,所以运行到这里就会停止了。
下面看一段代码
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024];
int year = 2014,mouth = 5, day = 13;
sprintf(buf, "%d-%d-%d\n", year,mouth,day); //snprintf加上了字符数量的限制
puts(buf);
exit(0);
}
运行结果如下

这里要注意就是这个函数会自动加上\n换行,所以这里本身不需要把\n添加进去了
接下来是scanf一族的函数,函数描述和上面的printf基本是相似的

7、fseek()相关函数
这几个函数是用于操作文件位置指针的函数,在man手册中这几个函数如下所示

相关的解释如下

大意就是fseek就是移动操作文件位置的指针,然后ftell范围当前位置的指针,rewind就是将文件指针移动到开始的位置
下面演示一段示例代码
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv)
{
FILE *fp;
int count = 0;
if(argc < 2)
{
fprintf(stderr, "usage... \n");
exit(0);
}
fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
fseek(fp,0,SEEK_END); //将文件指针移动到文件末尾
printf("%ld\n", ftell(fp));
rewind(fp);
printf("%ld\n", ftell(fp));
fclose(fp);
exit(0);
}
代码运行结果如下所示,可以看到其实是第一次将位置指针放到了文件末尾,这个时候其实是获取了文件大小,第二次使用rewind成功将文件移动到了开头的位置

fseek的作用,一般比较多用来制造空洞文件,就是touch一个文件之后,就立马用fseek占满需要的空间,之后用多进程进行同时读写。
8、fflush()函数
先看一段程序
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i;
printf("before while()");
while(1);
printf("after while()");
exit(0);
}
这段代码以为是第一个打印会输出,第二个应该不会输出,但是实际运行结果确是一个也没有输出

现在对代码进行一点点小的修改

可以看到输出结果发生了改变,这是因为标准终端是按照换行符来刷新缓冲区,或者是行满了才刷新缓冲区

我们也可以用fflush来刷新缓冲区

输出结果如下所示,可以看到成功进行了输出

下面说明一下缓冲区,缓冲区的作用是合并系统调用,模式分为下面几种:
- 行缓冲 换行的时候刷新和满了的时候刷新
- 全缓冲 满了的时候刷新
- 无缓冲 例如stderr,需要立即输出的内容
可以通过setvbuf来实现修改缓冲模式
9、getline()函数
这个函数是获取一行的内容,这个函数本身会申请一段内存,如果空间不够,就会继续申请一段内存,本身是一个动态申请内存
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char **argv)
{
FILE *fp;
char *linebuf;
size_t linesize;
if(argc < 2)
{
fprintf(stderr, "usage... \n");
exit(0);
}
fp = fopen("1.txt","r");
linebuf = NULL; //这一句和下面的一句非常重要,一定要加,不然就是段错误,没有指定内存,还会造成内存泄漏
linesize = 0;
while(1)
{
if(getline(&linebuf, &linesize, fp) < 0)
break;
puts(linebuf);
printf("%d\n", strlen(linebuf));
printf("%d\n",linesize); //申请的内存大小
}
exit(0);
}
运行结果如下


浙公网安备 33010602011771号