- printf\scanf函数族
- fseek\ftell\rewind
- getline
- 临时文件
printf\scanf函数族
printf一族: man 3 printf
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
printf
是把待输出内容 ...
按照指定格式 format
输出到 stdout
中。
fprintf
则可指定输出的位置,默认 stdout
和 stderr
指向屏幕, stdin
指向键盘。
sprintf
则将不同类型的数据综合成一个串。
snprintf
则解决 sprintf
没有指定缓冲区大小的问题,类似 fgets
和 gets
相比于 printf
,其他函数更常用。
sprintf()
应用举例: atoi
实现把串转换为整数【串结束和碰到非数字字符时停止】,但是c中并没有 itoa
即把整型转换为一个串,,用 sprintf
可以实现这一点。
示例代码:
##include<stdio.h>
##include<stdlib.h>int main()
{char s[] = "123a456";printf("%d\n", atoi(s));char buf[1024];int year = 2022, month = 12, day = 28; sprintf(buf, "%d-%d-%d", year, month, day);puts(buf);exit(1);
}
运行结果:
scanf一族: man 3 scanf
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
scanf
从 stdin
中获取内容,按照 format
格式,填充到变量 ...
中。
fscanf
则可指定从哪个stream中获取内容。
sscanf
则从字符串 str
中获取内容,可以理解为 sprintf
的逆过程,把一个字符串按格式拆分为不同类型。【功能包含了 atoi
】
sscanf
例子:
示例代码:
##include<stdio.h>
##include<stdlib.h>int main()
{char s[] = "123a456";int x1, x2; sscanf(s, "%da%d", &x1, &x2);printf("%d %d\n", x1, x2); exit(1);
}
运行结果:
fseek\ftell\rewind
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
fseek
:设置文件指针的位置
- 参数:流、偏移、基本位置(
SEEK_SET
SEEK_CUR
SEEK_END
文件头,当前位置,文件尾) - 返回值: 0表示设置成功,1表示设置失败
ftell
:判断当前文件指针的位置
- 参数:流
- 返回值:相对于开头的位置
rewind
:将文件指针重置到开头,相当于 fseek(stream, 0, SEEK_SET)
fseek
和 ftell
使用举例:
//1. 读取10个字节并输出
fp = fopen();
fgetc(fp) * 10 -> rewind/fseek(fp,0,SEEK_SET) -> fput()//2. fseek可以用来产生空洞文件//3. fseek和ftell判断文件大小
##include<stdio.h>
##include<stdlib.h>int main(char argc, char **argv)
{if(argc < 2){ fprintf(stderr, "Usage: %s <file_name>", argv[0]);exit(1);} FILE *fp;int cnt = 0;fp = fopen(argv[1], "r");if(fp == NULL){ perror("fopen()");exit(1);} fseek(fp, 0, SEEK_END);printf("%ld\n",ftell(fp)); /* while(fgetc(fp) != EOF)cnt++;printf("cnt = %d\n", cnt);
*/fclose(fp);exit(0);
}
运行结果:
fflush
:刷新缓冲区
int fflush(FILE *stream);
- 参数:流,如果填NULL,表示刷新所有打开的流
缓冲区的作用:大多数情况下是好事,合并系统调用
三种缓冲方式:
- 行缓冲:换行刷新、满了刷新、强制刷新【
stdout
,因为是终端设备】 - 全缓冲:满了刷新、强制刷新【默认,只要不是终端设备】
- 无缓冲:如
stderr
,需要立即输出的内容
现象:什么都不打印,因为缓冲区没有刷新
int main()
{int i;printf("Before while()");while(1);printf("After while()");exit(0);
}
现象:打印 “Before while()”,因为缓冲区刷新了
int main()
{int i;printf("Before while()\n");/*printf("Before while()");fflush(stdout); or fflush(NULL)*/while(1);printf("After while()\n");exit(0);
}
fseek和ftell的缺陷:二者同时使用的时候由于ftell只能返回long类型的正数部分,而long类型的大小不同机子上定义不同,一般是2G-1(32bit),所以两个函数最多只能操作2G大小
fseeko,ftello用一个自定义类型解决了这个问题,但是不支持C89,C99,是方言,所以移植性不足
使用时要在编译时加 _FILE_OFFSET_BITS=64
写法如 gcc a.c -o a -D_FILE_OFFSET_BITS=64
太麻烦,写入makefile中
##终端执行
vim makefile
##makefile中写入
CFLAGS += -D_FILE_OFFSET_BITS=64
如果要移植性好又要支持大文件,就得另想办法
getline
具有一个不可替代性:获取一行的大小,而无需在意该行有多大
内部实现:malloc,如果不够再realloc,从而取到足够大的空间来存储读取到的数据
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
- 参数:
lineptr
:char *
的地址,作为缓冲区n
:size_t
的地址,getline会反填n,表示lineptr
已分配的大小
- 返回值:成功0,失败-1
例子:获取文件每一行的大小,并打印
示例代码:
##include<stdio.h>
##include<stdlib.h>
##include<string.h>int main(int argc, char **argv)
{if(argc < 2){ fprintf(stderr, "Usage:...\n");exit(1);} FILE *fp;char *linebuf;size_t linesize;fp = fopen(argv[1], "r");if(fp == NULL){ perror("fopen()");exit(1);} //很重要!!!linebuf = NULL;linesize = 0;while(1){ if(getline(&linebuf, &linesize, fp) < 0)break;printf("%d\n", (int)strlen(linebuf));printf("%d\n", (int)linesize);} fclose(fp);exit(0);
}
运行结果:
*getline
存在可控的内存泄漏问题,因为它没有互逆操作来 free
掉 linebuf
的内存*
临时文件
//临时文件:
1. 名字如何不冲突
2. 及时销毁char* tmpnam(char *s);
FILE* tmpfile();
tmpnam
产生一个可用的临时文件名,然后我们拿这个文件去创建临时文件。
可以看书拿到文件名和创建文件是分两步的,中间可能会被打断,所以存在 并发问题 ,比如进程A执行 tmpnam
后没来得及创建文件,就轮到进程B执行了, 而进程B同样执行了 tmpnam
,由于进程A执行 tmpnam
后没有创建文件,所以系统认为 进程A中 tmpnam
返回的文件名仍是可用的,于是返回给了进程B,然后进程B创建文件,就会导致进程A的名字冲突。
tmpfile
:产生匿名文件,返回它的 FILE*
- 匿名文件:存在于磁盘但无法查看也无法
ls -a
查看的文件 - 在
fclose
后,匿名文件会自动销毁【硬链接为0,文件计数符为0】 - 如果忘记
fclose
:可控内存泄漏【如果程序exit
或return
正常结束,进程申请的空间会被释放】