C 标准库 IO 使用详解(2)

ftell(fp) 函数比较简单,直接返回当前文件指针在文件中的位置

// 实现计算文件字节数的功能 fseek(fp, 0, SEEK_END); ftell(fp); 五、以字符串为单位的IO函数

fgets 从指定的文件中读一行字符到调用者提供的缓冲区,读入内容不超过 size 。

char *fgets(char *s, int size, FILE *stream); char *gets(char *s);

首先要说明 gets() 函数强烈不推荐使用,类似 strcpy 函数,用户不可以指定缓冲区大小,很容易造成缓冲区溢出错误。不过 strcpy 程序员还是可以避免,而 gets 的输入用户可以提供任意长的字符串,唯一避免方法就是不使用 gets,而使用 fgets(buf, size, stdin)

fgets 函数从 stream 所指文件读取以 '\n' 结尾的一行,包括 '\n' 在内,存到缓冲区中,并在该行结尾添加一个 '\0' 组成完整的字符串。如果文件一行太长,fgets 从文件中读了 size-1 个字符还没有读到 '\n',就把已经读到的 size-1 个字符和一个 '\0' 字符存入缓冲区,文件行剩余的内容可以在下次调用 fgets 时继续读。

若一次 fgets 调用在读入若干字符后到达文件末尾,则将已读到的字符加上 '\0' 存入缓冲区并返回,如果再次调用则返回 NULL,可以据此判断是否读到文件末尾。

fputs 向指定文件写入一个字符串,缓冲区保存的是以 '\0' 结尾的字符串,与 fgets 不同的是,fputs 不关心字符串中的 '\n' 字符。

int fputs(const char *s, FILE *stream); int puts(const char *s); 六、以记录为单位的IO函数 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

fread 和 fwrite 用于读写记录,这里的记录是指一串固定长度的字节,比如一个 int、一个结构体货或一个定长数组。

参数 size 指出一条记录的长度,nmemb 指出要读或写多少条记录,这些记录在 ptr 所指内存空间连续存放,共占 size * nmemb 个字节。

fread 和 fwrite 返回的记录数有可能小于 nmemb 指定的记录数。例如当读写位置距文件末尾只有一条记录长度,调用 fread 指定 nmemb 为 2,则返回值为 1。如果写文件时出错,则 fwrite 的返回值小于 nmemb 指定的值。

struct t{ int a; short b; }; struct t val = {1, 2}; FILE *fp = fopen("file.txt", "w"); fwrite(&val, sizeof(val), 1, fp); fclose(fp); liwei:/tmp$ od -tx1 -tc -Ax file.txt 0000000 01 00 00 00 02 00 00 00 001 \0 \0 \0 002 \0 \0 \0

从结果可以看出,写入的是 8 个字节,有兴趣的同学可以就此分析下系统的「大小端」和结构体的「对齐补齐」问题。

七、格式化IO函数 (1). printf / scanf int printf(const char *format, ...); int scanf(const char *format, ...);

这两个函数是我们学习 C 语言最早接触,可能也是接触比较多的了,没什么特别要说的。printf 就是格式化打印到标准输出。下面总结下 printf 常用的方式。

printf("%d\n", 5); // 打印整数 5 printf("-%10s-\n", "hello") // 设置显示宽度并左对齐:- hello- printf("-%-10s-\n", "hello") // 设置显示宽度并右对齐:- hello- printf("%#x\n", 0xff); // 0xff 不加#则显示ff printf("%p\n", main); // 打印 main 函数首地址 printf("%%\n"); // 打印一个 %

scanf 就是从标准输入中读取格式化数据,简单举个例子:

int year, month, day; scanf("%d/%d/%d", &year, &month, &day); printf("year = %d, month = %d, day = %d\n", year, month, day); (2). sprintf / sscanf / snprintf

sprintf 并不打印到文件,而是打印到用户提供的缓冲区中并在末尾加 '\0',由于格式化后的字符串长度很难预计,所以很可能造成缓冲区溢出,强烈推荐 snprintf 更好一些,参数 size 指定了缓冲区长度,如果格式化后的字符串超过缓冲区长度,snprintf 就把字符串截断到 size - 1 字节,再加上一个 '\0',保证字符串以 '\0' 结尾。如果发生截断,返回值是截断之前的长度,通过对比返回值与缓冲区实际长度对比就知道是否发生截断。

int sscanf(const char *str, const char *format, ...); int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format, ...);

sscanf 是从输入字符串中按照指定的格式去读取相应的数据,函数功能非常的强大,支持类似正则表达式匹配的功能。具体的使用格式请自行查询官方手册,这里总结出最常用、最重要的几种使用场景和方式。

最基本的用法

char buf[1024] = 0; sscanf("123456", "%s", buf); printf("%s\n", buf); // 结果为:123456

取指定长度的字符串

sscanf("123456", "%4s", buf); printf("%s\n", buf); // 结果为:1234

取第1个字符串

sscanf("hello world", "%s", buf); printf("%s\n", buf); // 结果为:hello 因为默认是以空格来分割字符串的,%s读取第一个字符串hello

读取到指定字符为止的字符串

sscanf("123456#abcdef", "%[^#]", buf); // 结果为:123456 // %[^#]表示读取到#符号停止,不包括#

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/53b85e46a6b87f819b784b0e04ca8285.html