常见硬件设备所对应的文件
文件 | 硬件设备 |
---|---|
stdin | 标准输入文件,一般指键盘;scanf()、getchar() 等函数默认从 stdin 获取输入。 |
stdout | 标准输出文件,一般指显示器;printf()、putchar() 等函数默认向 stdout 输出数据。 |
stderr | 标准错误文件,一般指显示器;perror() 等函数默认向 stderr 输出数据(后续会讲到)。 |
stdprn | 标准打印文件,一般指打印机。 |
- 文件是数据源的一种,除了文件,还有数据库、网络、键盘等;数据传递到内存也就是保存到C语言的变量(例如整数、字符串、数组、缓冲区等)。我们把数据在数据源和程序(内存)之间传递的过程叫做数据流(Data Stream)。相应的,数据从数据源到程序(内存)的过程叫做输入流(Input Stream),从程序(内存)到数据源的过程叫做输出流(Output Stream)。
- 输入输出(Input output,IO)是指程序(内存)与外部设备(键盘、显示器、磁盘、其他计算机等)进行交互的操作。几乎所有的程序都有输入与输出操作,如从键盘上读取数据,从本地或网络上的文件读取数据或写入数据等。通过输入和输出操作可以从外界接收信息,或者是把信息传递给外界。
- 我们可以说,打开文件就是打开了一个流。
#include <stdio.h>
#include <stdlib.h>
#define N 100
int main() {
FILE *fp;
char str[N + 1];
//判断文件是否打开失败
if ( (fp = fopen("d:\\demo.txt", "rt")) == NULL ) {
puts("Fail to open file!");
exit(0);
}
//循环读取文件的每一行数据
while( fgets(str, N, fp) != NULL ) {
printf("%s", str);
}
//操作结束后关闭文件
fclose(fp);
return 0;
}
文本文件和二进制文件的区别
根据我们以往的经验,文本文件通常用来保存肉眼可见的字符,比如.txt文件、.c文件、.dat文件等,用文本编辑器打开这些文件,我们能够顺利看懂文件的内容。
二进制文件通常用来保存视频、图片、程序等不可阅读的内容,用文本编辑器打开这些文件,会看到一堆乱码,根本看不懂。
但是从物理上讲,二进制文件和字符文件并没有什么区别,它们都是以二进制的形式保存在磁盘上的数据。
我们之所以能看懂文本文件的内容,是因为文本文件中采用的是 ASCII、UTF-8、GBK 等字符编码,文本编辑器可以识别出这些编码格式,并将编码值转换成字符展示出来。
而二进制文件使用的是 mp4、gif、exe 等特殊编码格式,文本编辑器并不认识这些编码格式,只能按照字符编码格式胡乱解析,所以就成了一堆乱七八糟的字符,有的甚至都没见过。
fopen() 中的文本方式和二进制方式
在C语言中,二进制方式很简单,读取文件时,会原封不动的读出文件的全部內容,写入数据时,也是把缓冲区中的內容原封不动的写到文件中。文本方式和二进制方式并没有本质上的区别,只是对于换行符的处理不同。
总起来说,对于 Windows 平台,为了保险起见,我们最好用"t"
来打开文本文件,用"b"
来打开二进制文件。对于 Linux 平台,使用"r"
还是"b"
都无所谓,既然默认是"r"
,那我们什么都不写就行了。
字符形式
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//如果文件不存在,给出提示并退出
if( (fp=fopen("D:\\demo.txt","rt")) == NULL ){
puts("Fail to open file!");
exit(0);
}
//每次读取一个字节,直到读取完毕
while( (ch=fgetc(fp)) != EOF ){
putchar(ch);
}
putchar('\n'); //输出换行符
if(ferror(fp)){
puts("读取出错");
}else{
puts("读取成功");
}
fclose(fp);
return 0;
}
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//判断文件是否成功打开
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
printf("Input a string:\n");
//每次从键盘读取一个字符并写入文件
while ( (ch=getchar()) != '\n' ){
fputc(ch,fp);
}
fclose(fp);
return 0;
}
字符串形式
#include <stdio.h>
#include <stdlib.h>
#define N 100
int main(){
FILE *fp;
char str[N+1];
if( (fp=fopen("d:\\demo.txt","rt")) == NULL ){
puts("Fail to open file!");
exit(0);
}
while(fgets(str, N, fp) != NULL){
printf("%s", str);
}
fclose(fp);
return 0;
}
将下面的内容复制到 D:\\demo.txt:
C语言中文网
http://c.biancheng.net
一个学习编程的好网站!
#include<stdio.h>
int main(){
FILE *fp;
//一百个元素,每个元素的值都是空字符(是\0,不是0)
char str[102] = {0}, strTemp[100];
if( (fp=fopen("D:\\demo.txt", "at+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
printf("Input a string:");
gets(strTemp);
//末尾转下一行
strcat(str, "\n");
strcat(str, strTemp);
fputs(str, fp);
fclose(fp);
return 0;
}
运行程序,输入C C++ Java Linux Shell,打开 D:\\demo.txt,文件内容为:
C语言中文网
http://c.biancheng.net
一个学习编程的好网站!
C C++ Java Linux Shell
数据块形式
#include<stdio.h>
#define N 5
int main(){
//从键盘输入的数据放入a,从文件读取的数据放入b
int a[N], b[N];
int i, size = sizeof(int);
FILE *fp;
if( (fp=fopen("D:\\demo.txt", "rb+")) == NULL ){ //以二进制方式打开
puts("Fail to open file!");
exit(0);
}
//从键盘输入数据 并保存到数组a
for(i=0; i<N; i++){
scanf("%d", &a[i]);
}
//将数组a的内容写入到文件
fwrite(a, size, N, fp);
//将文件中的位置指针重新定位到文件开头
rewind(fp);
//从文件读取内容并保存到数组b
fread(b, size, N, fp);
//在屏幕上显示数组b的内容
for(i=0; i<N; i++){
printf("%d ", b[i]);
}
printf("\n");
fclose(fp);
return 0;
}
运行结果:
23 409 500 100 222↙
23 409 500 100 222
#include<stdio.h>
#define N 2
struct stu{
char name[10]; //姓名
int num; //学号
int age; //年龄
float score; //成绩
}boya[N], boyb[N], *pa, *pb;
int main(){
FILE *fp;
int i;
pa = boya;
pb = boyb;
if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
//从键盘输入数据
printf("Input data:\n");
for(i=0; i<N; i++,pa++){
scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);
}
//将数组 boya 的数据写入文件
fwrite(boya, sizeof(struct stu), N, fp);
//将文件指针重置到文件开头
rewind(fp);
//从文件读取数据并保存到数据 boyb
fread(boyb, sizeof(struct stu), N, fp);
//输出数组 boyb 中的数据
for(i=0; i<N; i++,pb++){
printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
}
fclose(fp);
return 0;
}
运行结果:
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom 2 15 90.500000
Hua 1 14 99.000000
C语言fscanf和fprintf函数的用法
#include<stdio.h>
#define N 2
struct stu{
char name[10];
int num;
int age;
float score;
} boya[N], boyb[N], *pa, *pb;
int main(){
FILE *fp;
int i;
pa=boya;
pb=boyb;
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
//从键盘读入数据,保存到boya
printf("Input data:\n");
for(i=0; i<N; i++,pa++){
scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);
}
pa = boya;
//将boya中的数据写入到文件
for(i=0; i<N; i++,pa++){
fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);
}
//重置文件指针
rewind(fp);
//从文件中读取数据,保存到boyb
for(i=0; i<N; i++,pb++){
fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);
}
pb=boyb;
//将boyb中的数据输出到显示器
for(i=0; i<N; i++,pb++){
printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
}
fclose(fp);
return 0;
}
运行结果:
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom 2 15 90.500000
Hua 1 14 99.000000
- 如果将 fp 设置为 stdin,那么 fscanf() 函数将会从键盘读取数据,与 scanf 的作用相同;设置为 stdout,那么 fprintf() 函数将会向显示器输出内容,与 printf 的作用相同。
#include<stdio.h>
int main(){
int a, b, sum;
fprintf(stdout, "Input two numbers: ");
fscanf(stdin, "%d %d", &a, &b);
sum = a + b;
fprintf(stdout, "sum=%d\n", sum);
return 0;
}
运行结果:
Input two numbers: 10 20↙
sum=30
C语言rewind和fseek函数
#include<stdio.h>
#define N 3
struct stu{
char name[10]; //姓名
int num; //学号
int age; //年龄
float score; //成绩
}boys[N], boy, *pboys;
int main(){
FILE *fp;
int i;
pboys = boys;
if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
printf("Input data:\n");
for(i=0; i<N; i++,pboys++){
scanf("%s %d %d %f", pboys->name, &pboys->num, &pboys->age, &pboys->score);
}
fwrite(boys, sizeof(struct stu), N, fp); //写入三条学生信息
fseek(fp, sizeof(struct stu), SEEK_SET); //移动位置指针
fread(&boy, sizeof(struct stu), 1, fp); //读取一条学生信息
printf("%s %d %d %f\n", boy.name, boy.num, boy.age, boy.score);
fclose(fp);
return 0;
}
运行结果:
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Zhao 10 16 95.5↙
Hua 1 14 99.000000
C语言实现文件复制功能
#include <stdio.h>
#include <stdlib.h>
int copyFile(char *fileRead, char *fileWrite);
int main(){
char fileRead[100]; // 要复制的文件名
char fileWrite[100]; // 复制后的文件名
// 获取用户输入
printf("要复制的文件:");
scanf("%s", fileRead);
printf("将文件复制到:");
scanf("%s", fileWrite);
// 进行复制操作
if( copyFile(fileRead, fileWrite) ){
printf("恭喜你,文件复制成功!\n");
}else{
printf("文件复制失败!\n");
}
return 0;
}
/**
* 文件复制函数
* @param fileRead 要复制的文件
* @param fileWrite 复制后文件的保存路径
* @return int 1: 复制成功;2: 复制失败
**/
int copyFile(char *fileRead, char *fileWrite){
FILE *fpRead; // 指向要复制的文件
FILE *fpWrite; // 指向复制后的文件
int bufferLen = 1024*4; // 缓冲区长度
char *buffer = (char*)malloc(bufferLen); // 开辟缓存
int readCount; // 实际读取的字节数
if( (fpRead=fopen(fileRead, "rb")) == NULL || (fpWrite=fopen(fileWrite, "wb")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
// 不断从fileRead读取内容,放在缓冲区,再将缓冲区的内容写入fileWrite
while( (readCount=fread(buffer, 1, bufferLen, fpRead)) > 0 ){
fwrite(buffer, readCount, 1, fpWrite);
}
free(buffer);
fclose(fpRead);
fclose(fpWrite);
return 1;
}
运行结果:
要复制的文件:d://1.mp4
将文件复制到:d://2.mp4
恭喜你,文件复制成功!
C语言获取文件大小(长度)
long fsize(FILE *fp){
long n;
fpos_t fpos; //当前位置
fgetpos(fp, &fpos); //获取当前位置
fseek(fp, 0, SEEK_END);
n = ftell(fp);
fsetpos(fp,&fpos); //恢复之前的位置
return n;
}
在windows平台下
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
long fsize(FILE *fp);
int main(){
long size = 0;
FILE *fp = NULL;
char filename[30] = "D:\\1.mp4";
if( (fp = fopen(filename, "rb")) == NULL ){ //以二进制方式打开文件
printf("Failed to open %s...", filename);
getch();
exit(EXIT_SUCCESS);
}
printf("%ld\n", fsize(fp));
return 0;
}
long fsize(FILE *fp){
long n;
fpos_t fpos; //当前位置
fgetpos(fp, &fpos); //获取当前位置
fseek(fp, 0, SEEK_END);
n = ftell(fp);
fsetpos(fp,&fpos); //恢复之前的位置
return n;
}
C语言插入、删除、更改文件内容
文件复制函数
/**
* 文件复制函数
* @param fSource 要复制的原文件
* @param offsetSource 原文件的位置偏移(相对文件开头),也就是从哪里开始复制
* @param len 要复制的内容长度,小于0表示复制offsetSource后边的所有内容
* @param fTarget 目标文件,也就是将文件复制到哪里
* @param offsetTarget 目标文件的位置偏移,也就是复制到目标文件的什么位置
* @return 成功复制的字节数
**/
long fcopy(FILE *fSource, long offsetSource, long len, FILE *fTarget, long offsetTarget){
int bufferLen = 1024*4; // 缓冲区长度
char *buffer = (char*)malloc(bufferLen); // 开辟缓存
int readCount; // 每次调用fread()读取的字节数
long nBytes = 0; //总共复制了多少个字节
int n = 0; //需要调用多少次fread()函数
int i; //循环控制变量
fseek(fSource, offsetSource, SEEK_SET);
fseek(fTarget, offsetTarget, SEEK_SET);
if(len<0){ //复制所有内容
while( (readCount=fread(buffer, 1, bufferLen, fSource)) > 0 ){
nBytes += readCount;
fwrite(buffer, readCount, 1, fTarget);
}
}else{ //复制len个字节的内容
n = (int)ceil((double)((double)len/bufferLen));
for(i=1; i<=n; i++){
if(len-nBytes < bufferLen){ bufferLen = len-nBytes; }
readCount = fread(buffer, 1, bufferLen, fSource);
fwrite(buffer, readCount, 1, fTarget);
nBytes += readCount;
}
}
fflush(fTarget);
free(buffer);
return nBytes;
}
fcopy(fSource, 0, -1, fTarget, 0);
文件内容插入函数
/**
* 向文件中插入内容
* @param fp 要插入内容的文件
* @param buffer 缓冲区,也就是要插入的内容
* @param offset 偏移量(相对文件开头),也就是从哪里开始插入
* @param len 要插入的内容长度
* @return 成功插入的字节数
**/
int finsert(FILE *fp, long offset, void *buffer, int len){
long fileSize = fsize(fp);
FILE *fpTemp; //临时文件
if(offset>fileSize || offset<0 || len<0){ //插入错误
return -1;
}
if(offset == fileSize){ //在文件末尾插入
fseek(fp, offset, SEEK_SET);
if(!fwrite(buffer, len, 1, fp)){
return -1;
}
}
if(offset < fileSize){ //从开头或者中间位置插入
fpTemp = tmpfile();
fcopy(fp, 0, offset, fpTemp, 0);
fwrite(buffer, len, 1, fpTemp);
fcopy(fp, offset, -1, fpTemp, offset+len);
freopen(FILENAME, "wb+", fp );
fcopy(fpTemp, 0, -1, fp, 0);
fclose(fpTemp);
}
return 0;
}
文件内容删除函数
int fdelete(FILE *fp, long offset, int len){
long fileSize = getFileSize(fp);
FILE *fpTemp;
if(offset>fileSize || offset<0 || len<0){ //错误
return -1;
}
fpTemp = tmpfile();
fcopy(fp, 0, offset, fpTemp, 0); //将前offset字节的数据复制到临时文件
fcopy(fp, offset+len, -1, fpTemp, offset); //将offset+len之后的所有内容都复制到临时文件
freopen(FILENAME, "wb+", fp ); //重新打开文件
fcopy(fpTemp, 0, -1, fp, 0);
fclose(fpTemp);
return 0;
}