cstdio,在C语言中称为stdio.h。该库使用所谓的流与物理设备(如键盘、打印机、终端)或系统支持的任何其他类型的文件一起操作。
在本文将会通过介绍函数参数,举出实际的简单例子来帮助大家快速上手使用函数。
目录
一、流
二、库函数
1、File access(文件访问)
fclose:
fflush :
fopen:
freopen:
setbuf :
setvbuf :
2、Operations on files(对文件的操作)
remove:
rename:
tmofile:
tmpnam:
3、Formatted input/output(格式化的输入/输出)
fprintf:
fscanf:
snprintf :
sprintf:
sscanf:
vfprintf:
vfscanf:
vprintf:
vscanf:
vsnprintf:
vsprintf:
4、Character input/output:(字符输入/输出)
fgetc:
fgets:
fputc:
fputs:
getc:
getchar:
gets:
putc:
putchar:
ungetc:
5、Direct input/output(直接输入/输出)
fread:
fwrite:
6、Error-handling(错误处理)
clearerr:
feof:
ferror:
perror:
7、Types(类型)
FILE:
fpos_t:
size_t:
二、小结
一、流
在C语言的标准库stdio.h中,流(stream)是一个抽象的概念,用于表示输入和输出流。在C语言中,流是用来处理文件输入和输出的抽象实体,它可以是标准输入流(stdin)、标准输出流(stdout)或者文件流(file stream)。
、、stdio.h中定义了一系列函数和宏来操作流,例如fopen()用于打开文件流,fclose()用于关闭文件流,fread()和fwrite()用于读写文件流等。此外,还有一些用于控制流的函数和宏,如fflush()用于刷新输出缓冲区,feof()和ferror()用于检查文件结束符和错误标志等。
通过使用流,程序可以方便地进行文件的输入输出操作,无论是从键盘读取输入,还是向文件写入数据,都可以通过流来实现。流的使用使得文件操作变得更加灵活和方便,同时也提供了一种统一的接口来处理输入输出操作。
在程序运行时一般会使用以下三个流:
- stdin —— 标准输入流(键盘)
- stdout —— 标准输出流(屏幕)
- stderr —— 标准错误流(屏幕)
二、库函数
1、File access(文件访问)
fclose:
用于关闭文件与流的联系
/* fclose example */
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile = fopen ("myfile.txt","wt");
fprintf (pFile, "fclose example");
fclose (pFile);//成功返回0,失败返回EOF
return 0;
}
----------------------------------------------------我是分割线-------------------------------------------------------------
fflush :
在C语言中,当你向文件写入数据时,数据通常首先被存储在内存中的缓冲区中,而不是立即写入文件。fflush函数可以强制将缓冲区中的数据写入文件,以确保数据被及时保存。这在某些情况下特别重要,比如在程序终止之前需要确保所有数据都已经写入文件时。
/* fflush example */
#include <stdio.h>
char mybuffer[80];
int main()
{
FILE * pFile;
pFile = fopen ("example.txt","r+");
if (pFile == NULL) perror ("Error opening file");
else {
fputs ("test",pFile);
fflush (pFile); // flushing or repositioning required
fgets (mybuffer,80,pFile);
puts (mybuffer);
fclose (pFile);
return 0;
}
}
----------------------------------------------------我是分割线-------------------------------------------------------------
fopen:
打开文件
FILE * fopen ( const char * filename, const char * mode )
打开其名称在参数 filename 中指定的文件,并将其与流相关联,该流可在将来的操作中通过返回的 FILE 指针进行标识。文件名应该包含要打开的文件的名称的 C 字符串。其值应遵循运行环境的文件名规范。
对流执行的操作以及如何执行这些操作由 mode 参数定义。
以下为mode参数:
返回值:如果文件已成功打开,该函数将返回指向 FILE 对象的指针,该对象可用于在将来的操作中标识流。否则,将返回 null 指针。
/* fopen example */
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile = fopen ("myfile.txt","w");
if (pFile!=NULL)
{
fputs ("fopen example",pFile);
fclose (pFile);
}
return 0;
}
----------------------------------------------------我是分割线-------------------------------------------------------------
freopen:
在C语言中,freopen()函数用于重新指定一个已经打开的文件流的文件名和访问模式。这个函数可以用来重新定向一个已经打开的文件流,从而改变该文件流对应的文件。
FILE * freopen ( const char * filename, const char * mode, FILE * stream )
filename
:要打开的文件名。mode
:打开方式,同fopen。File
:一个FILE型指针,可以是stdin(标准输入)或stdout(标准输出)。
使用freopen()函数可以实现一些文件操作的功能,例如:
- 重新打开一个文件,从而关闭当前的文件流并将其重新指向另一个文件。
- 改变文件流的访问模式,例如从只读模式改为只写模式。
总之,freopen()函数提供了一种方便的方式来重新指定一个已经打开的文件流,从而改变其对应的文件和访问模式。
----------------------------------------------------我是分割线-------------------------------------------------------------
setbuf :
用于为流指定缓冲区,可以提高输入/输出操作的性能。该函数通常用于设置标准输入、标准输出或标准错误流的缓冲区。它接受三个参数:正在设置缓冲区的流、指向缓冲区的指针和缓冲区的大小。setbuf函数对于优化输入/输出操作的性能特别有用,特别是在处理大量数据时。
void setbuf ( FILE * stream, char * buffer )
FILE* stream:指向标识打开流的 FILE 对象的指针
buffer :用户分配的缓冲区。长度至少为 BUFSIZ 字节。或者,可以指定 null 指针来禁用缓冲。
/* setbuf example */
#include <stdio.h>
int main ()
{
char buffer[BUFSIZ];
FILE *pFile1, *pFile2;
pFile1=fopen ("myfile1.txt","w");
pFile2=fopen ("myfile2.txt","a");
setbuf ( pFile1 , buffer );
fputs ("This is sent to a buffered stream",pFile1);
fflush (pFile1);
setbuf ( pFile2 , NULL );
fputs ("This is sent to an unbuffered stream",pFile2);
fclose (pFile1);
fclose (pFile2);
return 0;
}
在此示例中,打开两个文件进行写入。与文件 myfile1.txt 关联的流设置为用户分配的缓冲区;对它执行写入操作;数据在逻辑上是流的一部分,但在调用 fflush 函数之前,它尚未写入设备。
示例中与文件 myfile2.txt 关联的第二个缓冲区设置为无缓冲,因此后续输出操作将尽快写入设备。
但是,一旦文件关闭,缓冲流和无缓冲流的最终状态是相同的(关闭文件会刷新其缓冲区)。
----------------------------------------------------我是分割线------------------------------------------------------------
setvbuf :
用于设置文件流的缓冲方式,使用 setvbuf
可以控制文件流的缓冲行为,这对于低级 I/O 或对性能有严格要求的程序非常有用。
int setvbuf ( FILE * stream, char * buffer, int mode, size_t size )
参数说明:
stream
:指向 FILE 对象的指针,该 FILE 对象指定了要更改其缓冲区的流。buffer
:指向用户提供缓冲区的指针。如果这个参数是 NULL,则库函数会为流自动分配一个缓冲区。mode
:指定缓冲区的模式。有效的模式有:
_IONBF
:关闭缓冲。_IOFBF
:完全缓冲。_IOLBF
:行缓冲。size
:指定缓冲区的大小。如果该值为 0,则库函数将使用一个实现定义的值。
详细mode:
返回值:如果成功,函数返回 0;否则,返回一个非零值。
/* setvbuf example */
#include <stdio.h>
int main ()
{
FILE *pFile;
pFile=fopen ("myfile.txt","w");
setvbuf ( pFile , NULL , _IOFBF , 1024 );
// File operations here
fclose (pFile);
return 0;
}
在此示例中,将创建一个名为 myfile.txt 的文件,并为关联的流请求 1024 字节的完整缓冲区,因此,只有在每次填充 1024 字节缓冲区时,才应将输出到此流的数据写入文件。
2、Operations on files(对文件的操作)
remove:
删除文件
int remove ( const char * filename );
参数说明:
const char * filename:文件名
返回值:如果文件已成功删除,则返回零值。失败时,将返回非零值
#include <stdio.h>
int main ()
{
if( remove( "myfile.txt" ) != 0 )
perror( "Error deleting file" );
else
puts( "File successfully deleted" );
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
rename:
将 oldname 指定的文件或目录的名称更改为 newname。
int rename ( const char * oldname, const char * newname );
返回值:如果文件重命名成功,则返回零值。失败时,将返回非零值。
#include <stdio.h>
int main ()
{
int result;
char oldname[] ="oldname.txt";
char newname[] ="newname.txt";
result= rename( oldname , newname );
if ( result == 0 )
puts ( "File successfully renamed" );
else
perror( "Error renaming file" );
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
tmofile:
打开临时文件。创建一个临时二进制文件,打开以供更新(“wb+”模式,有关详细信息,请参见 fopen),其文件名保证与任何其他现有文件不同。当流关闭 (fclose) 或程序正常终止时,创建的临时文件会自动删除。如果程序异常终止,是否删除文件取决于具体的系统和库实现。
FILE * tmpfile ( void )
返回值:如果成功,该函数将返回指向创建的临时文件的流指针。失败时,返回 NULL。
#include <stdio.h>
#include <string.h>
int main ()
{
char buffer [256];
FILE * pFile;
pFile = tmpfile ();
do {
if (!fgets(buffer,256,stdin)) break;
fputs (buffer,pFile);
} while (strlen(buffer)>1);
rewind(pFile);
while (!feof(pFile)) {
if (fgets (buffer,256,pFile) == NULL) break;
fputs (buffer,stdout);
}
fclose (pFile);
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
tmpnam:
生成临时文件名。返回一个字符串,其中包含与任何现有文件的名称不同的文件名,因此适合安全地创建临时文件,而不会有覆盖现有文件的风险。
char * tmpnam ( char * str );
参数:
指向字符数组的指针,其中建议的临时名称将存储为 C 字符串。此数组的建议大小至少为 L_tmpnam 个字符。或者,可以指定一个 null 指针来使用内部静态数组来存储建议的临时名称,其指针由函数返回。
返回值:
成功后,指向包含临时文件建议名称的 C 字符串的指针:
- 如果 str 是 null 指针,则指向内部缓冲区 (其内容至少保留到下次调用此函数) 。
- 如果 str 不是 null 指针,则返回 str。
如果函数无法创建合适的文件名,它将返回一个 null 指针。
#include <stdio.h>
int main ()
{
char buffer [L_tmpnam];
char * pointer;
tmpnam (buffer);
printf ("Tempname #1: %s\n",buffer);
pointer = tmpnam (NULL);
printf ("Tempname #2: %s\n",pointer);
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
3、Formatted input/output(格式化的输入/输出)
fprintf:
将按格式指向的 C 字符串写入流。如果 format 包含格式说明符(以 % 开头的子序列),则格式后面的附加参数将格式化并插入到生成的字符串中,以替换它们各自的说明符。
int fprintf ( FILE * stream, const char * format, ... );
参数:
FILE*stream:指向标识输出流的 FILE 对象的指针。
const char * format:要输入的字符串(类似printf可以在字符串中规定占位符)
字符串后:每个参数都包含一个值,用于替换格式字符串中的格式说明符,这些参数的数量至少应与格式说明符中指定的值数一样多。类似printf与字符串中的占位符对应,具体请看举例理解。
返回值:
成功后,将返回写入的字符总数。
如果发生写入错误,则设置错误指示符 (ferror) 并返回负数。
/* fprintf example */
#include <stdio.h>
int main ()
{
FILE * pFile;
int n;
char name [100];
pFile = fopen ("myfile.txt","w");
for (n=0 ; n<3 ; n++)
{
puts ("please, enter a name: ");
gets (name);
fprintf (pFile, "Name %d [%-10.10s]\n",n+1,name);
}
fclose (pFile);
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
fscanf:
从流中读取数据,并根据参数格式将其存储到其他参数所指向的位置。
int fscanf ( FILE * stream, const char * format, ... );
stream:指向 FILE 对象的指针,该对象标识要从中读取数据的输入流。
format:格式说明符
附加参数:用来与格式说明符对应,只不过这些参数应该是指针,用于对该地值进行输入。
返回值:
成功后,该函数返回成功填充的参数列表的项数。
失败,返回EOF
/* fscanf example */
#include <stdio.h>
int main ()
{
char str [80];
float f;
FILE * pFile;
pFile = fopen ("myfile.txt","w+");
fprintf (pFile, "%f %s", 3.1416, "PI");
rewind (pFile);
fscanf (pFile, "%f", &f);
fscanf (pFile, "%s", str);
fclose (pFile);
printf ("I have read: %f and %s \n",f,str);
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
snprintf :
将C 字符串存储在 s 指向的缓冲区中(将 n 作为要填充的最大缓冲区容量)。
int snprintf ( char * s, size_t n, const char * format, ... );
s,n : s为要写入的字符串;n为要写入的字符的最大数目,超过n会被截断(因为读取要包括‘\0’,所以实际读入为n - 1个)
format : 格式化字符串,包含的格式字符串遵循与 printf 中的格式相同的规范
...:写入对应占位符的字符串或其他类型数据
类似该格式:snprintf(str, 3, "%s", "abcdefg")
返回值:
正常返回值为:字符串中字符数量,不包括NULL
编码错误:返回负数。
/* snprintf example */
#include <stdio.h>
int main ()
{
char buffer [100];
int cx;
cx = snprintf ( buffer, 100, "The half of %d is %d", 60, 60/2 );
if (cx>=0 && cx<100) // check returned value
snprintf ( buffer+cx, 100-cx, ", and the half of that is %d.", 60/2/2 );
puts (buffer);
return 0;
}
输出:The half of 60 is 30, and the half of that is 15.
----------------------------------------------------我是分割线------------------------------------------------------------
sprintf:
功能与snprintf相似,但是没有snprintf安全。
int sprintf ( char * str, const char * format, ... );
参数:
str:指向存储生成的 C 字符串的缓冲区的指针。缓冲区应足够大,以包含生成的字符串。
format:C 字符串,其中包含的格式字符串遵循与 printf 中的格式相同的规范
...:根据格式字符串,后跟随替换占位符的参数
返回值:
成功后:返回写入的字符总数。此计数不包括自动追加在字符串末尾的附加 null 字符。
失败时:返回负数。
/* sprintf example */
#include <stdio.h>
int main ()
{
char buffer [50];
int n, a=5, b=3;
n=sprintf (buffer, "%d plus %d is %d", a, b, a+b);
printf ("[%s] is a string %d chars long\n",buffer,n);
return 0;
}
[5 plus 3 is 8] is a string 13 chars long
----------------------------------------------------我是分割线------------------------------------------------------------
sscanf:
从字符串中读取格式化数据,从 s 读取数据,并根据参数格式将它们存储到附加参数给出的位置,就像使用 scanf 一样,但从 s 读取数据而不是标准输入 (stdin)。
int sscanf ( const char * s, const char * format, ...);
参数:
s:从该字符串中读取数据
format:C 字符串,其中包含的格式字符串,该字符串遵循与 scanf 中的格式相同的规范
...:后跟参数,对应格式字符串,作为指针对应占位符
返回值:
成功:该函数返回参数列表中成功填充的项数。
如果在成功解释任何数据之前输入失败,则返回 EOF。
/* sscanf example */
#include <stdio.h>
int main ()
{
char sentence []="Rudolph is 12 years old";
char str [20];
int i;
sscanf (sentence,"%s %*s %d",str,&i);//添加了*的部分会被忽略,不会被参数获取
printf ("%s -> %d\n",str,i);
return 0;
}
输出:Rudolph -> 12
----------------------------------------------------我是分割线------------------------------------------------------------
vfprintf:
将格式化数据从变量参数列表写入流
int vfprintf ( FILE * stream, const char * format, va_list arg );
参数:
stream:指向标识输出流的 FILE 对象的指针。
fomat:C 字符串,含占位符
arg:
在 vfprintf 函数中,va_list 类型用于访问参数列表中的各个参数。va_list 实际上是一个简单的类型定义,通常定义为 typedef int va_list,它只是一个整数类型的别名,用于在参数列表中进行偏移计算。
使用 vfprintf 函数时,通常需要结合 va_start、va_arg 和 va_end 宏来正确地处理可变参数列表。这些宏用于初始化 va_list 变量、获取参数列表中的下一个参数以及清理 va_list 变量,它们通常在 stdarg.h 头文件中定义。
vfprintf 函数根据 format 字符串和 ap参数列表的内容,将格式化的数据写入到 stream 指向的文件中。如果函数成功执行,它将返回写入的字符数。如果出现错误,它将返回一个负值。
这个函数通常与 <stdio.h> 和 <stdarg.h> 头文件一起使用
返回值:
成功:将返回写入的字符总数。
错误:设置错误指示符 (ferror) 并返回负数。
/* vfprintf example */
#include <stdio.h>
#include <stdarg.h>
void WriteFormatted (FILE * stream, const char * format, ...)
{
va_list args;
va_start (args, format);
vfprintf (stream, format, args);
va_end (args);
}
int main ()
{
FILE * pFile;
pFile = fopen ("myfile.txt","w");
WriteFormatted (pFile,"Call with %d variable argument.\n",1);
WriteFormatted (pFile,"Call with %d variable %s.\n",2,"arguments");
fclose (pFile);
return 0;
}
myfile.txt将包含:
我的文件:.txt
Call with 1 variable argument. Call with 2 variable arguments.
----------------------------------------------------我是分割线------------------------------------------------------------
vfscanf:
将格式化数据从流读取到变量参数列表中,从流中读取数据,并根据参数格式将它们存储到 arg 标识的变量参数列表中元素所指向的位置。
int vfscanf ( FILE * stream, const char * format, va_list arg );
stream:指向标识输入流的 FILE 对象的指针。
format:C 字符串,其中包含的格式字符串
arg:标识使用va_start初始化的变量参数列表的值。va_list是在<cstdarg>中定义的特殊类型。
成功:该函数返回成功填充的参数列表的项数。此计数可以与预期的项目数匹配,也可以由于匹配失败、读取错误或文件末尾的范围而更少(甚至为零)。
如果读取时发生读取错误或到达文件末尾,则设置正确的指示器(feof 或 ferror)。而且,如果在成功读取任何数据之前发生任何一种情况,则返回 EOF。
/* vfscanf example */
#include <stdio.h>
#include <stdarg.h>
void ReadStuff (FILE * stream, const char * format, ...)
{
va_list args;
va_start (args, format);
vfscanf (stream, format, args);
va_end (args);
}
int main ()
{
FILE * pFile;
int val;
char str[100];
pFile = fopen ("myfile.txt","r");
if (pFile!=NULL) {
ReadStuff ( pFile, " %s %d ", str, &val );
printf ("I have read %s and %d", str, val);
fclose (pFile);
}
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
vprintf:
将格式化数据从变量参数列表打印到 stdout,将 format 指向的 C 字符串写入标准输出 (stdout),以与 printf 相同的方式替换任何格式说明符,但使用 arg 标识的变量参数列表中的元素,而不是其他函数参数。
int vprintf ( const char * format, va_list arg );
参数:
format:C 字符串,其中包含的格式字符串遵循与 printf 中的格式相同的规范
arg:标识使用 va_start 初始化的变量参数列表的值。
va_list 是 <cstdarg> 中定义的特殊类型。之后文章会对va_list进行详细介绍
返回值:
成功:将返回写入的字符总数。
发生写入错误,则设置错误指示符 (ferror) 并返回负数。
/* vprintf example */
#include <stdio.h>
#include <stdarg.h>
void WriteFormatted ( const char * format, ... )
{
va_list args;
va_start (args, format);
vprintf (format, args);
va_end (args);
}
int main ()
{
WriteFormatted ("Call with %d variable argument.\n",1);
WriteFormatted ("Call with %d variable %s.\n",2,"arguments");
return 0;
}
该示例演示如何使用不同数量的参数调用 WriteFormatted,这些参数又传递给 vprintf 函数,显示以下输出:
Call with 1 variable argument. Call with 2 variable arguments.
----------------------------------------------------我是分割线------------------------------------------------------------
vscanf:
将格式化数据读入变量参数列表,从标准输入 (stdin) 读取数据,并根据参数格式将其存储到由 arg 标识的变量参数列表中的元素所指向的位置。
int vscanf ( const char * format, va_list arg )
参数:
format:C 字符串,其中包含的格式字符串,该字符串遵循与 scanf 中的格式相同的规范
返回值:
arg:之后会发布对arg进行详细讲解文章
/* vscanf example */
#include <stdio.h>
#include <stdarg.h>
void GetMatches ( const char * format, ... )
{
va_list args;
va_start (args, format);
vscanf (format, args);
va_end (args);
}
int main ()
{
int val;
char str[100];
printf ("Please enter a number and a word: ");
fflush (stdout);
GetMatches (" %d %99s ", &val, str);
printf ("Number read: %d\nWord read: %s\n", val, str);
return 0;
}
Please enter a number and a word: 911 airport Number read: 911 Word read: airport
----------------------------------------------------我是分割线------------------------------------------------------------
vsnprintf:
将格式化数据从变量参数列表写入大小缓冲区,使用在 printf 上使用 format 时打印的相同文本组成一个字符串,但使用 arg 标识的变量参数列表中的元素而不是其他函数参数,并将生成的内容作为 C 字符串存储在由 s 指向的缓冲区中(将 n 作为要填充的最大缓冲区容量)。
int vsnprintf (char * s, size_t n, const char * format, va_list arg );
参数:
s:指向存储生成的 C 字符串的缓冲区的指针。缓冲区的大小应至少为 n 个字符。
n:缓冲区中要使用的最大字节数。生成的字符串的长度最多为 n-1,为其他终止 null 字符留出空间。
format:C 字符串,其中包含的格式字符串遵循与 printf 中的格式相同的规范
arg:之后文章介绍
返回值:
如果足够大,则将写入的字符数,不包括终止的 null 字符。
如果发生编码错误,则返回负数。
/* vsnprintf example */
#include <stdio.h>
#include <stdarg.h>
void PrintFError ( const char * format, ... )
{
char buffer[256];
va_list args;
va_start (args, format);
vsnprintf (buffer,256,format, args);
perror (buffer);
va_end (args);
}
int main ()
{
FILE * pFile;
char szFileName[]="myfile.txt";
pFile = fopen (szFileName,"r");
if (pFile == NULL)
PrintFError ("Error opening '%s'",szFileName);
else
{
// file successfully open
fclose (pFile);
}
return 0;
}
如果文件 myfile.txt 不存在,则调用 perror 以显示类似于以下内容的错误消息:
Error opening file 'myfile.txt': No such file or directory
----------------------------------------------------我是分割线------------------------------------------------------------
vsprintf:
将格式化数据从变量参数列表写入字符串,使用在 printf 上使用 format 时打印的相同文本组成一个字符串,但使用 arg 标识的变量参数列表中的元素而不是其他函数参数,并将生成的内容作为 C 字符串存储在 s 指向的缓冲区中。
int vsprintf (char * s, const char * format, va_list arg );
参数:
s:指向存储生成的 C 字符串的缓冲区的指针。
format:C 字符串,其中包含的格式字符串遵循与 printf 中的格式相同的规范
arg:后文再做解释
返回值:
成功后,将返回写入的字符总数。
失败时,返回负数。
/* vsprintf example */
#include <stdio.h>
#include <stdarg.h>
void PrintFError ( const char * format, ... )
{
char buffer[256];
va_list args;
va_start (args, format);
vsprintf (buffer,format, args);
perror (buffer);
va_end (args);
}
int main ()
{
FILE * pFile;
char szFileName[]="myfile.txt";
pFile = fopen (szFileName,"r");
if (pFile == NULL)
PrintFError ("Error opening '%s'",szFileName);
else
{
// file successfully open
fclose (pFile);
}
return 0;
}
如果文件 myfile.txt 不存在,则调用 perror 以显示类似于以下内容的错误消息:
Error opening file 'myfile.txt': No such file or directory
----------------------------------------------------我是分割线------------------------------------------------------------
vsscanf:
将格式化数据从字符串读取到变量参数列表中,从 s 读取数据,并根据参数格式将它们存储到 arg 标识的变量参数列表中元素所指向的位置。
int vsscanf ( const char * s, const char * format, va_list arg );
s:函数作为其源处理以检索数据的 C 字符串。
format:C 字符串,其中包含的格式字符串,该字符串遵循与 scanf 中的格式相同的规范
arg:后文讲解
成功:返回参数列表中成功填充的项数。
在成功解释任何数据之前输入失败:返回 EOF。
/* vsscanf example */
#include <stdio.h>
#include <stdarg.h>
void GetMatches ( const char * str, const char * format, ... )
{
va_list args;
va_start (args, format);
vsscanf (str, format, args);
va_end (args);
}
int main ()
{
int val;
char buf[100];
GetMatches ( "99 bottles of beer on the wall", " %d %s ", &val, buf);
printf ("Product: %s\nQuantity: %d\n", buf, val);
return 0;
}
Product: bottles Quantity: 99
----------------------------------------------------我是分割线------------------------------------------------------------
4、Character input/output:(字符输入/输出)
fgetc:
从流中获取字符。
int fgetc ( FILE * stream );
返回值:
成功:将返回读取的字符(提升为 int 值)
失败:EOF
/* fgetc example: money counter */
#include <stdio.h>
int main ()
{
FILE * pFile;
int c;
int n = 0;
pFile=fopen ("myfile.txt","r");
if (pFile==NULL) perror ("Error opening file");
else
{
do {
c = fgetc (pFile);
if (c == '$') n++;
} while (c != EOF);
fclose (pFile);
printf ("The file contains %d dollar sign characters ($).\n",n);
}
return 0;
}
逐个字符读取名为 myfile.txt 的现有文件,并使用 n 变量来计算文件包含的美元字符数 ($)。
----------------------------------------------------我是分割线------------------------------------------------------------
fgets:
从流中获取字符串。
char * fgets ( char * str, int num, FILE * stream );
规则:
- 从流中读取字符,并将它们作为 C 字符串存储到 str 中,直到读取 (num-1) 个字符或到达换行符或文件末尾,以先到者为准。
- 换行符使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中。
- 终止 null 字符会自动追加到复制到 str 的字符之后。
fgets 与 gets 有很大不同:fgets 不仅接受 stream 参数,还允许指定 str 的最大大小,并在字符串中包含任何结束换行符。
参数:
str:指向复制字符串读取的 chars 数组的指针。
num:要复制到 str 中的最大字符数(包括终止 null 字符)。
stream:指向标识输入流的 FILE 对象的指针。,stdin可以用作从标准输入中读取的参数
返回值:
成功:该函数返回 str
失败:返回NULL
/* fgets example */
#include <stdio.h>
int main()
{
FILE * pFile;
char mystring [100];
pFile = fopen ("myfile.txt" , "r");
if (pFile == NULL) perror ("Error opening file");
else {
if ( fgets (mystring , 100 , pFile) != NULL )
puts (mystring);
fclose (pFile);
}
return 0;
}
读取 myfile.txt 的第一行或前 99 个字符(以先到者为准),并将其打印在屏幕上。
----------------------------------------------------我是分割线------------------------------------------------------------
fputc:
将字符写入流。
int fputc ( int character, FILE * stream );
参数:
character:要编写的字符的 int 提升(符号提升)。写入时,该值在内部转换为无符号字符。
stream:指向标识输出流的 FILE 对象的指针。
返回值:
成功:功返回所写的字符。
错误:返回 EOF 并设置错误指示符 (ferror)。
/* fputc example: alphabet writer */
#include <stdio.h>
int main ()
{
FILE * pFile;
char c;
pFile = fopen ("alphabet.txt","w");
if (pFile!=NULL) {
for (c = 'A' ; c <= 'Z' ; c++)
fputc ( c , pFile );
fclose (pFile);
}
return 0;
}
创建一个名为 alphabet.txt 的文件,并将 ABCDEFGHIJKLMNOPQRSTUVWXYZ 写入其中。
----------------------------------------------------我是分割线------------------------------------------------------------
fputs:
将字符串写入流。
int fputs ( const char * str, FILE * stream );
规则和跟puts的不同处:
将 str 指向的 C 字符串写入流。
该函数从指定的地址 (str) 开始复制,直到到达终止 null 字符 ('\0')。此终止 null 字符不会复制到流中。
请注意,fputs 与 put 的不同之处不仅在于可以指定目标流,而且 fputs 不会写入其他字符,而 puts 会自动在末尾附加换行符。
参数:
str:包含要写入流的内容的 C 字符串。
stream:指向标识输出流的 FILE 对象的指针。
返回值:
成功:返回非负值。
错误:该函数返回 EOF 并设置错误指示器 (ferror)。
/* fputs example */
#include <stdio.h>
int main ()
{
FILE * pFile;
char sentence [256];
printf ("Enter sentence to append: ");
fgets (sentence,256,stdin);
pFile = fopen ("mylog.txt","a");
fputs (sentence,pFile);
fclose (pFile);
return 0;
}
该程序允许在每次运行时将一行附加到名为 mylog.txt 的文件中。
----------------------------------------------------我是分割线------------------------------------------------------------
getc:
从流中获取字符
int getc ( FILE * stream );
规则:
- 返回指定流的内部文件位置指示器当前指向的字符。然后,内部文件位置指示器将前进到下一个字符。
- 如果流在调用时位于文件末尾,则该函数将返回 EOF 并设置流的文件末尾指示符 (feof)。
- 如果发生读取错误,该函数将返回 EOF 并设置流的错误指示器 (ferror)。
- getc 和 fgetc 是等效的,只是 getc 在某些库中可以作为宏实现。
参数:
stream:指向标识输入流的 FILE 对象的指针。
返回值:
成功后,将返回读取的字符(提升为 int 值)。
返回类型为 int 以适应特殊值 EOF,该值表示失败:
如果位置指示器位于文件末尾,则该函数返回 EOF 并设置流的 EOF 指示器 (feof)。
如果发生其他读取错误,该函数也会返回 EOF,但会设置其错误指示器 (ferror)。
/* getc example: money counter */
#include <stdio.h>
int main ()
{
FILE * pFile;
int c;
int n = 0;
pFile=fopen ("myfile.txt","r");
if (pFile==NULL) perror ("Error opening file");
else
{
do {
c = getc (pFile);
if (c == '$') n++;
} while (c != EOF);
fclose (pFile);
printf ("File contains %d$.\n",n);
}
return 0;
}
逐个字符读取名为 myfile.txt 的现有文件,并使用 n 变量来计算文件包含多少美元字符 ($)。
----------------------------------------------------我是分割线------------------------------------------------------------
getchar:
从 stdin 获取字符,返回读取的字符。
int getchar ( void );
等效于使用 stdin 作为参数调用 getc
返回值:
成功:返回读取的字符(提升为 int 值)。
失败:EOF
/* getchar example : typewriter */
#include <stdio.h>
int main ()
{
int c;
puts ("Enter text. Include a dot ('.') in a sentence to exit:");
do {
c=getchar();
putchar (c);
} while (c != '.');
return 0;
}
一台简单的打字机。一旦按下 ENTER,每个句子都会被回显,直到文本中包含一个点 (.)
----------------------------------------------------我是分割线------------------------------------------------------------
gets:
从 stdin 获取字符串,返回获取的字符串(终止 null 字符会自动追加到复制到 str 的字符之后)
char * gets ( char * str );
- [注意:此函数在 C 或 C++ 中不再可用(从 C11 和 C++14 开始)]
参数:
str:指向内存块(字符数组)的指针,其中读取的字符串被复制为 C 字符串。
返回值:
成功:函数返回 str。
失败:返回空指针。
/* gets example */
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string); // warning: unsafe (see fgets instead)
printf ("Your address is: %s\n",string);
return 0;
}
----------------------------------------------------我是分割线------------------------------------------------------------
putc:
将字符写入流(字符被写在流的内部位置指示器指示的位置,然后自动前进一个)
int putc ( int character, FILE * stream );
参数:
character:要写入的字符,写入时,该值在内部转换为无符号字符。
stream:指向标识输出流的 FILE 对象的指针。
返回值:
成功:返回所写的字符。
错误:返回 EOF 并设置错误指示符 (ferror)。
/* putc example: alphabet writer */
#include <stdio.h>
int main ()
{
FILE * pFile;
char c;
pFile=fopen("alphabet.txt","wt");
for (c = 'A' ; c <= 'Z' ; c++) {
putc (c , pFile);
}
fclose (pFile);
return 0;
}
示例程序创建一个名为 alphabet.txt 的文件,并将 ABCDEFGHIJKLMNOPQRSTUVWXYZ 写入其中。
----------------------------------------------------我是分割线------------------------------------------------------------
putchar:
将字符写入 stdout(等价于使用 stdout 作为第二个参数调用 putc)
int putchar ( int character );
参数:
character:要编写的字符的 int 提升,写入时,该值在内部转换为无符号字符。
返回值:
成功:返回所写的字符。
错误:返回 EOF 并设置错误指示符 (ferror)。
/* putchar example: printing the alphabet */
#include <stdio.h>
int main ()
{
char c;
for (c = 'A' ; c <= 'Z' ; c++) putchar (c);
return 0;
}
此程序将 ABCDEFGHIJKLMNOPQRSTUVWXYZ 写入标准输出。
----------------------------------------------------我是分割线------------------------------------------------------------
puts:
将字符串写入 stdout
int puts ( const char * str );
规则:
将 str 指向的 C 字符串写入标准输出 (stdout) 并附加换行符 ('\n')。
该函数从指定的地址 (str) 开始复制,直到到达终止 null 字符 ('\0')。此终止 null 字符不会复制到流中。
请注意,put 不仅与 fputs 的不同之处在于它使用 stdout 作为目标,而且还会自动在末尾附加一个换行符(fputs 不会)。
参数:
str:要打印的 C 字符串。
返回值:
成功:返回非负值。
错误:返回 EOF 并设置错误指示器 (ferror)。
/* puts example : hello world! */
#include <stdio.h>
int main ()
{
char string [] = "Hello world!";
puts (string);
}
----------------------------------------------------我是分割线------------------------------------------------------------
ungetc:
从流中取消获取字符
int ungetc(int c, FILE *stream);
功能:
ungetc
函数的作用是将指定的字符c
推送回输入流stream
中。推送回的字符会成为下一次读取的第一个字符,即使它已经被读取过。
参数:
c
是要推送回输入流的字符,通常是一个unsigned char
类型的值。stream
是指向FILE
结构的指针,表示要进行操作的文件流。
返回值:
成功:返回推送回的字符失败:返回EOF
注意事项:
- 一般来说,
ungetc
函数只能将一个字符推送回输入流。如果需要推送回多个字符,可以多次调用ungetc
函数。ungetc
函数只能在读取之前调用,即在调用任何输入函数(如fgetc
、fgets
等)之前调用。ungetc
函数通常用于实现简单的词法分析器,以便在读取字符后发现它不属于当前词法单元时将字符推送回输入流
/* ungetc example */
#include <stdio.h>
int main ()
{
FILE * pFile;
int c;
char buffer [256];
pFile = fopen ("myfile.txt","rt");
if (pFile==NULL) perror ("Error opening file");
else while (!feof (pFile)) {
c=getc (pFile);
if (c == EOF) break;
if (c == '#') ungetc ('@',pFile);
else ungetc (c,pFile);
if (fgets (buffer,255,pFile) != NULL)
fputs (buffer,stdout);
else break;
}
return 0;
}
打开一个名为 myfile.txt 的现有文件进行读取并打印其行,但首先获取每行的第一个字符,并将其放回流中,将任何起始 # 替换为 @。
----------------------------------------------------我是分割线------------------------------------------------------------
5、Direct input/output(直接输入/输出)
fread:
从流中读取 count 元素的数组,每个元素的大小为 bytes,并将它们存储在 ptr 指定的内存块中。
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
参数:
ptr:指向大小至少为 (size*count) 字节的内存块的指针,转换为 void*。
size:要读取的每个元素的大小(以字节为单位)。
count:元素数,每个元素的大小为字节。
stream:指向指定输入流的 FILE 对象的指针。
返回值:
成功:返回成功读取的元素总数。
如果 size 或 count 为零,则该函数返回零,并且 ptr 指向的流状态和内容保持不变。
/* fread example: read an entire file */
#include <stdio.h>
#include <stdlib.h>
int main () {
FILE * pFile;
long lSize;
char * buffer;
size_t result;
pFile = fopen ( "myfile.bin" , "rb" );
if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = (char*) malloc (sizeof(char)*lSize);
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
/* the whole file is now loaded in the memory buffer. */
// terminate
fclose (pFile);
free (buffer);
return 0;
}
将 myfile.bin 加载到动态分配的内存缓冲区中,该缓冲区可用于将文件的内容作为数组进行操作。
----------------------------------------------------我是分割线------------------------------------------------------------
fwrite:
从 ptr 指向的内存块写入一个 count 元素数组,每个元素的大小为 Bytes 字节。
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
参数:
ptr:指向要写入的元素数组的指针。
size:要写入的每个元素的大小(以字节为单位)。
count:元素数,每个元素的大小为字节。
stream:指向指定输出流的 FILE 对象的指针。
返回值:
成功:返回成功写入的元素总数。
如果此数字与 count 参数不同,则表示写入错误阻止了函数完成。在这种情况下,将为流设置错误指示器 (ferror)。
如果 size 或 count 为零,则函数返回零,错误指示器保持不变。
/* fwrite example : write buffer */
#include <stdio.h>
int main ()
{
FILE * pFile;
char buffer[] = { 'x' , 'y' , 'z' };
pFile = fopen ("myfile.bin", "wb");
fwrite (buffer , sizeof(char), sizeof(buffer), pFile);
fclose (pFile);
return 0;
}
创建一个名为 myfile.bin 的文件,并将缓冲区的内容存储到其中。char类型元素是1字节,所以直接用sizeof计算得到的就是数组的大小。
6、Error-handling(错误处理)
clearerr:
清除错误指示器。
void clearerr ( FILE * stream );
参数:
stream:指向标识流的 FILE 对象的指针。
#include <stdio.h>
int main() {
FILE *pFile;
pFile = fopen("myfile.txt", "r"); // 尝试以只读方式打开文件
if (pFile == NULL) {
perror("Error opening file"); // 如果打开文件失败,输出错误信息
} else {
fputc('x', pFile); // 向文件中写入字符 'x'
if (ferror(pFile)) {
printf("Error Writing to myfile.txt\n"); // 如果写入文件时发生错误,输出错误信息
clearerr(pFile); // 清除文件错误标志
}
fgetc(pFile); // 从文件中读取一个字符
if (!ferror(pFile)) {
printf("No errors reading myfile.txt\n"); // 如果读取文件时没有发生错误,输出成功信息
}
fclose(pFile); // 关闭文件
}
return 0;
}
程序打开一个名为 myfile.txt 的现有文件进行读取,并导致尝试在其上写入时出现 I/O 错误。使用 clearerr 清除该错误,因此第二次错误检查返回 false。这段代码主要演示了文件操作中的错误处理方式,包括文件打开失败的处理、写入错误的处理、读取错误的处理以及关闭文件。
输出结果:
Error Writing to myfile.txt
No errors reading myfile.txt
代码解析:
FILE *pFile;
声明了一个指向文件的指针pFile
。pFile = fopen("myfile.txt", "r");
尝试以只读方式打开名为 "myfile.txt" 的文件。如果打开失败,fopen
函数将返回NULL
,表示文件打开失败。if (pFile == NULL) { perror("Error opening file"); }
如果文件打开失败(即pFile
为NULL
),则输出错误信息,使用perror
函数输出系统错误信息。- 如果文件成功打开,程序会尝试向文件中写入字符
'x'
,然后检查是否有写入错误发生。如果有错误发生,输出错误信息并清除文件错误标志。- 然后程序尝试从文件中读取一个字符,并检查是否有读取错误发生。如果没有错误发生,输出成功信息。
- 最后,无论是否发生错误,都会关闭文件。
----------------------------------------------------我是分割线------------------------------------------------------------
feof:
检查是否设置了与流关联的文件结束指示符,如果设置了,则返回与零不同的值。
int feof ( FILE * stream );
参数:
stream:指向标识流的 FILE 对象的指针。
返回值:
- 如果
feof
返回 0,表示文件流的结束标志尚未设置,即文件流未到达文件末尾。- 如果
feof
返回非零值,表示文件流的结束标志已经设置,即文件流已经到达了文件末尾。
/* feof example: byte counter */
#include <stdio.h>
int main ()
{
FILE * pFile;
int n = 0;
pFile = fopen ("myfile.txt","rb");
if (pFile==NULL) perror ("Error opening file");
else
{
while (fgetc(pFile) != EOF) {
++n;
}
if (feof(pFile)) {
puts ("End-of-File reached.");
printf ("Total number of bytes read: %d\n", n);
}
else puts ("End-of-File was not reached.");
fclose (pFile);
}
return 0;
}
该程序中打开名为 myfile.txt 的文件,并通过逐个读取所有字符来计算它包含的字符数。程序检查是否已到达文件末尾,如果是,则打印读取的总字节数。
----------------------------------------------------我是分割线------------------------------------------------------------
ferror:
检查是否设置了与流关联的错误指示器,如果设置了,则返回与零不同的值。通常由对失败的流的先前操作设置,并通过调用 clearerr、rewind 或 freopen 来清除。
ferror
函数用于检查文件流的错误标志。在 C 语言中,文件流通常是通过 FILE
结构指针进行操作的。ferror
函数的返回值表示了文件流的错误标志
参数:
stream:指向标识流的 FILE 对象的指针。
返回值:
- 如果
ferror
返回 0,表示文件流的错误标志尚未设置,即文件操作未发生错误。- 如果
ferror
返回非零值,表示文件流的错误标志已经设置,即文件操作发生了错误。
/* ferror example: writing error */
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile=fopen("myfile.txt","r");
if (pFile==NULL) perror ("Error opening file");
else {
fputc ('x',pFile);
if (ferror (pFile))
printf ("Error Writing to myfile.txt\n");
fclose (pFile);
}
return 0;
}
尝试打开一个名为 "myfile.txt" 的文件,并向文件中写入字符 'x'。然后使用 ferror
函数来检查文件流的错误标志,以确定文件操作是否发生了错误。如果发生了错误,则输出相应的错误信息。最后关闭文件并返回 0。
(这段代码的作用是尝试向只读模式打开的文件中写入数据,这是一个错误的操作。因为文件以只读模式打开,所以写入操作会失败,ferror
函数会检测到错误,并输出相应的错误信息。)
下面是对该代码的详细讲解:
FILE * pFile;
- 声明了一个指向FILE
结构的指针pFile
。pFile=fopen("myfile.txt","r");
- 尝试以只读模式打开名为 "myfile.txt" 的文件。如果打开失败,使用perror
函数输出错误信息。fputc ('x',pFile);
- 向文件中写入字符 'x'。if (ferror (pFile)) printf ("Error Writing to myfile.txt\n");
- 使用ferror
函数检查文件流的错误标志,如果发生了错误,则输出相应的错误信息。fclose (pFile);
- 关闭文件。- 返回 0。
----------------------------------------------------我是分割线------------------------------------------------------------
perror:
打印错误消息,将 errno 的值解释为错误消息,并将其打印到 stderr(标准错误输出流,通常是控制台),如果参数 str 不是空指针,则打印 str,后跟冒号 (:) 和空格。然后,无论 str 是否为 null 指针,都会打印生成的错误描述,后跟换行符 ('\n')。
perror 应在产生错误后立即调用,否则可能会被调用其他函数覆盖。
void perror ( const char * str );
参数:
C 字符串包含要在错误消息本身之前打印的自定义消息。
如果是 null 指针,则不会打印前面的自定义消息,但仍会打印错误消息。
按照惯例,应用程序本身的名称通常用作参数。
/* perror example */
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile=fopen ("unexist.ent","rb");
if (pFile==NULL)
perror ("The following error occurred");
else
fclose (pFile);
return 0;
}
如果文件 unexist.ent 不存在,则程序输出可能会有类似的东西:
The following error occurred: No such file or directory
7、Types(类型)
FILE:
FILE
类型是 C 语言中用于表示文件流的结构类型。它通常用于打开、读取、写入和关闭文件。FILE
类型是通过 <stdio.h>
头文件定义的,它提供了一组函数来操作文件流。
以下是一个简单的例子,演示了如何使用 FILE
类型来打开、读取和关闭文件:
#include <stdio.h>
int main() {
FILE *pFile;
char buffer[100];
pFile = fopen("example.txt", "r"); // 以只读模式打开文件
if (pFile == NULL) {
perror("Error opening file");
} else {
if (fgets(buffer, 100, pFile) != NULL) { // 从文件中读取内容到 buffer
printf("Read from file: %s", buffer);
} else {
perror("Error reading from file");
}
fclose(pFile); // 关闭文件
}
return 0;
}
我们首先声明了一个 FILE
类型的指针 pFile
,用于表示文件流。然后使用 fopen
函数以只读模式打开名为 "example.txt" 的文件,如果打开失败则输出错误信息。接着使用 fgets
函数从文件中读取内容到 buffer
,并输出到控制台。最后使用 fclose
函数关闭文件。
作用小结:
FILE
类型提供了一种方便的方式来处理文件流,它允许我们进行文件的打开、读取、写入和关闭等操作。
----------------------------------------------------我是分割线------------------------------------------------------------
fpos_t:
fpos_t
是 C 语言标准库中用来表示文件位置的类型。它通常用于存储文件流的位置,并且可以在文件的定位和操作中使用。
fpos_t
类型的具体实现可能因平台而异,但通常是一个结构或者整数类型。
#include <stdio.h>
int main() {
FILE *pFile;
fpos_t position;
pFile = fopen("example.txt", "r"); // 以只读模式打开文件
if (pFile == NULL) {
perror("Error opening file");
} else {
fgetpos(pFile, &position); // 获取当前文件位置
printf("Current position: %lld\n", (long long)position);
fseek(pFile, 10, SEEK_SET); // 设置文件位置到偏移量为 10 的位置
fgetpos(pFile, &position); // 获取新的文件位置
printf("New position: %lld\n", (long long)position);
fclose(pFile); // 关闭文件
}
return 0;
}
在这个例子中,我们首先声明了一个 fpos_t
类型的变量 position
,用于存储文件位置。然后使用 fopen
函数以只读模式打开名为 "example.txt" 的文件,如果打开失败则输出错误信息。接着使用 fgetpos
函数获取当前文件位置,并输出到控制台。然后使用 fseek
函数将文件位置设置到偏移量为 10 的位置,并再次使用 fgetpos
函数获取新的文件位置,并输出到控制台。最后使用 fclose
函数关闭文件。
----------------------------------------------------我是分割线------------------------------------------------------------
size_t:
无符号整形的别名,相当于unsigned int 。
二、小结
在本篇博客中,我们深入探讨了 <stdio.h>
头文件在 C 语言中的重要性以及它所提供的丰富功能。我们首先介绍了该头文件中定义的一些常见函数,例如 fopen
、fclose
、fread
、fwrite
等,这些函数为我们提供了对文件的打开、关闭、读取和写入等操作。接着我们讨论了 FILE
类型和 fpos_t
类型,它们分别用于表示文件流和文件位置,为我们在文件操作中提供了更多的灵活性和控制。
通过本篇博客的学习,我们不仅对 <stdio.h>
头文件有了更深入的了解,还学会了如何使用它提供的函数和类型来进行文件的操作。文件操作在实际的编程工作中是非常常见的,因此对于 <stdio.h>
头文件的掌握将有助于我们更好地处理文件相关的任务。
希望本篇博客能够帮助读者更好地理解 <stdio.h>
头文件,并在日后的编程工作中能够更加熟练地运用其中提供的函数和类型。关于stdio.h中关于C语言中的宏定义将会在之后的文章进行讲解。如果你有任何关于 C 语言文件操作或者其他编程相关的问题,都可以随时留言交流。感谢阅读!