C语言流输入和输出函数
printf(scanf)、puts(gets)和putchar(getchar)是分别向标准流输出(由标准流读入)字符串、一行字符和单个字符的函数。除了这些函数,C语言也提供了面向任何流的输入和输出函数。
fprintf函数
fprintf函数的原型为:
int fprintf(FILE *stream, const char *format, ...);
该函数向stream流输出字符串。与printf函数唯一的不同只是增加了一个参数:访问输出流的文件指针,支持向任何流输出字符串。
可变长度实参
在<stdarg.h>中提供了三个宏va_start, va_arg和va_end以及一个类型va_list。利用这些元素可以编写具有变长参数列表的函数。
具有变长参数列表的函数必须至少具有一个正常的形式参数,在最后一个正常形式参数的后边添加,...。变长参数列表的声明形式为:
返回值类型 函数名(正常形式参数1,正常形式参数2,...);
在函数的定义中,首先使用va_list声明变量,以支持访问在最后一个正常实际参数后面的实际参数:
va_list ap;
然后调用va_start声明可变长度部分开始的位置:
va_start(ap, 最后一个正常形式参数名);
之后依次通过va_arg获取可变参数:
可变参数的值 = va_arg(ap, 可变参数的类型);
在访问完成后,使用va_end结束:
va_end(ap);
。
在调用时,会进行默认的参数类型提升,例如由字符型自动提升到整型,由float型到double型等。
vfprintf函数
vfprintf原型为:
int vfprintf(FILE *stream, const char *format, va_list arg);
可以在带有变长参数列表的函数内调用,支持对输出信息的包装。
/****************************************
* stream_fprintf.c *
* *
* C语言中文件流的输出和变长参数列表函数*
****************************************/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
void Print(FILE* fp, char *msgtype, char* format, ...)
{
va_list vp;
va_start(vp, format);
fprintf(fp, "%s: ", msgtype);
vfprintf(fp, format, vp);
va_end(vp);
fprintf(fp,"
");
}
int Sum(size_t n, ...)
{
va_list vp;
va_start(vp, n);
size_t i = 0;
int sum = 0;
while (i < n)
{
int num = va_arg(vp, int);
sum += num;
i++;
}
va_end(vp);
return sum;
}
int main()
{
FILE *fp = NULL;
fp = fopen("test.txt", "w");
int n;
Print(fp, "询问","输入整数的个数: ");
scanf("%d", &n);
Print(fp, "询问", "输入%d个整数: ", n);
int *nums = malloc(n * sizeof(int));
int i = 0;
for (; i < n; i++)
{
scanf("%d", nums + i);
}
Print(fp, "结果", "输入的整数为:");
for (i = 0; i < n; i++)
{
fprintf(fp, "%d ", nums[i]);
}
fprintf(fp, "
");
int sum1 = Sum(3, 4, 5, 6);
Print(fp, "结果","4,5,6的和为%d", sum1);
int sum2 = Sum(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Print(fp, "结果","1到10的和为%d", sum2);
fclose(fp);
return 0;
}
fscanf函数
fscanf函数的原型为:
int fscanf(FILE *stream, const char *format, ...);
该函数从stream流读入数据。与scanf函数唯一的不同只是增加了一个参数:访问输入流的文件指针,支持由任何流读入数据。
文件结尾或错误指示函数
每个流都有两个指示器:错误指示器和文件末尾指示器。当遇到文件结尾时设置文件末尾指示器,若遇到错误(不是匹配错误引起的)则设置错误指示器。重新打开同一个流时会清除原来的指示器。也可以使用clearerr函数主动清除文件末尾指示器和错误指示器,其使用原型为:
void clearerr(FILE *fp);
可以利用feof函数检查指定的流是否设置了文件末尾指示器,如果设置了则返回非零值,否则返回0。feof函数的原型为:
int feof(FILE *fp);
可以利用ferror函数检查指定的流是否设置了错误指示器,设置了则返回非零值,否则返回0。
/**************************************
* string_scanf.c *
* *
* C语言中文件流的输入和错误检测 *
**************************************/
#include <stdio.h>
int main()
{
FILE *fp = NULL;
fp = fopen("test.txt", "r");
int x ,y;
int num = fscanf(fp, "%d%d", &x, &y);
if (num < 2)
{
printf("是否已达到文件末尾:%d
", feof(fp));
printf("是否出现错误: %d
", ferror(fp));
}
clearerr(fp);
fclose(fp);
return 0;
}
字符输出函数fputc和putc
putchar函数向标准输出流输出一个字符。fputc和putc是putchar的更通用版本,向任何流输出字符,两个函数的原型为:
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
其中putc和fputc的功能相同,只是putc是利用宏实现的。
正常的情况下三个函数都会返回输出的字符,而在出现错误时都会设置错误指示器,并返回EOF。
字符输入函数fgetc、getc和ungetc
getchar从标准流中读入一个字符,fgetc和getc是getchar的更通用版本,从任何流读入一个字符,两个函数原型为:
int fgetc(FILE *stream);
int getc(FILE *stream);
其中getc和fgetc的功能相同,但getc是利用宏实现的。
fgetc、getc和getchar三个函数在与遇到文件结尾的问题,都会设置文件末尾指示器,并返回EOF。如果产生了错误,就会设置错误指示器。
ungetc函数将从流中读入的字符回退,并清除掉流的文件末尾指示器,原型为:
int ungetc(int c, FILE *stream);
通过多次调用ungetc函数可以回退多个的字符,可以回退的数量依赖于实现和流的类型。调用文件定位函数会丢失回退的字符。ungetc正常情况下会返回字符,若试图返回过多的字符则返回EOF。
/**************************************
* stream_char.c *
* *
* C语言文件流输入和输出字符 *
**************************************/
#include <stdio.h>
int main()
{
FILE *fp = NULL;
fp = fopen("test.txt", "w+");
int c = "A";
fputc(c, fp);
c = "B";
fputc(c, fp);
c = "C";
fputc(c, fp);
/*将文件位置返回到开头*/
rewind(fp);
c = fgetc(fp);
printf("读出字符:%c
", c);
c = fgetc(fp);
printf("读出字符:%c
", c);
ungetc(c,fp);
printf("放回字符:%c
", c);
c = fgetc(fp);
printf("再次读出字符:%c
", c);
fclose(fp);
return 0;
}
行输出函数fputs
puts函数向标准输出流stdout输出一串字符,并且在输出字符后自动添加一个换行符。fputs是puts的更通用版本,可以向任意的输出流写入字符串,不同的是它不会自动添加换行符。fputs的函数原型为:
int fputs(const char *s, FILE *stream);
当出现错误时,两个函数都返回EOF。否则,返回一个非负数。
行输入函数fgets
gets函数向标准输出流stdin读入一串字符,逐个读入字符,在遇到换行符时停止,并自动丢弃换行符。fgets是gets的更通用版本,可以从任意的输出流读入字符串,也更安全,限制了读入的字符的数量。fgets的函数原型为:
char *fgets(char *s, int n, FILE *stream);
fgets函数逐个读入字符,并在遇到首个换行符时停止操作,或者已读到n-1个字符时停止操作,且fgets会将换行符和其他字符一起存储。
如果在读入过程中发生错误或者在读入任何字符前达到了输入流的末尾,则返回空指针,否则都返回读入字符串的指针。
/**************************************
* stream_put_get.c *
* *
* C语言文件流的行输入和输出函数 *
**************************************/
#include <stdio.h>
#define STR_LEN 32
int main()
{
FILE *fp = NULL;
fp = fopen("test.txt", "w+");
fputs("1: Hello World
", fp);
fputs("2: Hello World
", fp);
rewind(fp);
char tmp[STR_LEN];
fgets(tmp, STR_LEN, fp);
printf("%s", tmp);
fgets(tmp, STR_LEN, fp);
printf("%s", tmp);
fclose(fp);
return 0;
}
块输入输出函数fread和fwrite
fread和fwrite函数运行程序一次读入大数据块,常用于二进制流。
fwrite将内存中的数组复制给流,其原型为:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fwrite函数的第一个参数是数组的地址,第二个参数是每个数组元素的大小,第三个参数为需要写的数组元素的数量,最后一个是指示写入位置的文件指针。函数返回值为实际写入元素的数量,若小于第三个参数则表示出现写入错误。
fread从流中读入数组的元素,其原型为:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread每个参数的意义和fwrite是对应的,返回值是读入的元素数目,可以通过与第三个参数比较来判断是否出现读入错误。
尽管fread和fwrite是为写入数组设计的,但它可以用以全部类型的变量。
/**************************************
* stream_block.c *
* *
* C语言中文件流的块输入输出 *
*************************************/
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("输入整数个数:");
int n;
scanf("%d", &n);
int *nums = malloc(n * sizeof(int));
printf("输入每个整数:");
int i = 0;
for (; i < n; i++)
{
scanf("%d", nums + i);
}
printf("nums: ");
for (i = 0; i < n; i++)
{
printf("%d ", nums[i]);
}
printf("
");
FILE *fp = NULL;
fp = fopen("test.txt", "w+");
fwrite(nums, sizeof(int), n, fp);
fclose(fp);
free(nums);
// system("cat test.txt");
fp = fopen("test.txt", "r");
int *nums2 = calloc(n, sizeof(int));
fread(nums2, sizeof(int), n, fp);
printf("nums2: ");
for (i = 0; i < n; i++ )
{
printf("%d ", nums[i]);
}
printf("
");
fclose(fp);
free(nums2);
return 0;
}
文件的定位函数
在打开文件进行写操作时,可以通过模式设置从文件头开始,或以追加的方式在文件尾写入。C语言提供了fseek等5个函数支持更加灵活的定位方式。
fseek函数的原型为:
int fseek(FILE *stream, long int offset, int whence);
fseek函数改变stream指向的文件的文件位置,第三个参数whence说明第二个参数偏移量是想对于文件的起始处、当前位置还是末尾处,由3个宏表示:
-SEEK_SET: 文件的起始处
- SEEK_CUR:文件的当前位置
- SEEK_END:文件的末尾处。
在正常的情况下,fseek返回0,如果出现错误,则返回非零值。fseek最适合用于二进制流,对于文本流offset必须为0或者whence必须为SEEK_SET,且offset的值必须由ftell函数获得。对于二进制流来说,fseek函数不一定支持whence是SEEK_END的调用。
ftell函数以长整形方式返回当前文件位置,如果发生错误,返回-1L,并且将错误码放入errno中。ftell的原型为:
long int ftell(FILE *stream);
rewind函数将文件位置设置在开始处,并清除错误标记。
void rewind(FILE *stream);
fgetpos和fsettpos函数用于大型文件,利用fpos_t型值表示文件位置,它可以是一个结构体类型。调用成功则返回0,否则返回非零值,并将错误代码记录在errno中,这两个函数的原型为:
int fgetpos(FILE *stream, fpos_t *pos);
int fgetpos(FILE *stream, const fpos_t *pos);
字符串输入和输出函数sprintf和sscanf
sprintf函数和sscanf函数用以将字符串作为流的输入源或输入目的读写数据,sprintf(sscanf)与prinf(fprintf)唯一的不同就是读出和写入的流关联于字符串。sscanf的好处之一是可以多次进行测试输入行。它也返回成功读入并存储的数据项的数量。但如果在找到第一个数据之前就到达文件末尾,就返回EOF。类似的也有支持变长参数列表的svprintf函数。
/**************************************
* stream_string.c *
* *
* C语言中的字符串输入输出函数 *
**************************************/
#include <stdio.h>
#define STR_LEN 32
int main()
{
char msg[STR_LEN];
int x = 10;
int y = 32;
sprintf(msg, "%d,%d", x, y);
int m = 0;
int n = 0;
sscanf(msg,"%d,%d", &m, &n);
printf("m = %d, n = %d
", m, n);
return 0;
}
参考文献
- K.N. King 著,吕秀峰 译. C语言程序设计-现代方法. 人民邮电出版社
- 上一篇:没有了
- 下一篇:没有了
