添加头文件
#include <stdio.h>;
一、printf()
作用:以输出各种类型的数据
格式控制符 | 说明 |
---|---|
%c | 输出一个单一的字符 |
%hd、%d、%ld | 以十进制、有符号的形式输出 short、int、long 类型的整数 |
%hu、%u、%lu | 以十进制、无符号的形式输出 short、int、long 类型的整数 |
%ho、%o、%lo | 以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数 |
%#ho、%#o、%#lo | 以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数 |
%hx、%x、%lx %hX、%X、%lX | 以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。 |
%#hx、%#x、%#lx %#hX、%#X、%#lX | 以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。 |
%f、%lf | 以十进制的形式输出 float、double 类型的小数 |
%e、%le %E、%lE | 以指数的形式输出 float、double 类型的小数。如果 e 小写,那么输出结果中的 e 也小写;如果 E 大写,那么输出结果中的 E 也大写。 |
%g、%lg %G、%lG | 以十进制和指数中较短的形式输出 float、double 类型的小数,并且小数部分的最后不会添加多余的 0。如果 g 小写,那么当以指数形式输出时 e 也小写;如果 G 大写,那么当以指数形式输出时 E 也大写。 |
%s | 输出一个字符串 |
高级用法:%[flag][width][.precision]type
1) type 表示输出类型
%d、%f、%c、%lf,type 就分别对应 d、f、c、lf
2)width 表示最小输出宽度
如,%9d中 width 对应 9,表示输出结果最少占用 9 个字符的宽度
当输出结果的宽度不足 width 时,以空格补齐(如果没有指定对齐方式,默认会在左边补齐空格),当输出结果的宽度超过 width 时,width 不再起作用,按照数据本身的宽度来输出。
#include <stdio.h>
int main(){
int n = 234;
float f = 9.8;
char c = '@';
char *str = "http://c.biancheng.net";
printf("%10d%12f%4c%8s", n, f, c, str);
return 0;
}
//结果
234 9.800000 @http://c.biancheng.net
3) .precision 表示输出精度,也就是小数的位数
#include <stdio.h>
int main(){
int n = 123456;
double f = 882.923672;
char *str = "abcdefghi";
printf("n: %.9d %.4d\n", n, n);
printf("f: %.2lf %.4lf %.10lf\n", f, f, f);
printf("str: %.5s %.15s\n", str, str);
return 0;
}
//结果
n: 000123456 123456
f: 882.92 882.9237 882.9236720000
str: abcde abcdefghi
- 当小数部分的位数大于 precision 时,会按照四舍五入的原则丢掉多余的数字
- 当小数部分的位数小于 precision 时,会在后面补 0
- 用于整数时,.precision 表示最小输出宽度。与 width 不同的是,整数的宽度不足时会在左边补 0,而不是补空格。
- 用于字符串时,.precision 表示最大输出宽度,或者说截取字符串。当字符串的长度大于 precision 时,会截掉多余的字符;当字符串的长度小于 precision 时,.precision 就不再起作用。
4) flag 是标志字符
标志字符 | 含 义 |
---|---|
- | -表示左对齐。如果没有,就按照默认的对齐方式,默认一般为右对齐。 |
+ | 用于整数或者小数,表示输出符号(正负号)。如果没有,那么只有负数才会输出符号。 |
空格 | 用于整数或者小数,输出值为正时冠以空格,为负时冠以负号 |
# | 对于八进制(%o)和十六进制(%x / %X)整数,# 表示在输出时添加前缀;八进制的前缀是 0,十六进制的前缀是 0x / 0X。 |
二、scanf()
作用:从键盘接收用户输入。
1)接收整数 %d:
int a, b, c; 创建变量空间, 等待接收用户输入。
scanf("%d %d %d", &a, &b, &c);
2)接收字符 %c:
char a, b, c;
scanf("%c %c %c", &a, &b, &c);
3) 接收字符串 %s:
char str[10]; // 定义一个数组,用来接收用户输入的 字符串。
scanf("%s", str); // 变量名要取地址传递给 scanf, 数组名本身表示地址,不用 & 符。
【接收字符串时】:
- scanf 具有安全隐患。如果存储空间不足,数据能存储到内存中,但不被保护。【空间不足不要使用】
- scanf 函数接收字符串时, 碰到 空格 和 换行 会自动终止。不能使用 scanf 的 %s 接收带有空格的字符串。
scanf("%d %d", &a, &b); // 获取用户输入的两个整数,分别赋值给变量 a 和 b
注意:"%d %d"之间是有空格的,所以输入数据时也要有空格。对于 scanf(),输入数据的格式要和控制字符串的格式保持一致。
三、变参函数
变参函数:参数形参中 有“...”, 最后一个固参通常是格式描述串(包含格式匹配符), 函数的参数个数、类型、顺序由这个固参决定。
printf("hello"); //一个参数
printf("%s", "hello"); //两个参数
printf("ret = %d+%d\n", 10, 5); //三个参数
printf("%d = %d%c%d\n", 10+5, 10, '+', 5);//五个参数
--> 屏幕
char buf[1024]; //缓冲区
sprintf(buf, "%d = %d%c%d\n", 10+5, 10, '+', 5); //六个参数
--> buf 中
FILE * fp = fopen(); //文件指针
fprintf(fp, "%d = %d%c%d\n", 10+5, 10, '+', 5); //六个参数
--> fp 对应的文件中
四、文件指针
#include <stdio.h>
在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。
typedef struct
{
short level; //缓冲区"满"或者"空"的程度
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned char hold; //如无缓冲区不读取字符
short bsize; //缓冲区的大小
unsigned char *buffer;//数据缓冲区的位置
unsigned ar; //指针,当前的指向
unsigned istemp; //临时文件,指示器
short token; //用于有效性的检查
}FILE;
C语言中有三个特殊的文件指针由系统默认打开,用户无需定义即可直接使用:
- stdin: 标准输入,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
- stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
- stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。
文件打开
任何文件使用之前必须打开:
FILE * fopen(const char * filename, const char * mode);
功能:打开文件
参数:filename:需要打开的文件名,根据需要加上路径
mode:打开文件的模式设置
返回值:成功:文件指针
失败:NULL
filename的几种形式:
FILE *fp_passwd = NULL;
//相对路径:
//打开当前目录passdw文件:源文件(源程序)所在目录
FILE *fp_passwd = fopen("passwd.txt", "r");
//打开当前目录(test)下passwd.txt文件
fp_passwd = fopen(". / test / passwd.txt", "r");
//打开当前目录上一级目录(相对当前目录)passwd.txt文件
fp_passwd = fopen(".. / passwd.txt", "r");
//绝对路径:
//打开C盘test目录下一个叫passwd.txt文件
fp_passwd = fopen("c:/test/passwd.txt","r");
mode的几种形式:
打开模式 | 含义 |
---|---|
r或rb | 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错) |
w或wb | 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a或ab | 以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件 |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或wb+ | 以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a+或ab+ | 以添加方式打开可读、可写的文件。若文件不存在则创建文件;如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留。 |
注意:b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
int main(void)
{
FILE *fp = NULL;
// "\\"这样的路径形式,只能在windows使用
// "/"这样的路径形式,windows和linux平台下都可用,建议使用这种
// 路径可以是相对路径,也可是绝对路径
fp = fopen("../test", "w");
//fp = fopen("..\\test", "w");
if (fp == NULL) //返回空,说明打开失败
{
//perror()是标准出错打印函数,能打印调用库函数出错原因
perror("open");
return -1;
}
return 0;
}
文件关闭
任何文件在使用后应该关闭:
- 打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
- 一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败
- 如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。
int fclose(FILE * stream);
功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
参数:stream:文件指针
返回值:成功:0
失败:-1
FILE * fp = NULL;
fp = fopen("abc.txt", "r");
fclose(fp);
五、文件的读写
#include <stdio.h>
1)文件的顺序读写
1.按照字符写文件fputc
int fputc(int ch, FILE * stream);
功能:将ch转换为unsigned char后写入stream指定的文件中
参数:ch:需要写入文件的字符
stream:文件指针
返回值:成功:成功写入文件的字符
失败:返回-1
char buf[] = "this is a test for fputc";
int i = 0;
int n = strlen(buf);
for (i = 0; i < n; i++)
{
//往文件fp写入字符buf[i]
int ch = fputc(buf[i], fp);
printf("ch = %c\n", ch);
}
2.文件结尾EOF
在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。
#define EOF (-1)
当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSIC提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。
int feof(FILE * stream);
功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)。
参数:stream:文件指针
返回值:非0值:已经到文件结尾
0:没有到文件结尾
3.按照字符读文件fgetc
int fgetc(FILE * stream);
功能:从stream指定的文件中读取一个字符
参数:stream:文件指针
返回值:成功:返回读取到的字符
失败:-1
#include <stdio.h>
int main()
{
FILE* fp=NULL;
fp = fopen("text.txt", "r+" );
if (fp == NULL)
{
perror("fp is NULL!");
system("pause");
return -1;
}
while (!feof(fp))
{
char ch = fgetc(fp);
printf("%c", ch);
}
printf("\n");
char buf[] = "this is a new chars!";
int n = strlen(buf);
for (int i = 0; i < n; i++)
{
char ch = fputc(buf[i],fp);
printf("%c", ch);
}
fclose(fp);
system("pause");
return 0;
}
2)文件的按行读写
1.按照行写文件fputs
int fputs(const char * str, FILE * stream);
功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
参数:str:字符串
stream:文件指针
返回值:成功:0
失败:-1
char *buf[] = { "123456\n", "bbbbbbbbbb\n", "ccccccccccc\n" };//字符串指针数组
int i = 0;
int n = 3;
for (i = 0; i < n; i++)
{
int len = fputs(buf[i], fp);
printf("len = %d\n", len);
}
2.按照行读文件fgets
char * fgets(char * str, int size, FILE * stream);
功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
参数:str:字符串
size:指定最大读取字符串的长度(size - 1)
stream:文件指针
返回值:成功:成功读取的字符串
读到文件尾或出错: NULL
#include <stdio.h>
#include <string.h>
int main()
{
FILE* fp=NULL;
fp = fopen("text.txt", "r+" );
if (fp == NULL)
{
perror("fp is NULL!");
system("pause");
return -1;
}
char buf[100] = { 0 };
while (!feof(fp))
{
char *ch = fgets(buf, sizeof(buf), fp);
if(ch!=NULL) //这一步很重要,防止文件中最后一句话进行了换行后结束,
{ //因此要判断读出的字符是否为空。
printf("%s", buf);
}
}
printf("\n");
char *bufs[] = { "This is one!\n","This is two\n","This is three!" };
int n = strlen(bufs);
for (int i = 0; i < n; i++)
{
char ch = fputs(bufs[i],fp);
}
fclose(fp);
system("pause");
return 0;
}
3)文件的按格式化读写
1.按照格式化写文件fprintf
int fprintf(FILE * stream, const char * format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
参数:stream:已经打开的文件
format:字符串格式,用法和printf()一样
返回值:成功:实际写入文件的字符个数
失败:-1
FILE* fp = NULL;
fp=fopen("text2.txt","r+");
if (fp == NULL)
{
perror("file is null!");
return -1;
}
fprintf(fp, "%d %d %d", 50, 20, 30);
return 0;
2.按照格式化读文件fscanf
int fscanf(FILE * stream, const char * format, ...);
功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
参数:stream:已经打开的文件
format:字符串格式,用法和scanf()一样
返回值:成功:参数数目,成功转换的值的个数
失败: - 1
FILE* fp = NULL;
fp=fopen("text2.txt","r+");
if (fp == NULL)
{
perror("file is null!");
return -1;
}
int a, b, c;
fscanf(fp, "%d %d %d", &a, &b, &c);
printf("%d %d %d", a, b, c);
return 0;
4)文件的按块读写
1.文件的按块写文件
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以数据块的方式给文件写入内容
参数:ptr:准备写入文件数据的地址
size: size_t 为 unsigned int类型,此参数指定写入文件内容的块数据大小
nmemb:写入文件的块数,写入文件数据总大小为:size * nmemb
stream:已经打开的文件指针
返回值:成功:实际成功写入文件数据的块数目,此值和 nmemb 相等
失败:0
Stu s[3];
int i = 0;
for (i = 0; i < 3; i++)
{
sprintf(s[i].name, "stu%d%d%d", i, i, i);
s[i].id = i + 1;
}
int ret = fwrite(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
2.文件的按块读文件
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以数据块的方式从文件中读取内容
参数:ptr:存放读取出来数据的内存空间
size: size_t 为 unsigned int类型,此参数指定读取文件内容的块数据大小
nmemb:读取文件的块数,读取文件数据总大小为:size * nmemb
stream:已经打开的文件指针
返回值:成功:实际成功读取到内容的块数,如果此值比nmemb小,但大于0,说明读到文件的结尾。
失败:0
Stu s[3];
int ret = fread(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
int i = 0;
for (i = 0; i < 3; i++)
{
printf("s = %s, %d\n", s[i].name, s[i].id);
}
5)文件的随机读写
1.int fseek(FILE *stream, long offset, int whence);
功能:移动文件流(文件光标)的读写位置。
参数:stream:已经打开的文件指针
offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出 错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。
whence:其取值如下,SEEK_SET:从文件开头移动offset个字节 ;SEEK_CUR:从当前位置移动offset个字节;SEEK_END:从文件末尾移动offset个字节
返回值:成功:0
失败:-1
2.long ftell(FILE *stream);
功能:获取文件流(文件光标)的读写位置。
参数:stream:已经打开的文件指针
返回值:成功:当前文件流(文件光标)的读写位置
失败:-1
3.void rewind(FILE *stream);
功能:把文件流(文件光标)的读写位置移动到文件开头。
参数:stream:已经打开的文件指针
返回值:无返回值
//假如已经往文件写入3个结构体
//fwrite(s, sizeof(Stu), 3, fp);Stu s[3];
Stu tmp;
int ret = 0;
//文件光标读写位置从开头往右移动2个结构体的位置
fseek(fp, 2 * sizeof(Stu), SEEK_SET);
//读第3个结构体
ret = fread(&tmp, sizeof(Stu), 1, fp);
if (ret == 1)
{
printf("[tmp]%s, %d\n", tmp.name, tmp.id);
}
//把文件光标移动到文件开头
//fseek(fp, 0, SEEK_SET);
rewind(fp);
ret = fread(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
int i = 0;
for (i = 0; i < 3; i++)
{
printf("s === %s, %d\n", s[i].name, s[i].id);
}
6)判断文本文件是Linux格式还是Windows格式
- b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
- Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
- 在Windows平台下,以“文本”方式打开文件,不加b:
- 当读取文件的时候,系统会将所有的 "\r\n" 转换成 "\n"
- 当写入文件的时候,系统会将 "\n" 转换成 "\r\n" 写入
- 以"二进制"方式打开文件,则读\写都不会进行这样的转换
- 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输入输出
#include<stdio.h>
int main(int argc, char **args)
{
if (argc < 2)
return 0;
FILE *p = fopen(args[1], "rb");//以二进制方式只读
if (!p)
return 0;
char a[1024] = { 0 };
fgets(a, sizeof(a), p);
int len = 0;
while (a[len])
{
if (a[len] == '\n')
{
if (a[len - 1] == '\r')
{
printf("windows file\n");
}
else
{
printf("linux file\n");
}
}
len++;
}
fclose(p);
return 0;
}
7)获取文件状态
#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
功能:获取文件状态信息
参数:path:文件名
buf:保存文件信息的结构体
返回值:成功:0
失败-1
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(int argc, char **args)
{
if (argc < 2)
return 0;
struct stat st = { 0 };
stat(args[1], &st);
int size = st.st_size;//得到结构体中的成员变量
printf("%d\n", size);
return 0;
}
8)获取文件状态
#include <stdio.h>
int remove(const char *pathname);
功能:删除文件
参数:pathname:文件名
返回值:成功:0
失败:-1
int rename(const char *oldpath, const char *newpath);
功能:把oldpath的文件名改为newpath
参数:oldpath:旧文件名
newpath:新文件名
返回值:成功:0
失败: - 1
六、文件的缓冲区
ANSIC标准采用“缓冲文件系统”处理数据文件。
缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。
如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量) 。
磁盘文件的存取
- 磁盘文件,一般保存在硬盘、U盘等掉电不丢失的磁盘设备中,在需要时调入内存
- 在内存中对文件进行编辑处理后,保存到磁盘中
- 程序与磁盘之间交互,不是立即完成,系统或程序可根据需要设置缓冲区,以提高存取效率
更新缓冲区
#include <stdio.h>
int fflush(FILE *stream);
功能:更新缓冲区,让缓冲区的数据立马写到文件中。
参数:stream:文件指针
返回值:成功:0
失败:-1